diff --git a/package-lock.json b/package-lock.json index d44b853..83cc951 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@istanbuljs/load-nyc-config": "^1.1.0", + "espree": "^9.5.2", "istanbul-lib-instrument": "^5.1.0", "picocolors": "^1.0.0", "test-exclude": "^6.0.0" @@ -1422,7 +1423,6 @@ "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -1430,6 +1430,14 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -2304,6 +2312,33 @@ "node": ">=0.8.0" } }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -9613,8 +9648,13 @@ "acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "requires": {} }, "agent-base": { "version": "6.0.2", @@ -10268,6 +10308,21 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==" + }, + "espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", diff --git a/package.json b/package.json index 45ee231..d5d61fe 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ ], "dependencies": { "@istanbuljs/load-nyc-config": "^1.1.0", + "espree": "^9.5.2", "istanbul-lib-instrument": "^5.1.0", "picocolors": "^1.0.0", "test-exclude": "^6.0.0" diff --git a/src/index.ts b/src/index.ts index 417e555..7755244 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import { createInstrumenter } from 'istanbul-lib-instrument'; import TestExclude from 'test-exclude'; import { loadNycConfig } from '@istanbuljs/load-nyc-config'; import picocolors from 'picocolors'; +import {createIdentitySourceMap} from "./source-map"; const { yellow } = picocolors; @@ -181,9 +182,13 @@ export default function istanbulPlugin(opts: IstanbulPluginOptions = {}): Plugin const filename = resolveFilename(id); if (testExclude.shouldInstrument(filename)) { + // Create a source map to combine with the source map of previous plugins + instrumenter.instrumentSync(srcCode, filename, createIdentitySourceMap(filename, srcCode)) + const map = instrumenter.lastSourceMap(); + + // Instrument code using the source map of previous plugins const sourceMap = sanitizeSourceMap(this.getCombinedSourcemap()); const code = instrumenter.instrumentSync(srcCode, filename, sourceMap); - const map = instrumenter.lastSourceMap(); // Required to cast to correct mapping value return { code, map } as TransformResult; diff --git a/src/source-map.ts b/src/source-map.ts new file mode 100644 index 0000000..304714b --- /dev/null +++ b/src/source-map.ts @@ -0,0 +1,19 @@ +import * as espree from 'espree' +import {SourceMapGenerator} from 'source-map' + + +export function createIdentitySourceMap(file: string, source: string) { + const gen = new SourceMapGenerator({ file }); + const tokens = espree.tokenize(source, { loc: true, ecmaVersion: 'latest' }); + + tokens.forEach((token: any) => { + const loc = token.loc.start; + gen.addMapping({ + source: file, + original: loc, + generated: loc + }); + }); + + return JSON.parse(gen.toString()) +} diff --git a/src/types.d.ts b/src/types.d.ts index 3b974b8..92f0740 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -51,3 +51,41 @@ declare module 'test-exclude' { export = TestExclude; } + +declare module 'espree' { + // https://github.com/eslint/espree#options + export interface Options { + comment?: boolean; + ecmaFeatures?: { + globalReturn?: boolean; + impliedStrict?: boolean; + jsx?: boolean; + }; + ecmaVersion?: + | 3 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 2015 + | 2016 + | 2017 + | 2018 + | 2019 + | 2020 + | 2021 + | 2022 + | 2023 + | 'latest'; + loc?: boolean; + range?: boolean; + sourceType?: 'script' | 'module'; + tokens?: boolean; + } + // https://github.com/eslint/espree#tokenize + export function tokenize(code: string, options?: Options): any; +}