Files
node/benchmark/module/module-require-source-map.js
Shelley Vohr 728ae35678 lib: defer source map payload decoding until first use
With source maps enabled, every module that carries a sourceMappingURL
had its map decoded (or read from disk), JSON-parsed and its sources
resolved to absolute URLs while the module was being loaded, and the
per-line length table used for coverage was built with a per-code-point
loop. None of that is needed unless a stack trace is later mapped.

Keep the URL on the cache entry and resolve the payload on the first
findSourceMap() for that file. Under NODE_V8_COVERAGE the payload is
still resolved at load time, since the cache is serialized during
shutdown. lineLengths() now splits on '\n' with indexOf and only falls
back to the code point walk when the source contains U+2028/U+2029.

Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65490
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-09-02 18:11:27 +00:00

78 lines
2.4 KiB
JavaScript

'use strict';
// Loading modules that carry source maps, with source map support enabled.
// This is the cost paid at startup by applications bundled or transpiled with
// source maps; the maps are only consulted if a stack trace is generated.
const fs = require('fs');
const path = require('path');
const common = require('../common.js');
const tmpdir = require('../../test/common/tmpdir');
const benchmarkDirectory = tmpdir.resolve('nodejs-benchmark-module-source-map');
const bench = common.createBenchmark(main, {
sourceMap: ['none', 'inline', 'external'],
n: [1000],
}, {
setup(configs) {
tmpdir.refresh();
const maxN = configs.reduce((max, c) => Math.max(max, c.n), 0);
createModules(maxN);
},
});
function moduleSource(i) {
const methods = [];
for (let m = 0; m < 40; m++) {
methods.push(` method${m}(input) { return [].concat(input).map((item) => ({ item, m: ${m}, service: ${i} })); }`);
}
return `'use strict';
class Service${i} {
constructor(options = {}) { this.options = { retries: 3, ...options }; }
${methods.join('\n')}
}
function helper${i}(list) { return list.filter(Boolean).slice(0, ${i % 7}); }
module.exports = { Service${i}, helper${i} };
`;
}
function sourceMapFor(i, source) {
return JSON.stringify({
version: 3,
file: `${i}.js`,
sources: [`../src/${i}.ts`],
sourcesContent: [source],
names: [],
mappings: 'AAAA;' + 'AACA,MAAM;'.repeat(44),
});
}
function createModules(n) {
for (const kind of ['none', 'inline', 'external']) {
const dir = path.join(benchmarkDirectory, kind);
fs.mkdirSync(dir, { recursive: true });
for (let i = 0; i < n; i++) {
const source = moduleSource(i);
let trailer = '';
if (kind === 'inline') {
const data = Buffer.from(sourceMapFor(i, source)).toString('base64');
trailer = `//# sourceMappingURL=data:application/json;base64,${data}\n`;
} else if (kind === 'external') {
fs.writeFileSync(path.join(dir, `${i}.js.map`), sourceMapFor(i, source));
trailer = `//# sourceMappingURL=${i}.js.map\n`;
}
fs.writeFileSync(path.join(dir, `${i}.js`), source + trailer);
}
}
}
function main({ sourceMap, n }) {
process.setSourceMapsEnabled(true);
const dir = path.join(benchmarkDirectory, sourceMap);
bench.start();
for (let i = 0; i < n; i++) {
require(path.join(dir, `${i}.js`));
}
bench.end(n);
}