| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 359fff1 commit bcfe9c8
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -364,7 +364,7 @@ util.formatWithOptions({ colors: true }, 'See object %O', { foo: 42 }); | |||
| 364 | 364 | // when printed to a terminal. | |
| 365 | 365 | ``` | |
| 366 | 366 | ||
| 367 | - ## `util.getCallSites(frameCount)` | ||
| 367 | + ## `util.getCallSites(frameCountOrOptions, [options])` | ||
| 368 | 368 | ||
| 369 | 369 | > Stability: 1.1 - Active development | |
| 370 | 370 | ||
@@ -376,8 +376,11 @@ changes: | |||
| 376 | 376 | description: The API is renamed from `util.getCallSite` to `util.getCallSites()`. | |
| 377 | 377 | --> | |
| 378 | 378 | ||
| 379 | - * `frameCount` {number} Number of frames to capture as call site objects. | ||
| 379 | + * `frameCount` {number} Optional number of frames to capture as call site objects. | ||
| 380 | 380 | **Default:** `10`. Allowable range is between 1 and 200. | |
| 381 | + * `options` {Object} Optional | ||
| 382 | + * `sourceMap` {boolean} Reconstruct the original location in the stacktrace from the source-map. | ||
| 383 | + Enabled by default with the flag `--enable-source-maps`. | ||
| 381 | 384 | * Returns: {Object\[]} An array of call site objects | |
| 382 | 385 | * `functionName` {string} Returns the name of the function associated with this call site. | |
| 383 | 386 | * `scriptName` {string} Returns the name of the resource that contains the script for the | |
@@ -425,6 +428,33 @@ function anotherFunction() { | |||
| 425 | 428 | anotherFunction(); | |
| 426 | 429 | ``` | |
| 427 | 430 | ||
| 431 | + It is possible to reconstruct the original locations by setting the option `sourceMap` to `true`. | ||
| 432 | + If the source map is not available, the original location will be the same as the current location. | ||
| 433 | + When the `--enable-source-maps` flag is enabled, for example when using `--experimental-transform-types`, | ||
| 434 | + `sourceMap` will be true by default. | ||
| 435 | + | ||
| 436 | + ```ts | ||
| 437 | + import util from 'node:util'; | ||
| 438 | + | ||
| 439 | + interface Foo { | ||
| 440 | + foo: string; | ||
| 441 | + } | ||
| 442 | + | ||
| 443 | + const callSites = util.getCallSites({ sourceMap: true }); | ||
| 444 | + | ||
| 445 | + // With sourceMap: | ||
| 446 | + // Function Name: '' | ||
| 447 | + // Script Name: example.js | ||
| 448 | + // Line Number: 7 | ||
| 449 | + // Column Number: 26 | ||
| 450 | + | ||
| 451 | + // Without sourceMap: | ||
| 452 | + // Function Name: '' | ||
| 453 | + // Script Name: example.js | ||
| 454 | + // Line Number: 2 | ||
| 455 | + // Column Number: 26 | ||
| 456 | + ``` | ||
| 457 | + | ||
| 428 | 458 | ## `util.getSystemErrorName(err)` | |
| 429 | 459 | ||
| 430 | 460 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,7 @@ const { | |||
| 25 | 25 | ArrayIsArray, | |
| 26 | 26 | ArrayPrototypeJoin, | |
| 27 | 27 | ArrayPrototypePop, | |
| 28 | + ArrayPrototypePush, | ||
| 28 | 29 | Date, | |
| 29 | 30 | DatePrototypeGetDate, | |
| 30 | 31 | DatePrototypeGetHours, | |
@@ -70,6 +71,7 @@ const { | |||
| 70 | 71 | validateNumber, | |
| 71 | 72 | validateString, | |
| 72 | 73 | validateOneOf, | |
| 74 | + validateObject, | ||
| 73 | 75 | } = require('internal/validators'); | |
| 74 | 76 | const { isBuffer } = require('buffer').Buffer; | |
| 75 | 77 | const { | |
@@ -84,11 +86,13 @@ function lazyUtilColors() { | |||
| 84 | 86 | utilColors ??= require('internal/util/colors'); | |
| 85 | 87 | return utilColors; | |
| 86 | 88 | } | |
| 89 | + const { getOptionValue } = require('internal/options'); | ||
| 87 | 90 | ||
| 88 | 91 | const binding = internalBinding('util'); | |
| 89 | 92 | ||
| 90 | 93 | const { | |
| 91 | 94 | deprecate, | |
| 95 | + getLazy, | ||
| 92 | 96 | getSystemErrorMap, | |
| 93 | 97 | getSystemErrorName: internalErrorName, | |
| 94 | 98 | getSystemErrorMessage: internalErrorMessage, | |
@@ -472,14 +476,90 @@ function parseEnv(content) { | |||
| 472 | 476 | return binding.parseEnv(content); | |
| 473 | 477 | } | |
| 474 | 478 | ||
| 479 | + const lazySourceMap = getLazy(() => require('internal/source_map/source_map_cache')); | ||
| 480 | + | ||
| 481 | + /** | ||
| 482 | + * @typedef {object} CallSite // The call site | ||
| 483 | + * @property {string} scriptName // The name of the resource that contains the | ||
| 484 | + * script for the function for this StackFrame | ||
| 485 | + * @property {string} functionName // The name of the function associated with this stack frame | ||
| 486 | + * @property {number} lineNumber // The number, 1-based, of the line for the associate function call | ||
| 487 | + * @property {number} columnNumber // The 1-based column offset on the line for the associated function call | ||
| 488 | + */ | ||
| 489 | + | ||
| 490 | + /** | ||
| 491 | + * @param {CallSite} callSite // The call site object to reconstruct from source map | ||
| 492 | + * @returns {CallSite | undefined} // The reconstructed call site object | ||
| 493 | + */ | ||
| 494 | + function reconstructCallSite(callSite) { | ||
| 495 | + const { scriptName, lineNumber, column } = callSite; | ||
| 496 | + const sourceMap = lazySourceMap().findSourceMap(scriptName); | ||
| 497 | + if (!sourceMap) return; | ||
| 498 | + const entry = sourceMap.findEntry(lineNumber - 1, column - 1); | ||
| 499 | + if (!entry?.originalSource) return; | ||
| 500 | + return { | ||
| 501 | + __proto__: null, | ||
| 502 | + // If the name is not found, it is an empty string to match the behavior of `util.getCallSite()` | ||
| 503 | + functionName: entry.name ?? '', | ||
| 504 | + scriptName: entry.originalSource, | ||
| 505 | + lineNumber: entry.originalLine + 1, | ||
| 506 | + column: entry.originalColumn + 1, | ||
| 507 | + }; | ||
| 508 | + } | ||
| 509 | + | ||
| 510 | + /** | ||
| 511 | + * | ||
| 512 | + * The call site array to map | ||
| 513 | + * @param {CallSite[]} callSites | ||
| 514 | + * Array of objects with the reconstructed call site | ||
| 515 | + * @returns {CallSite[]} | ||
| 516 | + */ | ||
| 517 | + function mapCallSite(callSites) { | ||
| 518 | + const result = []; | ||
| 519 | + for (let i = 0; i < callSites.length; ++i) { | ||
| 520 | + const callSite = callSites[i]; | ||
| 521 | + const found = reconstructCallSite(callSite); | ||
| 522 | + ArrayPrototypePush(result, found ?? callSite); | ||
| 523 | + } | ||
| 524 | + return result; | ||
| 525 | + } | ||
| 526 | + | ||
| 527 | + /** | ||
| 528 | + * @typedef {object} CallSiteOptions // The call site options | ||
| 529 | + * @property {boolean} sourceMap // Enable source map support | ||
| 530 | + */ | ||
| 531 | + | ||
| 475 | 532 | /** | |
| 476 | 533 | * Returns the callSite | |
| 477 | 534 | * @param {number} frameCount | |
| 478 | - * @returns {object} | ||
| 535 | + * @param {CallSiteOptions} options | ||
| 536 | + * @returns {CallSite[]} | ||
| 479 | 537 | */ | |
| 480 | - function getCallSites(frameCount = 10) { | ||
| 538 | + function getCallSites(frameCount = 10, options) { | ||
| 539 | + // If options is not provided check if frameCount is an object | ||
| 540 | + if (options === undefined) { | ||
| 541 | + if (typeof frameCount === 'object') { | ||
| 542 | + // If frameCount is an object, it is the options object | ||
| 543 | + options = frameCount; | ||
| 544 | + validateObject(options, 'options'); | ||
| 545 | + validateBoolean(options.sourceMap, 'options.sourceMap'); | ||
| 546 | + frameCount = 10; | ||
| 547 | + } else { | ||
| 548 | + // If options is not provided, set it to an empty object | ||
| 549 | + options = {}; | ||
| 550 | + }; | ||
| 551 | + } else { | ||
| 552 | + // If options is provided, validate it | ||
| 553 | + validateObject(options, 'options'); | ||
| 554 | + validateBoolean(options.sourceMap, 'options.sourceMap'); | ||
| 555 | + } | ||
| 556 | + | ||
| 481 | 557 | // Using kDefaultMaxCallStackSizeToCapture as reference | |
| 482 | 558 | validateNumber(frameCount, 'frameCount', 1, 200); | |
| 559 | + // If options.sourceMaps is true or if sourceMaps are enabled but the option.sourceMaps is not set explictly to false | ||
| 560 | + if (options.sourceMap === true || (getOptionValue('--enable-source-maps') && options.sourceMap !== false)) { | ||
| 561 | + return mapCallSite(binding.getCallSites(frameCount)); | ||
| 562 | + } | ||
| 483 | 563 | return binding.getCallSites(frameCount); | |
| 484 | 564 | }; | |
| 485 | 565 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,10 @@ | |||
| 1 | + const { getCallSites } = require('node:util'); | ||
| 2 | + | ||
| 3 | + interface CallSite { | ||
| 4 | + A; | ||
| 5 | + B; | ||
| 6 | + } | ||
| 7 | + | ||
| 8 | + const callSite = getCallSites({ sourceMap: false })[0]; | ||
| 9 | + | ||
| 10 | + console.log('mapCallSite: ', callSite); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,10 @@ | |||
| 1 | + const { getCallSites } = require('node:util'); | ||
| 2 | + | ||
| 3 | + interface CallSite { | ||
| 4 | + A; | ||
| 5 | + B; | ||
| 6 | + } | ||
| 7 | + | ||
| 8 | + const callSite = getCallSites()[0]; | ||
| 9 | + | ||
| 10 | + console.log('getCallSite: ', callSite); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -53,7 +53,17 @@ const assert = require('node:assert'); | |||
| 53 | 53 | code: 'ERR_OUT_OF_RANGE' | |
| 54 | 54 | })); | |
| 55 | 55 | assert.throws(() => { | |
| 56 | - getCallSites({}); | ||
| 56 | + getCallSites([]); | ||
| 57 | + }, common.expectsError({ | ||
| 58 | + code: 'ERR_INVALID_ARG_TYPE' | ||
| 59 | + })); | ||
| 60 | + assert.throws(() => { | ||
| 61 | + getCallSites({}, {}); | ||
| 62 | + }, common.expectsError({ | ||
| 63 | + code: 'ERR_INVALID_ARG_TYPE' | ||
| 64 | + })); | ||
| 65 | + assert.throws(() => { | ||
| 66 | + getCallSites(10, 10); | ||
| 57 | 67 | }, common.expectsError({ | |
| 58 | 68 | code: 'ERR_INVALID_ARG_TYPE' | |
| 59 | 69 | })); | |
@@ -104,3 +114,51 @@ const assert = require('node:assert'); | |||
| 104 | 114 | assert.notStrictEqual(callSites.length, 0); | |
| 105 | 115 | Error.stackTraceLimit = originalStackTraceLimit; | |
| 106 | 116 | } | |
| 117 | + | ||
| 118 | + { | ||
| 119 | + const { status, stderr, stdout } = spawnSync(process.execPath, [ | ||
| 120 | + '--no-warnings', | ||
| 121 | + '--experimental-transform-types', | ||
| 122 | + fixtures.path('typescript/ts/test-get-callsite.ts'), | ||
| 123 | + ]); | ||
| 124 | + | ||
| 125 | + const output = stdout.toString(); | ||
| 126 | + assert.strictEqual(stderr.toString(), ''); | ||
| 127 | + assert.match(output, /lineNumber: 8/); | ||
| 128 | + assert.match(output, /column: 18/); | ||
| 129 | + assert.match(output, /test-get-callsite\.ts/); | ||
| 130 | + assert.strictEqual(status, 0); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + { | ||
| 134 | + const { status, stderr, stdout } = spawnSync(process.execPath, [ | ||
| 135 | + '--no-warnings', | ||
| 136 | + '--experimental-transform-types', | ||
| 137 | + '--no-enable-source-maps', | ||
| 138 | + fixtures.path('typescript/ts/test-get-callsite.ts'), | ||
| 139 | + ]); | ||
| 140 | + | ||
| 141 | + const output = stdout.toString(); | ||
| 142 | + assert.strictEqual(stderr.toString(), ''); | ||
| 143 | + // Line should be wrong when sourcemaps are disable | ||
| 144 | + assert.match(output, /lineNumber: 2/); | ||
| 145 | + assert.match(output, /column: 18/); | ||
| 146 | + assert.match(output, /test-get-callsite\.ts/); | ||
| 147 | + assert.strictEqual(status, 0); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + { | ||
| 151 | + // Source maps should be disabled when options.sourceMap is false | ||
| 152 | + const { status, stderr, stdout } = spawnSync(process.execPath, [ | ||
| 153 | + '--no-warnings', | ||
| 154 | + '--experimental-transform-types', | ||
| 155 | + fixtures.path('typescript/ts/test-get-callsite-explicit.ts'), | ||
| 156 | + ]); | ||
| 157 | + | ||
| 158 | + const output = stdout.toString(); | ||
| 159 | + assert.strictEqual(stderr.toString(), ''); | ||
| 160 | + assert.match(output, /lineNumber: 2/); | ||
| 161 | + assert.match(output, /column: 18/); | ||
| 162 | + assert.match(output, /test-get-callsite-explicit\.ts/); | ||
| 163 | + assert.strictEqual(status, 0); | ||
| 164 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments