From 766d0ef0217b3a3b78541dc6ac8c2ffa90e12d76 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Thu, 24 Jul 2025 20:31:12 +0100 Subject: [PATCH] Add support for source maps Use the new getCallSites API, which is both a bit simpler to use than V8 one, but also integrates correctly with Node.js source maps support. For a demo, I renamed example.js to example.ts) and used [tsx](https://github.com/privatenumber/tsx) to run it. Before: ``` > npx tsx --enable-source-maps example.ts There are 4 handle(s) keeping the process running. # Timeout example.ts:1 - import whyIsNodeRunning from 'why-is-node-running' // should be your first import example.ts:1 - import whyIsNodeRunning from 'why-is-node-running' // should be your first import # TCPSERVERWRAP example.ts:1 - import whyIsNodeRunning from 'why-is-node-running' // should be your first import example.ts:1 - import whyIsNodeRunning from 'why-is-node-running' // should be your first import # Timeout example.ts:1 - import whyIsNodeRunning from 'why-is-node-running' // should be your first import example.ts:1 - import whyIsNodeRunning from 'why-is-node-running' // should be your first import # TCPSERVERWRAP example.ts:1 - import whyIsNodeRunning from 'why-is-node-running' // should be your first import example.ts:1 - import whyIsNodeRunning from 'why-is-node-running' // should be your first import ``` After: ``` > npx tsx --enable-source-maps example.ts There are 4 handle(s) keeping the process running. # Timeout example.ts:6 - setInterval(() => {}, 1000) example.ts:10 - startServer() # TCPSERVERWRAP example.ts:7 - server.listen(0) example.ts:10 - startServer() # Timeout example.ts:6 - setInterval(() => {}, 1000) example.ts:11 - startServer() # TCPSERVERWRAP example.ts:7 - server.listen(0) example.ts:11 - startServer() ``` --- index.js | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/index.js b/index.js index 9189e97..9080f63 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ import { createHook } from 'node:async_hooks' import { readFileSync } from 'node:fs' import { relative } from 'node:path' import { fileURLToPath } from 'node:url' +import { getCallSites } from 'node:util' const IGNORED_TYPES = [ 'TIMERWRAP', @@ -17,7 +18,7 @@ const hook = createHook({ return } - const stacks = captureStackTraces().slice(1) + const stacks = getCallSites().slice(1) asyncResources.set(asyncId, { type, @@ -42,7 +43,7 @@ export default function whyIsNodeRunning (logger = console) { if (resource === undefined) { return false } - + return resource.hasRef?.() ?? true }) @@ -55,7 +56,7 @@ export default function whyIsNodeRunning (logger = console) { function printStacks (asyncResource, logger) { const stacks = asyncResource.stacks.filter((stack) => { - const fileName = stack.fileName + const fileName = stack.scriptName return fileName !== null && !fileName.startsWith('node:') }) @@ -72,9 +73,9 @@ function printStacks (asyncResource, logger) { for (const stack of stacks) { const location = formatLocation(stack) const padding = ' '.repeat(maxLength - location.length) - + try { - const lines = readFileSync(normalizeFilePath(stack.fileName), 'utf-8').split(/\n|\r\n/) + const lines = readFileSync(normalizeFilePath(stack.scriptName), 'utf-8').split(/\n|\r\n/) const line = lines[stack.lineNumber - 1].trim() logger.error(`${location}${padding} - ${line}`) @@ -85,7 +86,7 @@ function printStacks (asyncResource, logger) { } function formatLocation (stack) { - const filePath = formatFilePath(stack.fileName) + const filePath = formatFilePath(stack.scriptName) return `${filePath}:${stack.lineNumber}` } @@ -99,24 +100,3 @@ function formatFilePath (filePath) { function normalizeFilePath (filePath) { return filePath.startsWith('file://') ? fileURLToPath(filePath) : filePath } - -function prepareStackTrace(error, stackTraces) { - return stackTraces.map(stack => ({ - lineNumber: stack.getLineNumber(), - fileName: stack.getFileName() - })) -} - -// See: https://v8.dev/docs/stack-trace-api -function captureStackTraces () { - const target = {} - const original = Error.prepareStackTrace - - Error.prepareStackTrace = prepareStackTrace - Error.captureStackTrace(target, captureStackTraces) - - const capturedTraces = target.stack - Error.prepareStackTrace = original - - return capturedTraces -}