diff --git a/lib/is-cjs-esm-bridge.js b/lib/is-cjs-esm-bridge.js new file mode 100644 index 00000000..a7dd3093 --- /dev/null +++ b/lib/is-cjs-esm-bridge.js @@ -0,0 +1,10 @@ +module.exports = ({ functions }) => { + // https://github.com/nodejs/node/blob/v12.1.0/lib/internal/modules/esm/create_dynamic_module.js#L11-L19 + return functions.length === 3 && + functions[0].functionName === '' && + functions[0].isBlockCoverage === true && + functions[1].functionName === 'get' && + functions[1].isBlockCoverage === false && + functions[2].functionName === 'set' && + functions[2].isBlockCoverage === true +} diff --git a/lib/report.js b/lib/report.js index b6992c87..ae4f1c11 100644 --- a/lib/report.js +++ b/lib/report.js @@ -8,6 +8,7 @@ const { isAbsolute, resolve } = require('path') // TODO: switch back to @c88/v8-coverage once patch is landed. const { mergeProcessCovs } = require('@bcoe/v8-coverage') const v8toIstanbul = require('v8-to-istanbul') +const isCjsEsmBridgeCov = require('./is-cjs-esm-bridge') class Report { constructor ({ @@ -55,21 +56,43 @@ class Report { if (this._allCoverageFiles) return this._allCoverageFiles const v8ProcessCov = this._getMergedProcessCov() - const map = libCoverage.createCoverageMap({}) + const resultCountPerPath = new Map() + const possibleCjsEsmBridges = new Map() for (const v8ScriptCov of v8ProcessCov.result) { try { const path = resolve(this.resolve, v8ScriptCov.url) const converter = v8toIstanbul(path, this.wrapperLength) await converter.load() - converter.applyCoverage(v8ScriptCov.functions) - map.merge(converter.toIstanbul()) + + if (resultCountPerPath.has(path)) { + resultCountPerPath.set(path, resultCountPerPath.get(path) + 1) + } else { + resultCountPerPath.set(path, 0) + } + + if (isCjsEsmBridgeCov(v8ScriptCov)) { + possibleCjsEsmBridges.set(converter, { + path, + functions: v8ScriptCov.functions + }) + } else { + converter.applyCoverage(v8ScriptCov.functions) + map.merge(converter.toIstanbul()) + } } catch (err) { console.warn(`file: ${v8ScriptCov.url} error: ${err.stack}`) } } + for (const [converter, { path, functions }] of possibleCjsEsmBridges) { + if (resultCountPerPath.get(path) <= 1) { + converter.applyCoverage(functions) + map.merge(converter.toIstanbul()) + } + } + this._allCoverageFiles = map return this._allCoverageFiles } diff --git a/test/fixtures/export.cjs b/test/fixtures/export.cjs new file mode 100644 index 00000000..ce7972f1 --- /dev/null +++ b/test/fixtures/export.cjs @@ -0,0 +1 @@ +module.exports = () => 'foo'; diff --git a/test/fixtures/import.mjs b/test/fixtures/import.mjs index 1e49e856..6c2603d5 100644 --- a/test/fixtures/import.mjs +++ b/test/fixtures/import.mjs @@ -1,3 +1,4 @@ -import foo from './export.mjs' +import esm from './export.mjs' +import cjs from './export.cjs' -console.info(foo()) +console.log(esm(), cjs()) diff --git a/test/integration.js.snap b/test/integration.js.snap index fecf3eb1..95b0e99b 100644 --- a/test/integration.js.snap +++ b/test/integration.js.snap @@ -1,11 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`c8 ESM Modules collects coverage for ESM modules 1`] = ` -",bar +",bar foo ------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | ------------|----------|----------|----------|----------|-------------------| -All files | 80 | 100 | 50 | 80 | | +All files | 83.33 | 100 | 66.67 | 83.33 | | + export.cjs | 100 | 100 | 100 | 100 | | export.mjs | 71.43 | 100 | 50 | 71.43 | 2,3 | import.mjs | 100 | 100 | 100 | 100 | | ------------|----------|----------|----------|----------|-------------------|