Skip to content

Commit

Permalink
process: add api to enable source-maps programmatically
Browse files Browse the repository at this point in the history
Add `process.setSourceMapsEnabled` to enable
source-maps programmatically.

PR-URL: #39085
Reviewed-By: Ben Coe <bencoe@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
legendecas authored and BethGriggs committed Jul 29, 2021
1 parent e44ccd9 commit cdf7251
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 16 deletions.
19 changes: 19 additions & 0 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -3248,6 +3248,24 @@ This function is only available on POSIX platforms (i.e. not Windows or
Android).
This feature is not available in [`Worker`][] threads.
## `process.setSourceMapsEnabled(val)`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental
* `val` {boolean}
This function enables or disables the [Source Map v3][Source Map] support for
stack traces.
It provides same features as launching Node.js process with commandline options
`--enable-source-maps`.
Only source maps in JavaScript files that are loaded after source maps has been
enabled will be parsed and loaded.
## `process.setUncaughtExceptionCaptureCallback(fn)`
<!-- YAML
added: v9.3.0
Expand Down Expand Up @@ -3669,6 +3687,7 @@ cases:
[LTS]: https://github.com/nodejs/Release
[Readable]: stream.md#stream_readable_streams
[Signal Events]: #process_signal_events
[Source Map]: https://sourcemaps.info/spec.html
[Stream compatibility]: stream.md#stream_compatibility_with_older_node_js_versions
[TTY]: tty.md#tty_tty
[Writable]: stream.md#stream_writable_streams
Expand Down
8 changes: 8 additions & 0 deletions lib/internal/bootstrap/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
// (including preload modules).
initializeClusterIPC();

initializeSourceMapsHandlers();
initializeDeprecations();
initializeWASI();
initializeCJSLoader();
Expand Down Expand Up @@ -454,6 +455,12 @@ function initializeESMLoader() {
}
}

function initializeSourceMapsHandlers() {
const { setSourceMapsEnabled } =
require('internal/source_map/source_map_cache');
process.setSourceMapsEnabled = setSourceMapsEnabled;
}

