Skip to content

Commit 2fb007f

Browse files
legendecasaduh95
authored andcommitted
lib: allow skipping source maps in node_modules
Files in `node_modules` are not authored by the user directly and the original sources are less relevant to the user. Skipping source maps in `node_modules` improves the general performance. Add `module.setSourceMapsSupport(enabled, options)` to skip source maps in `node_modules` if it is needed. This moves all source maps related API to `node:module` and this a step to promote the source maps API to stable. PR-URL: #56639 Reviewed-By: Vinícius Lourenço Claro Cardoso <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent f885496 commit 2fb007f

24 files changed

+491
-48
lines changed

‎benchmark/es/error-stack.js‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
constcommon=require('../common.js');
44
constmodPath=require.resolve('../fixtures/simple-error-stack.js');
5+
constnodeModulePath=require.resolve('../fixtures/node_modules/error-stack/simple-error-stack.js');
6+
constModule=require('node:module');
57

68
constbench=common.createBenchmark(main,{
7-
method: ['without-sourcemap','sourcemap'],
9+
method: [
10+
'without-sourcemap',
11+
'sourcemap',
12+
'node-modules-without-sourcemap',
13+
'node-module-sourcemap'],
814
n: [1e5],
915
});
1016

11-
functionrunN(n){
17+
functionrunN(n,modPath){
1218
deleterequire.cache[modPath];
1319
constmod=require(modPath);
1420
bench.start();
@@ -22,11 +28,23 @@ function main({n, method }){
2228
switch(method){
2329
case'without-sourcemap':
2430
process.setSourceMapsEnabled(false);
25-
runN(n);
31+
runN(n,modPath);
2632
break;
2733
case'sourcemap':
2834
process.setSourceMapsEnabled(true);
29-
runN(n);
35+
runN(n,modPath);
36+
break;
37+
case'node-modules-without-sourcemap':
38+
Module.setSourceMapsSupport(true,{
39+
nodeModules: false,
40+
});
41+
runN(n,nodeModulePath);
42+
break;
43+
case'node-modules-sourcemap':
44+
Module.setSourceMapsSupport(true,{
45+
nodeModules: true,
46+
});
47+
runN(n,nodeModulePath);
3048
break;
3149
default:
3250
thrownewError(`Unexpected method "${method}"`);

‎benchmark/fixtures/node_modules/error-stack/simple-error-stack.js‎

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

‎benchmark/fixtures/node_modules/error-stack/simple-error-stack.ts‎

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

‎doc/api/module.md‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,20 @@ import{findSourceMap, SourceMap } from 'node:module'
15901590
const{findSourceMap, SourceMap } =require('node:module');
15911591
```
15921592
1593+
### `module.getSourceMapsSupport()`
1594+
1595+
<!-- YAML
1596+
added: REPLACEME
1597+
-->
1598+
1599+
* Returns:{Object}
1600+
* `enabled`{boolean} If the source maps support is enabled
1601+
* `nodeModules`{boolean} If the support is enabled for files in `node_modules`.
1602+
* `generatedCode`{boolean} If the support is enabled for generated code from `eval` or `newFunction`.
1603+
1604+
This method returns whether the [Source Map v3][Source Map] support for stack
1605+
traces is enabled.
1606+
15931607
<!-- Anchors to make sure old links find a target -->
15941608
15951609
<a id="module_module_findsourcemap_path_error"></a>
@@ -1609,6 +1623,31 @@ added:
16091623
`path` is the resolved path for the file for which a corresponding source map
16101624
should be fetched.
16111625
1626+
### `module.setSourceMapsSupport(enabled[, options])`
1627+
1628+
<!-- YAML
1629+
added: REPLACEME
1630+
-->
1631+
1632+
* `enabled`{boolean} Enable the source map support.
1633+
* `options`{Object} Optional
1634+
* `nodeModules`{boolean} If enabling the support for files in
1635+
`node_modules`. **Default:** `false`.
1636+
* `generatedCode`{boolean} If enabling the support for generated code from
1637+
`eval` or `newFunction`. **Default:** `false`.
1638+
1639+
This function enables or disables the [Source Map v3][Source Map] support for
1640+
stack traces.
1641+
1642+
It provides same features as launching Node.js process with commandline options
1643+
`--enable-source-maps`, with additional options to alter the support for files
1644+
in `node_modules` or generated codes.
1645+
1646+
Only source maps in JavaScript files that are loaded after source maps has been
1647+
enabled will be parsed and loaded. Preferably, use the commandline options
1648+
`--enable-source-maps` to avoid losing track of source maps of modules loaded
1649+
before this API call.
1650+
16121651
### Class: `module.SourceMap`
16131652
16141653
<!-- YAML
@@ -1709,6 +1748,7 @@ returned object contains the following keys:
17091748
[Customization hooks]: #customization-hooks
17101749
[ES Modules]: esm.md
17111750
[Permission Model]: permissions.md#permission-model
1751+
[Source Map]: https://sourcemaps.info/spec.html
17121752
[Source map v3 format]: https://sourcemaps.info/spec.html#h.mofvlxcwqzej
17131753
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
17141754
[V8 code cache]: https://v8.dev/blog/code-caching-for-devs

‎doc/api/process.md‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3987,7 +3987,7 @@ added:
39873987
- v14.18.0
39883988
-->
39893989
3990-
> Stability: 1 - Experimental
3990+
> Stability: 1 - Experimental: Use [`module.setSourceMapsSupport()`][] instead.
39913991
39923992
* `val`{boolean}
39933993
@@ -4000,6 +4000,9 @@ It provides same features as launching Node.js process with commandline options
40004000
Only source maps in JavaScript files that are loaded after source maps has been
40014001
enabled will be parsed and loaded.
40024002
4003+
This implies calling `module.setSourceMapsSupport()` with an option
4004+
`{nodeModules:true, generatedCode:true }`.
4005+
40034006
## `process.setUncaughtExceptionCaptureCallback(fn)`
40044007
40054008
<!-- YAML
@@ -4034,7 +4037,7 @@ added:
40344037
- v18.19.0
40354038
-->
40364039
4037-
> Stability: 1 - Experimental
4040+
> Stability: 1 - Experimental: Use [`module.getSourceMapsSupport()`][] instead.
40384041
40394042
*{boolean}
40404043
@@ -4501,7 +4504,9 @@ cases:
45014504
[`console.error()`]: console.md#consoleerrordata-args
45024505
[`console.log()`]: console.md#consolelogdata-args
45034506
[`domain`]: domain.md
4507+
[`module.getSourceMapsSupport()`]: module.md#modulegetsourcemapssupport
45044508
[`module.isBuiltin(id)`]: module.md#moduleisbuiltinmodulename
4509+
[`module.setSourceMapsSupport()`]: module.md#modulesetsourcemapssupportenabled-options
45054510
[`net.Server`]: net.md#class-netserver
45064511
[`net.Socket`]: net.md#class-netsocket
45074512
[`os.constants.dlopen`]: os.md#dlopen-constants

‎lib/internal/bootstrap/node.js‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,8 @@ internalBinding('process_methods').setEmitWarningSync(emitWarningSync);
368368

369369
{
370370
const{
371-
getSourceMapsEnabled,
372-
setSourceMapsEnabled,
371+
getSourceMapsSupport,
372+
setSourceMapsSupport,
373373
maybeCacheGeneratedSourceMap,
374374
}=require('internal/source_map/source_map_cache');
375375
const{
@@ -381,10 +381,19 @@ internalBinding('process_methods').setEmitWarningSync(emitWarningSync);
381381
enumerable: true,
382382
configurable: true,
383383
get(){
384-
returngetSourceMapsEnabled();
384+
returngetSourceMapsSupport().enabled;
385385
},
386386
});
387-
process.setSourceMapsEnabled=setSourceMapsEnabled;
387+
process.setSourceMapsEnabled=functionsetSourceMapsEnabled(val){
388+
setSourceMapsSupport(val,{
389+
__proto__: null,
390+
// TODO(legendecas): In order to smoothly improve the source map support,
391+
// skip source maps in node_modules and generated code with
392+
// `process.setSourceMapsEnabled(true)` in a semver major version.
393+
nodeModules: val,
394+
generatedCode: val,
395+
});
396+
};
388397
// The C++ land calls back to maybeCacheGeneratedSourceMap()
389398
// when code is generated by user with eval() or new Function()
390399
// to cache the source maps from the evaluated code, if any.

‎lib/internal/modules/esm/module_job.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const{
3030
}=internalBinding('util');
3131
const{ decorateErrorStack, kEmptyObject }=require('internal/util');
3232
const{
33-
getSourceMapsEnabled,
33+
getSourceMapsSupport,
3434
}=require('internal/source_map/source_map_cache');
3535
constassert=require('internal/assert');
3636
constresolvedPromise=PromiseResolve();
@@ -186,7 +186,7 @@ class ModuleJob extends ModuleJobBase{
186186
// of missing named export. This is currently not possible because
187187
// stack trace originates in module_job, not the file itself. A hidden
188188
// symbol with filename could be set in node_errors.cc to facilitate this.
189-
if(!getSourceMapsEnabled()&&
189+
if(!getSourceMapsSupport().enabled&&
190190
StringPrototypeIncludes(e.message,
191191
' does not provide an export named')){
192192
constsplitStack=StringPrototypeSplit(e.stack,'\n');

‎lib/internal/process/pre_execution.js‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,9 +608,17 @@ function initializeESMLoader(forceDefaultLoader){
608608

609609
functioninitializeSourceMapsHandlers(){
610610
const{
611-
setSourceMapsEnabled,
611+
setSourceMapsSupport,
612612
}=require('internal/source_map/source_map_cache');
613-
setSourceMapsEnabled(getOptionValue('--enable-source-maps'));
613+
constenabled=getOptionValue('--enable-source-maps');
614+
setSourceMapsSupport(enabled,{
615+
__proto__: null,
616+
// TODO(legendecas): In order to smoothly improve the source map support,
617+
// skip source maps in node_modules and generated code with
618+
// `--enable-source-maps` in a semver major version.
619+
nodeModules: enabled,
620+
generatedCode: enabled,
621+
});
614622
}
615623

616624
functioninitializeFrozenIntrinsics(){

‎lib/internal/source_map/source_map_cache.js‎

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const{
44
ArrayPrototypePush,
55
JSONParse,
6+
ObjectFreeze,
67
RegExpPrototypeExec,
78
SafeMap,
89
StringPrototypeCodePointAt,
@@ -15,15 +16,15 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) =>{
1516
debug=fn;
1617
});
1718

18-
const{ validateBoolean }=require('internal/validators');
19+
const{ validateBoolean, validateObject}=require('internal/validators');
1920
const{
2021
setSourceMapsEnabled: setSourceMapsNative,
2122
}=internalBinding('errors');
2223
const{
2324
defaultPrepareStackTrace,
2425
setInternalPrepareStackTrace,
2526
}=require('internal/errors');
26-
const{ getLazy }=require('internal/util');
27+
const{ getLazy, isUnderNodeModules, kEmptyObject}=require('internal/util');
2728

2829
constgetModuleSourceMapCache=getLazy(()=>{
2930
const{ SourceMapCacheMap }=require('internal/source_map/source_map_cache_map');
@@ -45,30 +46,48 @@ const{fileURLToPath, pathToFileURL, URL, URLParse } = require('internal/url');
4546
letSourceMap;
4647

4748
// This is configured with --enable-source-maps during pre-execution.
48-
letsourceMapsEnabled=false;
49-
functiongetSourceMapsEnabled(){
50-
returnsourceMapsEnabled;
49+
letsourceMapsSupport=ObjectFreeze({
50+
__proto__: null,
51+
enabled: false,
52+
nodeModules: false,
53+
generatedCode: false,
54+
});
55+
functiongetSourceMapsSupport(){
56+
// Return a read-only object.
57+
returnsourceMapsSupport;
5158
}
5259

5360
/**
5461
* Enables or disables source maps programmatically.
55-
* @param{boolean} val
62+
* @param{boolean} enabled
63+
* @param{object} options
64+
* @param{boolean} [options.nodeModules]
65+
* @param{boolean} [options.generatedCode]
5666
*/
57-
functionsetSourceMapsEnabled(val){
58-
validateBoolean(val,'val');
67+
functionsetSourceMapsSupport(enabled,options=kEmptyObject){
68+
validateBoolean(enabled,'enabled');
69+
validateObject(options,'options');
70+
71+
const{ nodeModules =false, generatedCode =false}=options;
72+
validateBoolean(nodeModules,'options.nodeModules');
73+
validateBoolean(generatedCode,'options.generatedCode');
5974

60-
setSourceMapsNative(val);
61-
if(val){
75+
setSourceMapsNative(enabled);
76+
if(enabled){
6277
const{
6378
prepareStackTraceWithSourceMaps,
6479
}=require('internal/source_map/prepare_stack_trace');
6580
setInternalPrepareStackTrace(prepareStackTraceWithSourceMaps);
66-
}elseif(sourceMapsEnabled!==undefined){
67-
// Reset prepare stack trace callback only when disabling source maps.
81+
}else{
6882
setInternalPrepareStackTrace(defaultPrepareStackTrace);
6983
}
7084

71-
sourceMapsEnabled=val;
85+
sourceMapsSupport=ObjectFreeze({
86+
__proto__: null,
87+
enabled,
88+
nodeModules: nodeModules,
89+
generatedCode: generatedCode,
90+
});
7291
}
7392

7493
/**
@@ -130,14 +149,18 @@ function extractSourceMapURLMagicComment(content){
130149
* @param{string | undefined} sourceMapURL - the source map url
131150
*/
132151
functionmaybeCacheSourceMap(filename,content,moduleInstance,isGeneratedSource,sourceURL,sourceMapURL){
133-
constsourceMapsEnabled=getSourceMapsEnabled();
134-
if(!(process.env.NODE_V8_COVERAGE||sourceMapsEnabled))return;
152+
constsupport=getSourceMapsSupport();
153+
if(!(process.env.NODE_V8_COVERAGE||support.enabled))return;
135154
const{ normalizeReferrerURL }=require('internal/modules/helpers');
136155
filename=normalizeReferrerURL(filename);
137156
if(filename===undefined){
138157
// This is most likely an invalid filename in sourceURL of [eval]-wrapper.
139158
return;
140159
}
160+
if(!support.nodeModules&&isUnderNodeModules(filename)){
161+
// Skip file under node_modules if not enabled.
162+
return;
163+
}
141164

142165
if(sourceMapURL===undefined){
143166
sourceMapURL=extractSourceMapURLMagicComment(content);
@@ -185,8 +208,8 @@ function maybeCacheSourceMap(filename, content, moduleInstance, isGeneratedSourc
185208
* @param{string} content - the eval'd source code
186209
*/
187210
functionmaybeCacheGeneratedSourceMap(content){
188-
constsourceMapsEnabled=getSourceMapsEnabled();
189-
if(!(process.env.NODE_V8_COVERAGE||sourceMapsEnabled))return;
211+
constsupport=getSourceMapsSupport();
212+
if(!(process.env.NODE_V8_COVERAGE||support.enabled||support.generated))return;
190213

191214
constsourceURL=extractSourceURLMagicComment(content);
192215
if(sourceURL===null){
@@ -352,6 +375,10 @@ function findSourceMap(sourceURL){
352375
returnundefined;
353376
}
354377

378+
if(!getSourceMapsSupport().nodeModules&&isUnderNodeModules(sourceURL)){
379+
returnundefined;
380+
}
381+
355382
SourceMap??=require('internal/source_map/source_map').SourceMap;
356383
try{
357384
if(RegExpPrototypeExec(kLeadingProtocol,sourceURL)===null){
@@ -377,8 +404,8 @@ function findSourceMap(sourceURL){
377404

378405
module.exports={
379406
findSourceMap,
380-
getSourceMapsEnabled,
381-
setSourceMapsEnabled,
407+
getSourceMapsSupport,
408+
setSourceMapsSupport,
382409
maybeCacheSourceMap,
383410
maybeCacheGeneratedSourceMap,
384411
sourceMapCacheToObject,

0 commit comments

Comments
(0)