Skip to content

Commit

Permalink
lib: 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.
  • Loading branch information
legendecas committed Jun 19, 2021
1 parent e4eadb2 commit 6400009
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 16 deletions.
17 changes: 17 additions & 0 deletions doc/api/process.md
Expand Up @@ -2379,6 +2379,22 @@ 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
-->

* `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 @@ -2763,6 +2779,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
6 changes: 6 additions & 0 deletions lib/internal/bootstrap/pre_execution.js
Expand Up @@ -65,6 +65,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
// (including preload modules).
initializeClusterIPC();

initializeSourceMapsHandlers();
initializeDeprecations();
initializeWASI();
initializeCJSLoader();
Expand Down Expand Up @@ -448,6 +449,11 @@ 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
36 changes: 24 additions & 12 deletions lib/internal/source_map/source_map_cache.js
Expand Up @@ -41,22 +41,33 @@ 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) {
const {
setSourceMapsEnabled,
setPrepareStackTraceCallback
} = internalBinding('errors');
setSourceMapsEnabled(val);
if (val) {
const {
prepareStackTrace
} = require('internal/source_map/prepare_stack_trace');
setPrepareStackTraceCallback(prepareStackTrace);
} else if (sourceMapsEnabled !== undefined) {
// Set 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 @@ -239,6 +250,7 @@ function findSourceMap(sourceURL) {
module.exports = {
findSourceMap,
getSourceMapsEnabled,
setSourceMapsEnabled,
maybeCacheSourceMap,
rekeySourceMap,
sourceMapCacheToObject,
Expand Down
9 changes: 5 additions & 4 deletions src/node_errors.cc
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>()->BooleanValue(env->isolate()));
}

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
19 changes: 19 additions & 0 deletions test/message/source_map_disabled_by_api.js
@@ -0,0 +1,19 @@
// 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');
30 changes: 30 additions & 0 deletions test/message/source_map_disabled_by_api.out
@@ -0,0 +1,30 @@
*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:*)
*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:*)
16 changes: 16 additions & 0 deletions test/message/source_map_enabled_by_api.js
@@ -0,0 +1,16 @@
'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
@@ -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:*)

0 comments on commit 6400009

Please sign in to comment.