function initializeFrozenIntrinsics() {
if (getOptionValue('--frozen-intrinsics')) {
process.emitWarning('The --frozen-intrinsics flag is experimental',
Expand Down Expand Up @@ -485,6 +492,7 @@ module.exports = {
initializeDeprecations,
initializeESMLoader,
initializeFrozenIntrinsics,
initializeSourceMapsHandlers,
loadPreloadModules,
setupTraceCategoryState,
setupInspectorHooks,
Expand Down
2 changes: 2 additions & 0 deletions lib/internal/main/worker_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const {
initializeESMLoader,
initializeFrozenIntrinsics,
initializeReport,
initializeSourceMapsHandlers,
loadPreloadModules,
setupTraceCategoryState
} = require('internal/bootstrap/pre_execution');
Expand Down Expand Up @@ -66,6 +67,7 @@ setupInspectorHooks();
setupDebugEnv();

setupWarningHandler();
initializeSourceMapsHandlers();

// Since worker threads cannot switch cwd, we do not need to
// overwrite the process.env.NODE_V8_COVERAGE variable.
Expand Down
39 changes: 27 additions & 12 deletions lib/internal/source_map/source_map_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const { IterableWeakMap } = require('internal/util/iterable_weak_map');
const {
normalizeReferrerURL,
} = require('internal/modules/cjs/helpers');
const { validateBoolean } = require('internal/validators');
// Since the CJS module cache is mutable, which leads to memory leaks when
// modules are deleted, we use a WeakMap so that the source map cache will
// be purged automatically:
Expand All @@ -41,22 +42,35 @@ let SourceMap;
let sourceMapsEnabled;
function getSourceMapsEnabled() {
if (sourceMapsEnabled === undefined) {
sourceMapsEnabled = getOptionValue('--enable-source-maps');
if (sourceMapsEnabled) {
const {
enableSourceMaps,
setPrepareStackTraceCallback
} = internalBinding('errors');
const {
prepareStackTrace
} = require('internal/source_map/prepare_stack_trace');
setPrepareStackTraceCallback(prepareStackTrace);
enableSourceMaps();
}
setSourceMapsEnabled(getOptionValue('--enable-source-maps'));
}
return sourceMapsEnabled;
}

function setSourceMapsEnabled(val) {
validateBoolean(val, 'val');

const {
setSourceMapsEnabled,
setPrepareStackTraceCallback
} = internalBinding('errors');
setSourceMapsEnabled(val);
if (val) {
const {
prepareStackTrace
} = require('internal/source_map/prepare_stack_trace');
setPrepareStackTraceCallback(prepareStackTrace);
} else if (sourceMapsEnabled !== undefined) {
// Reset prepare stack trace callback only when disabling source maps.
const {
prepareStackTrace,
} = require('internal/errors');
setPrepareStackTraceCallback(prepareStackTrace);
}

sourceMapsEnabled = val;
}

function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
const sourceMapsEnabled = getSourceMapsEnabled();
if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return;
Expand Down Expand Up @@ -231,6 +245,7 @@ function findSourceMap(sourceURL) {
module.exports = {
findSourceMap,
getSourceMapsEnabled,
setSourceMapsEnabled,
maybeCacheSourceMap,
sourceMapCacheToObject,
};
9 changes: 5 additions & 4 deletions src/node_errors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -820,9 +820,10 @@ void SetPrepareStackTraceCallback(const FunctionCallbackInfo<Value>& args) {
env->set_prepare_stack_trace_callback(args[0].As<Function>());
}

static void EnableSourceMaps(const FunctionCallbackInfo<Value>& args) {
static void SetSourceMapsEnabled(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
env->set_source_maps_enabled(true);
CHECK(args[0]->IsBoolean());
env->set_source_maps_enabled(args[0].As<Boolean>()->Value());
}

static void SetEnhanceStackForFatalException(
Expand Down Expand Up @@ -858,7 +859,7 @@ static void TriggerUncaughtException(const FunctionCallbackInfo<Value>& args) {

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(SetPrepareStackTraceCallback);
registry->Register(EnableSourceMaps);
registry->Register(SetSourceMapsEnabled);
registry->Register(SetEnhanceStackForFatalException);
registry->Register(NoSideEffectsToString);
registry->Register(TriggerUncaughtException);
Expand All @@ -871,7 +872,7 @@ void Initialize(Local<Object> target,
Environment* env = Environment::GetCurrent(context);
env->SetMethod(
target, "setPrepareStackTraceCallback", SetPrepareStackTraceCallback);
env->SetMethod(target, "enableSourceMaps", EnableSourceMaps);
env->SetMethod(target, "setSourceMapsEnabled", SetSourceMapsEnabled);
env->SetMethod(target,
"setEnhanceStackForFatalException",
SetEnhanceStackForFatalException);
Expand Down
20 changes: 20 additions & 0 deletions test/message/source_map_disabled_by_api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Flags: --enable-source-maps

'use strict';
require('../common');

process.setSourceMapsEnabled(false);

try {
require('../fixtures/source-map/enclosing-call-site-min.js');
} catch (e) {
console.log(e);
}

delete require.cache[require
.resolve('../fixtures/source-map/enclosing-call-site-min.js')];

// Re-enable.
process.setSourceMapsEnabled(true);

require('../fixtures/source-map/enclosing-call-site-min.js');
26 changes: 26 additions & 0 deletions test/message/source_map_disabled_by_api.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Error: an error!
at functionD (*enclosing-call-site-min.js:1:156)
at functionC (*enclosing-call-site-min.js:1:97)
at functionB (*enclosing-call-site-min.js:1:60)
at functionA (*enclosing-call-site-min.js:1:26)
at Object.<anonymous> (*enclosing-call-site-min.js:1:199)
at Module._compile (node:internal/modules/cjs/loader:*)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:*)
at Module.load (node:internal/modules/cjs/loader:*)
at Function.Module._load (node:internal/modules/cjs/loader:*)
at Module.require (node:internal/modules/cjs/loader:*)
*enclosing-call-site.js:16
throw new Error('an error!')
^

Error: an error!
at functionD (*enclosing-call-site.js:16:17)
at functionC (*enclosing-call-site.js:10:3)
at functionB (*enclosing-call-site.js:6:3)
at functionA (*enclosing-call-site.js:2:3)
at Object.<anonymous> (*enclosing-call-site.js:24:3)
at Module._compile (node:internal/modules/cjs/loader:*)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:*)
at Module.load (node:internal/modules/cjs/loader:*)
at Function.Module._load (node:internal/modules/cjs/loader:*)
at Module.require (node:internal/modules/cjs/loader:*)
17 changes: 17 additions & 0 deletions test/message/source_map_enabled_by_api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';
require('../common');

process.setSourceMapsEnabled(true);

try {
require('../fixtures/source-map/enclosing-call-site-min.js');
} catch (e) {
console.log(e);
}

delete require.cache[require
.resolve('../fixtures/source-map/enclosing-call-site-min.js')];

process.setSourceMapsEnabled(false);

require('../fixtures/source-map/enclosing-call-site-min.js');
30 changes: 30 additions & 0 deletions test/message/source_map_enabled_by_api.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
*enclosing-call-site.js:16
throw new Error('an error!')
^

Error: an error!
at functionD (*enclosing-call-site.js:16:17)
at functionC (*enclosing-call-site.js:10:3)
at functionB (*enclosing-call-site.js:6:3)
at functionA (*enclosing-call-site.js:2:3)
at Object.<anonymous> (*enclosing-call-site.js:24:3)
at Module._compile (node:internal/modules/cjs/loader:*)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:*)
at Module.load (node:internal/modules/cjs/loader:*)
at Function.Module._load (node:internal/modules/cjs/loader:*)
at Module.require (node:internal/modules/cjs/loader:*)
*enclosing-call-site-min.js:1
var functionA=function(){functionB()};function functionB(){functionC()}var functionC=function(){functionD()},functionD=function(){if(0<Math.random())throw Error("an error!");},thrower=functionA;try{functionA()}catch(a){throw a;};
^

Error: an error!
at functionD (*enclosing-call-site-min.js:1:156)
at functionC (*enclosing-call-site-min.js:1:97)
at functionB (*enclosing-call-site-min.js:1:60)
at functionA (*enclosing-call-site-min.js:1:26)
at Object.<anonymous> (*enclosing-call-site-min.js:1:199)
at Module._compile (node:internal/modules/cjs/loader:*)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:*)
at Module.load (node:internal/modules/cjs/loader:*)
at Function.Module._load (node:internal/modules/cjs/loader:*)
at Module.require (node:internal/modules/cjs/loader:*)
16 changes: 16 additions & 0 deletions test/parallel/test-process-setsourcemapsenabled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';
require('../common');
const assert = require('assert');

const unexpectedValues = [
undefined,
null,
1,
{},
() => {},
];
for (const it of unexpectedValues) {
assert.throws(() => {
process.setSourceMapsEnabled(it);
}, /ERR_INVALID_ARG_TYPE/);
}

0 comments on commit cdf7251

Please sign in to comment.