Skip to content

Commit 992359a

Browse files
authored
feat(experimental): Support using --all with node.js ESM (#1320)
This allows `--all` to collect initial coverage from fils which node.js recognizes as ES modules (`.mjs` files and conditionally `.js` files). Previously this would cause node.js to produce an `ERR_REQUIRE_ESM` error. This does not enable collection of coverage during actual tests, for that you must use the experimental `@istanbuljs/esm-loader-hook`.
1 parent 086fd20 commit 992359a

File tree

9 files changed

+76
-11
lines changed

9 files changed

+76
-11
lines changed

index.js

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const rimraf = promisify(require('rimraf'))
2020
const SourceMaps = require('./lib/source-maps')
2121
const TestExclude = require('test-exclude')
2222
const pMap = require('p-map')
23+
const getPackageType = require('get-package-type')
2324

2425
const debugLog = debuglog('nyc')
2526

@@ -170,14 +171,36 @@ class NYC {
170171
return source
171172
}
172173

174+
_getSourceMap (code, filename, hash) {
175+
const sourceMap = {}
176+
if (this._sourceMap) {
177+
sourceMap.sourceMap = this.sourceMaps.extract(code, filename)
178+
sourceMap.registerMap = () => this.sourceMaps.registerMap(filename, hash, sourceMap.sourceMap)
179+
} else {
180+
sourceMap.registerMap = () => {}
181+
}
182+
183+
return sourceMap
184+
}
185+
173186
async addAllFiles () {
174187
this._loadAdditionalModules()
175188

176189
this.fakeRequire = true
177190
const files = await this.exclude.glob(this.cwd)
178-
files.forEach(relFile => {
191+
for (const relFile of files) {
179192
const filename = path.resolve(this.cwd, relFile)
180-
this.addFile(filename)
193+
const ext = path.extname(filename)
194+
if (ext === '.mjs' || (ext === '.js' && await getPackageType(filename) === 'module')) {
195+
const source = await fs.readFile(filename, 'utf8')
196+
this.instrumenter().instrumentSync(
197+
source,
198+
filename,
199+
this._getSourceMap(source, filename)
200+
)
201+
} else {
202+
this.addFile(filename)
203+
}
181204
const coverage = coverageFinder()
182205
const lastCoverage = this.instrumenter().lastFileCoverage()
183206
if (lastCoverage) {
@@ -187,7 +210,7 @@ class NYC {
187210
all: true
188211
}
189212
}
190-
})
213+
}
191214
this.fakeRequire = false
192215

193216
this.writeCoverageFile()
@@ -276,14 +299,7 @@ class NYC {
276299

277300
return (code, metadata, hash) => {
278301
const filename = metadata.filename
279-
const sourceMap = {}
280-
281-
if (this._sourceMap) {
282-
sourceMap.sourceMap = this.sourceMaps.extract(code, filename)
283-
sourceMap.registerMap = () => this.sourceMaps.registerMap(filename, hash, sourceMap.sourceMap)
284-
} else {
285-
sourceMap.registerMap = () => {}
286-
}
302+
const sourceMap = this._getSourceMap(code, filename, hash)
287303

288304
try {
289305
instrumented = instrumenter.instrumentSync(code, filename, sourceMap)

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"find-cache-dir": "^3.2.0",
6565
"find-up": "^4.1.0",
6666
"foreground-child": "^2.0.0",
67+
"get-package-type": "^0.1.0",
6768
"glob": "^7.1.6",
6869
"istanbul-lib-coverage": "^3.0.0",
6970
"istanbul-lib-hook": "^3.0.0",

tap-snapshots/test-nyc-integration.js-TAP.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
* Make sure to inspect the output below. Do not ignore changes!
66
*/
77
'use strict'
8+
exports[`test/nyc-integration.js TAP --all does not fail on ERR_REQUIRE_ESM > stdout 1`] = `
9+
------------|---------|----------|---------|---------|-------------------
10+
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
11+
------------|---------|----------|---------|---------|-------------------
12+
All files | 33.33 | 100 | 0 | 33.33 |
13+
extra.mjs | 0 | 100 | 0 | 0 | 2
14+
index.js | 0 | 100 | 0 | 0 | 2
15+
script.cjs | 100 | 100 | 100 | 100 |
16+
------------|---------|----------|---------|---------|-------------------
17+
18+
`
19+
820
exports[`test/nyc-integration.js TAP --all includes files with both .map files and inline source-maps > stdout 1`] = `
921
----------|---------|----------|---------|---------|-------------------
1022
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function () {
2+
return 'es module';
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function () {
2+
return 'es module';
3+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "all-type-module",
3+
"version": "0.1.0",
4+
"description": "",
5+
"main": "index.js",
6+
"type": "module",
7+
"nyc": {
8+
"all": true
9+
}
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env node
2+
'use strict';
3+
4+
// Not doing anything, we just want something to run with `nyc --all`
5+
process.exit(0);

test/nyc-integration.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const nycConfigJS = path.resolve(fixturesCLI, 'nyc-config-js')
1515
const nycrcDir = path.resolve(fixturesCLI, 'nycrc')
1616
const fixturesSourceMaps = path.resolve(fixturesCLI, '../source-maps')
1717
const fixturesENM = path.resolve(fixturesCLI, '../exclude-node-modules')
18+
const fixturesAllTypeModule = path.resolve(fixturesCLI, '../all-type-module')
1819

1920
const executeNodeModulesArgs = [
2021
'--all=true',
@@ -436,6 +437,15 @@ t.test('--all uses source-maps to exclude original sources from reports', t => t
436437
cwd: fixturesSourceMaps
437438
}))
438439

440+
t.test('--all does not fail on ERR_REQUIRE_ESM', t => testSuccess(t, {
441+
args: [
442+
'--all',
443+
process.execPath,
444+
'script.cjs'
445+
],
446+
cwd: fixturesAllTypeModule
447+
}))
448+
439449
t.test('caches source-maps from .map files', async t => {
440450
await testSuccess(t, {
441451
args: [

0 commit comments

Comments
 (0)