Skip to content

Commit

Permalink
Fix sourcemaps for vue
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinpalkovic committed Nov 13, 2023
1 parent 2e9b10e commit 12a4851
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 48 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@
"@jsdevtools/coverage-istanbul-loader": "^3.0.5",
"@types/istanbul-lib-coverage": "^2.0.4",
"convert-source-map": "^2.0.0",
"espree": "^9.6.1",
"istanbul-lib-instrument": "^6.0.1",
"loader-utils": "^3.2.1",
"merge-source-map": "^1.1.0",
"source-map": "^0.7.4",
"test-exclude": "^6.0.0",
"vite-plugin-istanbul": "^3.0.1"
}
Expand Down
102 changes: 64 additions & 38 deletions src/loader/webpack5-istanbul-loader.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { Instrumenter, InstrumenterOptions } from "istanbul-lib-instrument";
import { fromSource, fromMapFileSource } from "convert-source-map";
import {
createInstrumenter,
InstrumenterOptions,
} from "istanbul-lib-instrument";

// @ts-expect-error no types
import mergeSourceMap from "merge-source-map";
import { LoaderContext } from "webpack";
import * as espree from "espree";
import fs from "fs";
import path from "path";
import { LoaderContext } from "webpack";
import { SourceMapGenerator, StartOfSourceMap } from "source-map";

import { AddonOptionsWebpack } from "../types";

export type Options = Partial<InstrumenterOptions> & AddonOptionsWebpack;
export type Options = Partial<InstrumenterOptions> &
AddonOptionsWebpack & {
instrumenter: Instrumenter;
};

type RawSourceMap = {
version: number;
Expand All @@ -22,51 +25,74 @@ type RawSourceMap = {
names?: string[];
};

export const defaultOptions: Partial<InstrumenterOptions> = {
preserveComments: true,
produceSourceMap: true,
autoWrap: true,
esModules: true,
compact: false,
};
function sanitizeSourceMap(rawSourceMap: RawSourceMap): RawSourceMap {
const { sourcesContent, ...sourceMap } = rawSourceMap ?? {};

// JSON parse/stringify trick required for istanbul to accept the SourceMap
return JSON.parse(JSON.stringify(sourceMap));
}

function createIdentitySourceMap(
file: string,
source: string,
option: StartOfSourceMap
) {
const gen = new SourceMapGenerator(option);
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());
}

export default function (
this: LoaderContext<Options>,
source: string,
sourceMap?: RawSourceMap
) {
let map = sourceMap;
let options = Object.assign(defaultOptions, this.getOptions());
let map = sourceMap ?? getInlineSourceMap.call(this, source);
const options = this.getOptions();
const callback = this.async();

// If there's no external sourceMap file, then check for an inline sourceMap
if (!map) {
map = sourceMap = getInlineSourceMap.call(this, source);
callback(null, source, sourceMap);
return;
}

// Instrument the code
let instrumenter = createInstrumenter(options);
instrumenter.instrument(
const instrumenter = options.instrumenter;

const combinedSourceMap = sanitizeSourceMap(sourceMap);

const code = instrumenter.instrumentSync(
source,
this.resourcePath,
(error, instrumentedSource) => {
let instrumentedSourceMap = instrumenter.lastSourceMap();

if (sourceMap && instrumentedSourceMap) {
// Re-map the source map to the original source code
instrumentedSourceMap = mergeSourceMap(
sourceMap,
instrumentedSourceMap
);
}

this.callback(
error,
instrumentedSource,
instrumentedSourceMap as any as RawSourceMap
);
},
sourceMap as any
combinedSourceMap as any
);

const identitySourceMap = sanitizeSourceMap(
createIdentitySourceMap(this.resourcePath, source, {
file: combinedSourceMap.file,
sourceRoot: combinedSourceMap.sourceRoot,
})
);

instrumenter.instrumentSync(
source,
this.resourcePath,
identitySourceMap as any
);

const lastSourceMap = instrumenter.lastSourceMap();

callback(null, code, lastSourceMap as any);
}

/**
Expand Down
22 changes: 20 additions & 2 deletions src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { defaultExclude, defaultExtensions } from "./constants";
import type { AddonOptionsVite, AddonOptionsWebpack } from "./types";
import { createTestExclude } from "./webpack5-exclude";
import { getNycConfig } from "./nyc-config";
import {
InstrumenterOptions,
createInstrumenter,
} from "istanbul-lib-instrument";

export const viteFinal = async (
viteConfig: Record<string, any>,
Expand Down Expand Up @@ -32,6 +36,14 @@ export const viteFinal = async (
return viteConfig;
};

const defaultOptions: Partial<InstrumenterOptions> = {
preserveComments: true,
produceSourceMap: true,
autoWrap: true,
esModules: true,
compact: false,
};

export const webpackFinal = async (
webpackConfig: Record<string, any>,
options: Options & AddonOptionsWebpack
Expand All @@ -45,11 +57,17 @@ export const webpackFinal = async (

const testExclude = await createTestExclude(options.istanbul);

webpackConfig.module.rules.push({
let instrumenterOptions = Object.assign(defaultOptions, options.istanbul);
let instrumenter = createInstrumenter(instrumenterOptions);

webpackConfig.module.rules.unshift({
test: new RegExp(extensions?.join("|").replace(/\./g, "\\.")),
loader: require.resolve("./loader/webpack5-istanbul-loader"),
enforce: "post",
options: options.istanbul || {},
options: {
...(options.istanbul ?? {}),
instrumenter,
},
include: (modulePath: string) => testExclude.shouldInstrument(modulePath),
});

Expand Down
31 changes: 25 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1903,7 +1903,12 @@ acorn-import-assertions@^1.9.0:
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac"
integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==

acorn@^8.7.1, acorn@^8.8.2:
acorn-jsx@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==

acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0:
version "8.11.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
Expand Down Expand Up @@ -3039,6 +3044,20 @@ [email protected]:
esrecurse "^4.3.0"
estraverse "^4.1.1"

eslint-visitor-keys@^3.4.1:
version "3.4.3"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==

espree@^9.6.1:
version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
dependencies:
acorn "^8.9.0"
acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.4.1"

esprima@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
Expand Down Expand Up @@ -4045,11 +4064,6 @@ loader-utils@^2.0.0:
emojis-list "^3.0.0"
json5 "^2.1.2"

loader-utils@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576"
integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==

locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
Expand Down Expand Up @@ -5282,6 +5296,11 @@ source-map@^0.6.0, source-map@^0.6.1:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==

source-map@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==

spawn-command@^0.0.2-1:
version "0.0.2-1"
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
Expand Down

0 comments on commit 12a4851

Please sign in to comment.