From c1bc9e4016c7d7f79b796d14948999375df85162 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Sat, 2 Nov 2024 12:19:49 +0100 Subject: [PATCH] util: add sourcemap support to getCallSite --- doc/api/module.md | 2 +- doc/api/util.md | 5 +- lib/util.js | 18 +++++- .../ts/test-get-callsite-explicit.ts | 10 ++++ .../typescript/ts/test-get-callsite.ts | 10 ++++ test/parallel/test-util-getCallSite.js | 60 ++++++++++++++++++- 6 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/typescript/ts/test-get-callsite-explicit.ts create mode 100644 test/fixtures/typescript/ts/test-get-callsite.ts diff --git a/doc/api/module.md b/doc/api/module.md index a7688b56ea62f0..6ffed8ece19edc 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -1428,7 +1428,7 @@ returned object contains the following keys: * columnNumber: {number} The 1-indexed columnNumber of the corresponding call site in the original source -[CallSite]: util.md#utilgetcallsiteframes +[CallSite]: util.md#utilgetcallsiteframes-options [CommonJS]: modules.md [Conditional exports]: packages.md#conditional-exports [Customization hooks]: #customization-hooks diff --git a/doc/api/util.md b/doc/api/util.md index 12527b794d0948..6e40919871c764 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -364,7 +364,7 @@ util.formatWithOptions({ colors: true }, 'See object %O', { foo: 42 }); // when printed to a terminal. ``` -## `util.getCallSite(frames)` +## `util.getCallSite(frames, options)` > Stability: 1.1 - Active development @@ -374,6 +374,9 @@ added: v22.9.0 * `frames` {number} Number of frames returned in the stacktrace. **Default:** `10`. Allowable range is between 1 and 200. +* `options` {Object} + * `sourceMap` {boolean} Reconstruct the original location in the stacktrace from the source-map. + Enabled by default with the flag `--enable-source-maps`. * Returns: {Object\[]} An array of stacktrace objects * `functionName` {string} Returns the name of the function associated with this stack frame. * `scriptName` {string} Returns the name of the resource that contains the script for the diff --git a/lib/util.js b/lib/util.js index 6034e1af3e56d1..d76e8de2a27045 100644 --- a/lib/util.js +++ b/lib/util.js @@ -61,6 +61,7 @@ const { validateNumber, validateString, validateOneOf, + validateObject, } = require('internal/validators'); const { isReadableStream, @@ -74,6 +75,7 @@ function lazyUtilColors() { utilColors ??= require('internal/util/colors'); return utilColors; } +const { getOptionValue } = require('internal/options'); const binding = internalBinding('util'); @@ -333,9 +335,23 @@ function parseEnv(content) { * @param {number} frames * @returns {object} */ -function getCallSite(frames = 10) { +function getCallSite(frames = 10, options) { + if (options === undefined) { + if (typeof frames === 'object') { + options = frames; + frames = 10; + } else { + options = {}; + }; + } // Using kDefaultMaxCallStackSizeToCapture as reference validateNumber(frames, 'frames', 1, 200); + validateObject(options, 'options'); + // If options.sourceMaps is true or if sourceMaps are enabled but the option.sourceMaps is not set explictly to false + if (options.sourceMap === true || (getOptionValue('--enable-source-maps') && options.sourceMap !== false)) { + const { mapCallSite } = require('internal/source_map/source_map_cache'); + return mapCallSite(binding.getCallSite(frames)); + } return binding.getCallSite(frames); }; diff --git a/test/fixtures/typescript/ts/test-get-callsite-explicit.ts b/test/fixtures/typescript/ts/test-get-callsite-explicit.ts new file mode 100644 index 00000000000000..e0e0f6383d5453 --- /dev/null +++ b/test/fixtures/typescript/ts/test-get-callsite-explicit.ts @@ -0,0 +1,10 @@ +const { getCallSite } = require('node:util'); + +interface CallSite { + A; + B; +} + +const callSite = getCallSite({ sourceMap: false })[0]; + +console.log('mapCallSite: ', callSite); diff --git a/test/fixtures/typescript/ts/test-get-callsite.ts b/test/fixtures/typescript/ts/test-get-callsite.ts new file mode 100644 index 00000000000000..7ac04eb0575299 --- /dev/null +++ b/test/fixtures/typescript/ts/test-get-callsite.ts @@ -0,0 +1,10 @@ +const { getCallSite } = require('node:util'); + +interface CallSite { + A; + B; +} + +const callSite = getCallSite()[0]; + +console.log('getCallSite: ', callSite); diff --git a/test/parallel/test-util-getCallSite.js b/test/parallel/test-util-getCallSite.js index ae862e2b278401..e66f5c43d59ad3 100644 --- a/test/parallel/test-util-getCallSite.js +++ b/test/parallel/test-util-getCallSite.js @@ -53,7 +53,17 @@ const assert = require('node:assert'); code: 'ERR_OUT_OF_RANGE' })); assert.throws(() => { - getCallSite({}); + getCallSite([]); + }, common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE' + })); + assert.throws(() => { + getCallSite({}, {}); + }, common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE' + })); + assert.throws(() => { + getCallSite(10, 10); }, common.expectsError({ code: 'ERR_INVALID_ARG_TYPE' })); @@ -104,3 +114,51 @@ const assert = require('node:assert'); assert.notStrictEqual(callsite.length, 0); Error.stackTraceLimit = originalStackTraceLimit; } + +{ + const { status, stderr, stdout } = spawnSync(process.execPath, [ + '--no-warnings', + '--experimental-transform-types', + fixtures.path('typescript/ts/test-get-callsite.ts'), + ]); + + const output = stdout.toString(); + assert.strictEqual(stderr.toString(), ''); + assert.match(output, /lineNumber: 8/); + assert.match(output, /column: 18/); + assert.match(output, /typescript\/ts\/test-get-callsite\.ts/); + assert.strictEqual(status, 0); +} + +{ + const { status, stderr, stdout } = spawnSync(process.execPath, [ + '--no-warnings', + '--experimental-transform-types', + '--no-enable-source-maps', + fixtures.path('typescript/ts/test-get-callsite.ts'), + ]); + + const output = stdout.toString(); + assert.strictEqual(stderr.toString(), ''); + // Line should be wrong when sourcemaps are disable + assert.match(output, /lineNumber: 2/); + assert.match(output, /column: 18/); + assert.match(output, /typescript\/ts\/test-get-callsite\.ts/); + assert.strictEqual(status, 0); +} + +{ + // Source maps should be disabled when options.sourceMap is false + const { status, stderr, stdout } = spawnSync(process.execPath, [ + '--no-warnings', + '--experimental-transform-types', + fixtures.path('typescript/ts/test-get-callsite-explicit.ts'), + ]); + + const output = stdout.toString(); + assert.strictEqual(stderr.toString(), ''); + assert.match(output, /lineNumber: 2/); + assert.match(output, /column: 18/); + assert.match(output, /typescript\/ts\/test-get-callsite-explicit\.ts/); + assert.strictEqual(status, 0); +}