Skip to content

Commit cc7c652

Browse files
committed
Merge remote-tracking branch 'origin/v25'
2 parents 10d864c + 1dcc779 commit cc7c652

21 files changed

+217
-213
lines changed

.github/workflows/ci-module.yml

+3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ on:
55
branches:
66
- master
77
pull_request:
8+
workflow_dispatch:
89

910
jobs:
1011
test:
1112
uses: hapijs/.github/.github/workflows/ci-module.yml@master
13+
with:
14+
min-node-version: 14

lib/cli.js

+3-10
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ exports.run = async function () {
3636
settings.lintingPath = process.cwd();
3737

3838
if (settings.coverage) {
39-
Modules.coverage.instrument(settings);
39+
await Modules.coverage.instrument(settings);
4040
}
4141
else if (settings.transform) {
4242
Modules.transform.install(settings);
@@ -160,8 +160,7 @@ internals.traverse = async function (paths, options) {
160160
return process.exit(1);
161161
}
162162

163-
if (pkg.lab &&
164-
pkg.lab._root) {
163+
if (pkg.lab?._root) {
165164

166165
scripts.push(Object.assign(pkg.lab, {
167166
_type: type,
@@ -230,12 +229,6 @@ internals.options = function () {
230229
description: 'prevent recursive inclusion of all files in coveragePath in report',
231230
default: null
232231
},
233-
'coverage-module': {
234-
type: 'string',
235-
description: 'enable coverage on external module',
236-
multiple: true,
237-
default: null
238-
},
239232
'coverage-path': {
240233
type: 'string',
241234
description: 'set code coverage path',
@@ -498,7 +491,7 @@ internals.options = function () {
498491
options.paths = argv._ ? [].concat(argv._) : options.paths;
499492

500493
const keys = ['assert', 'bail', 'colors', 'context-timeout', 'coverage', 'coverage-exclude',
501-
'coverage-path', 'coverage-all', 'coverage-flat', 'coverage-module', 'coverage-pattern', 'coverage-predicates',
494+
'coverage-path', 'coverage-all', 'coverage-flat', 'coverage-pattern', 'coverage-predicates',
502495
'default-plan-threshold', 'dry', 'environment', 'flat', 'globals', 'grep', 'lint',
503496
'lint-errors-threshold', 'lint-fix', 'lint-options', 'lint-warnings-threshold', 'linter',
504497
'output', 'pattern', 'reporter', 'require', 'retries', 'seed', 'shuffle', 'silence', 'silent-skips',

lib/index.d.ts

-5
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,6 @@ declare namespace script {
8080
*/
8181
readonly 'coverage-flat'?: boolean;
8282

83-
/**
84-
* Enables coverage on external modules.
85-
*/
86-
readonly 'coverage-module'?: string[];
87-
8883
/**
8984
* Sets code coverage path.
9085
*/

lib/index.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ for (const module of ['coverage', 'leaks', 'types']) {
2121
Object.defineProperty(exports, module, Object.getOwnPropertyDescriptor(Modules, module));
2222
}
2323

24-
2524
/*
2625
experiment('Utilities', () => {
2726
@@ -278,6 +277,6 @@ internals.only = function (script, type) {
278277
internals.mergeOptions = function (parent, child) {
279278

280279
const options = Object.assign({}, parent, child);
281-
options.only = child && child.only;
280+
options.only = child?.only;
282281
return options;
283282
};

lib/linter/index.js

+13-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const Hoek = require('@hapi/hoek');
1010
const internals = {};
1111

1212

13-
exports.lint = function () {
13+
exports.lint = async function () {
1414

1515
const configuration = {
1616
ignore: true
@@ -24,7 +24,7 @@ exports.lint = function () {
2424
!Fs.existsSync('.eslintrc.yml') &&
2525
!Fs.existsSync('.eslintrc.json') &&
2626
!Fs.existsSync('.eslintrc')) {
27-
configuration.configFile = Path.join(__dirname, '.eslintrc.js');
27+
configuration.overrideConfigFile = Path.join(__dirname, '.eslintrc.js');
2828
}
2929

3030
if (options) {
@@ -33,17 +33,14 @@ exports.lint = function () {
3333

3434
let results;
3535
try {
36-
const engine = new Eslint.CLIEngine(configuration);
37-
results = engine.executeOnFiles(['.']);
36+
const eslint = new Eslint.ESLint(configuration);
37+
results = await eslint.lintFiles(['.']);
3838
}
3939
catch (ex) {
40-
results = {
41-
results: [{ messages: [ex] }]
42-
};
40+
results = [{ messages: [ex] }];
4341
}
4442

45-
46-
return results.results.map((result) => {
43+
return results.map((result) => {
4744

4845
const transformed = {
4946
filename: result.filePath
@@ -68,4 +65,10 @@ exports.lint = function () {
6865
});
6966
};
7067

71-
process.send(exports.lint());
68+
const main = async () => {
69+
70+
const results = await exports.lint();
71+
process.send(results);
72+
};
73+
74+
main();

lib/modules/coverage.js

+23-63
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,15 @@ const Path = require('path');
99

1010
const ESLint = require('eslint');
1111
const ESLintParser = require('@babel/eslint-parser');
12-
const SourceMap = require('source-map');
1312
const SourceMapSupport = require('source-map-support');
1413

1514
const Eslintrc = require('../linter/.eslintrc');
15+
const SourceMap = require('../source-map');
1616
const Transform = require('./transform');
1717

1818
const internals = {
19-
ext: Symbol.for('@hapi/lab/coverage/initialize'),
2019
_state: Symbol.for('@hapi/lab/coverage/_state'),
21-
EslintEngine: new ESLint.CLIEngine({ baseConfig: Eslintrc })
20+
eslint: new ESLint.ESLint({ baseConfig: Eslintrc })
2221
};
2322

2423

@@ -31,8 +30,6 @@ global[internals._state] = global[internals._state] || {
3130

3231
predicates: {},
3332

34-
externals: new Set(),
35-
3633
files: {},
3734

3835
_line: function (name, line) {
@@ -55,17 +52,6 @@ global[internals._state] = global[internals._state] || {
5552
statement.hit[!!source] = true;
5653
}
5754

58-
return source;
59-
},
60-
61-
_external: function (name, line, source) {
62-
63-
const initialize = source[Symbol.for('@hapi/lab/coverage/initialize')];
64-
if (typeof initialize !== 'function') {
65-
throw new Error('Failed to find a compatible external coverage method in ' + name + ':' + line);
66-
}
67-
68-
internals.state.externals.add(initialize());
6955
return source;
7056
}
7157
};
@@ -78,17 +64,13 @@ if (typeof global.__$$labCov === 'undefined') {
7864
// $lab:coverage:on$
7965

8066

81-
exports.instrument = function (options) {
67+
exports.instrument = async function (options) {
8268

83-
if (options['coverage-module']) {
84-
for (const name of options['coverage-module']) {
85-
internals.state.modules.add(name);
86-
}
87-
}
69+
const ctx = await internals.context(options);
8870

8971
internals.state.patterns.unshift(internals.pattern(options));
9072
internals.state.predicates = options['coverage-predicates'] || {};
91-
Transform.install(options, internals.prime);
73+
Transform.install(options, (ext) => internals.prime(ext, ctx));
9274
};
9375

9476

@@ -124,15 +106,15 @@ internals.escape = function (string) {
124106
};
125107

126108

127-
internals.prime = function (extension) {
109+
internals.prime = function (extension, ctx) {
128110

129111
require.extensions[extension] = function (localModule, filename) {
130112

131113
// We never want to instrument eslint configs in order to avoid infinite recursion
132114
if (Path.basename(filename, extension) !== '.eslintrc') {
133115
for (let i = 0; i < internals.state.patterns.length; ++i) {
134116
if (internals.state.patterns[i].test(filename.replace(/\\/g, '/'))) {
135-
return localModule._compile(internals.instrument(filename), filename);
117+
return localModule._compile(internals.instrument(filename, ctx), filename);
136118
}
137119
}
138120
}
@@ -143,7 +125,7 @@ internals.prime = function (extension) {
143125
};
144126

145127

146-
internals.instrument = function (filename) {
128+
internals.instrument = function (filename, ctx) {
147129

148130
filename = filename.replace(/\\/g, '/');
149131

@@ -297,8 +279,7 @@ internals.instrument = function (filename) {
297279
'LabeledStatement'
298280
];
299281

300-
if (node.parent &&
301-
node.parent.type === 'BlockStatement' &&
282+
if (node.parent?.type === 'BlockStatement' &&
302283
node.parent.parent.type.includes('FunctionExpression') &&
303284
node.parent.body[0] === node) {
304285

@@ -325,37 +306,26 @@ internals.instrument = function (filename) {
325306

326307
node.set(`(global.__$$labCov._statement(\'${filename}\',${left},${line},${node.left.source()})${node.operator}global.__$$labCov._statement(\'${filename}\',${right},${line},${node.right.source()}))`);
327308
}
328-
else if (node.parent &&
329-
node.parent.type === 'ArrowFunctionExpression' &&
309+
else if (node.parent?.type === 'ArrowFunctionExpression' &&
330310
node.type.includes('Expression')) {
331311

332312
const id = addStatement(line, node, false);
333313

334314
node.set(`global.__$$labCov._statement('${filename}', ${id}, ${line}, ${node.source()})`);
335315
}
336-
else if (node.parent &&
337-
node.parent.test === node &&
316+
else if (node.parent?.test === node &&
338317
node.parent.type !== 'SwitchCase') {
339318

340319
const test = addStatement(line, node, true);
341320

342321
node.set(`global.__$$labCov._statement(\'${filename}\',${test},${line},${node.source()})`);
343322
}
344-
else if (node.type === 'CallExpression' &&
345-
node.callee.name === 'require') {
346-
347-
const name = node.arguments[0].value;
348-
if (internals.state.modules.has(name)) {
349-
node.set(`global.__$$labCov._external(\'${filename}\',${line},${node.source()})`);
350-
}
351-
}
352323
};
353324

354325
// Parse tree
355326

356-
const eslintConfig = internals.EslintEngine.getConfigForFile(filename);
357327
const tree = ESLintParser.parse(content, {
358-
...eslintConfig.parserOptions,
328+
...ctx.parserOptions,
359329
loc: true,
360330
range: true,
361331
comment: true
@@ -551,13 +521,13 @@ exports.analyze = async function (options) {
551521

552522
const report = internals.state.files;
553523
const pattern = internals.pattern(options);
524+
const ctx = await internals.context(options);
554525

555526
const cov = {
556527
sloc: 0,
557528
hits: 0,
558529
misses: 0,
559530
percent: 0,
560-
externals: 0,
561531
files: []
562532
};
563533

@@ -568,7 +538,7 @@ exports.analyze = async function (options) {
568538
const filename = file.replace(/\\/g, '/');
569539
if (pattern.test(filename)) {
570540
if (!report[filename]) {
571-
internals.instrument(filename);
541+
internals.instrument(filename, ctx);
572542
}
573543

574544
report[filename].source = internals.state.sources[filename] || [];
@@ -578,7 +548,6 @@ exports.analyze = async function (options) {
578548
cov.hits += data.hits;
579549
cov.misses += data.misses;
580550
cov.sloc += data.sloc;
581-
cov.externals += data.externals ? data.externals.length : 0;
582551
}
583552
}
584553

@@ -630,8 +599,7 @@ internals.file = async function (filename, data, options) {
630599
hits: 0,
631600
misses: 0,
632601
sloc: data.sloc,
633-
source: {},
634-
externals: internals.external(filename)
602+
source: {}
635603
};
636604

637605
// Use sourcemap consumer rather than SourceMapSupport.mapSourcePosition itself which perform path transformations
@@ -691,7 +659,7 @@ internals.file = async function (filename, data, options) {
691659
};
692660

693661
const originalPosition = smc.originalPositionFor(position);
694-
source.originalFilename = originalPosition.source && originalPosition.source.replace(/\\/g, '/'); // Ensure folder separator to be url-friendly
662+
source.originalFilename = originalPosition.source?.replace(/\\/g, '/'); // Ensure folder separator to be url-friendly
695663
source.originalLine = originalPosition.line;
696664
if (!originalPosition.source) {
697665
if (isValidLine) {
@@ -791,21 +759,13 @@ internals.file = async function (filename, data, options) {
791759
return ret;
792760
};
793761

762+
internals.context = async (options) => {
794763

795-
internals.external = function (filename) {
796-
797-
filename = Path.normalize(filename);
798-
799-
const reports = [];
800-
for (const external of internals.state.externals) {
801-
const report = external.report(filename);
802-
if (report) {
803-
const items = [].concat(report);
804-
for (const item of items) {
805-
reports.push(Object.assign({}, item, { source: external.name }));
806-
}
807-
}
808-
}
764+
// The parserOptions are shared by all files for coverage purposes, based on
765+
// the effective eslint config for a hypothetical file {coveragePath}/x.js
766+
const { parserOptions } = await internals.eslint.calculateConfigForFile(
767+
Path.join(options.coveragePath || '', 'x.js')
768+
);
809769

810-
return reports.length ? reports : null;
770+
return { parserOptions };
811771
};

lib/modules/types.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const internals = {
1818
jsx: Ts.JsxEmit.React,
1919
lib: ['lib.es2020.d.ts'],
2020
module: Ts.ModuleKind.CommonJS,
21-
target: Ts.ScriptTarget.ES2019,
21+
target: Ts.ScriptTarget.ES2020,
2222
moduleResolution: Ts.ModuleResolutionKind.NodeJs
2323
},
2424

lib/modules/typescript.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ internals.transform = function (content, fileName) {
1313
if (!internals.configs.has(configFile)) {
1414
try {
1515
var { config, error } = Typescript.readConfigFile(configFile, Typescript.sys.readFile);
16-
if (error) {
17-
throw new Error(`TypeScript config error in ${configFile}: ${error.messageText}`);
18-
}
1916
}
2017
catch (err) {
2118
throw new Error(`Cannot find a tsconfig file for ${fileName}`);
2219
}
2320

21+
if (error) {
22+
throw new Error(`TypeScript config error in ${configFile}: ${error.messageText}`);
23+
}
24+
2425
const { options } = Typescript.parseJsonConfigFileContent(config, Typescript.sys, Typescript.getDirectoryPath(configFile), {}, configFile);
2526
options.sourceMap = false;
2627
options.inlineSourceMap = true;

0 commit comments

Comments
 (0)