@cspotcode/source-map-support
Advanced tools
Comparing version 0.6.1 to 0.7.0
{ | ||
"name": "@cspotcode/source-map-support", | ||
"description": "Fixes stack traces for files with source maps", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"main": "./source-map-support.js", | ||
@@ -15,2 +15,4 @@ "types": "./source-map-support.d.ts", | ||
"/register.js", | ||
"/register-hook-require.d.ts", | ||
"/register-hook-require.js", | ||
"/source-map-support.d.ts", | ||
@@ -40,3 +42,7 @@ "/source-map-support.js" | ||
"node": ">=12" | ||
}, | ||
"volta": { | ||
"node": "16.11.0", | ||
"npm": "7.24.2" | ||
} | ||
} |
@@ -33,2 +33,4 @@ # Source Map Support | ||
node -r @cspotcode/source-map-support/register compiled.js | ||
# Or to enable hookRequire | ||
node -r @cspotcode/source-map-support/register-hook-require compiled.js | ||
``` | ||
@@ -35,0 +37,0 @@ |
// tslint:disable:no-useless-files | ||
// For following usage: | ||
// import 'source-map-support/register' | ||
// import '@cspotcode/source-map-support/register' | ||
// Instead of: | ||
// import sourceMapSupport from 'source-map-support' | ||
// import sourceMapSupport from '@cspotcode/source-map-support' | ||
// sourceMapSupport.install() |
@@ -34,2 +34,13 @@ // Type definitions for source-map-support 0.5 | ||
retrieveSourceMap?(source: string): UrlAndMap | null; | ||
/** | ||
* Set false to disable redirection of require / import `source-map-support` to `@cspotcode/source-map-support` | ||
*/ | ||
redirectConflictingLibrary?: boolean; | ||
/** | ||
* Callback will be called every time we redirect due to `redirectConflictingLibrary` | ||
* This allows consumers to log helpful warnings if they choose. | ||
* @param parent NodeJS.Module which made the require() or require.resolve() call | ||
* @param options options object internally passed to node's `_resolveFilename` hook | ||
*/ | ||
onConflictingLibraryRedirect?: (request: string, parent: any, isMain: boolean, options: any, redirectedRequest: string) => void; | ||
} | ||
@@ -54,1 +65,6 @@ | ||
export function install(options?: Options): void; | ||
/** | ||
* Uninstall SourceMap support. | ||
*/ | ||
export function uninstall(): void; |
@@ -26,25 +26,93 @@ var SourceMapConsumer = require('@cspotcode/source-map-consumer').SourceMapConsumer; | ||
// Only install once if called multiple times | ||
var errorFormatterInstalled = false; | ||
var uncaughtShimInstalled = false; | ||
/** | ||
* @typedef {{ | ||
* enabled: boolean; | ||
* originalValue: any; | ||
* installedValue: any; | ||
* }} HookState | ||
* Used for installing and uninstalling hooks | ||
*/ | ||
// If true, the caches are reset before a stack trace formatting operation | ||
var emptyCacheBetweenOperations = false; | ||
// Increment this if the format of sharedData changes in a breaking way. | ||
var sharedDataVersion = 1; | ||
/** | ||
* @template T | ||
* @param {T} defaults | ||
* @returns {T} | ||
*/ | ||
function initializeSharedData(defaults) { | ||
var sharedDataKey = 'source-map-support/sharedData'; | ||
if (typeof Symbol !== 'undefined') { | ||
sharedDataKey = Symbol.for(sharedDataKey); | ||
} | ||
var sharedData = this[sharedDataKey]; | ||
if (!sharedData) { | ||
sharedData = { version: sharedDataVersion }; | ||
if (Object.defineProperty) { | ||
Object.defineProperty(this, sharedDataKey, { value: sharedData }); | ||
} else { | ||
this[sharedDataKey] = sharedData; | ||
} | ||
} | ||
if (sharedDataVersion !== sharedData.version) { | ||
throw new Error("Multiple incompatible instances of source-map-support were loaded"); | ||
} | ||
for (var key in defaults) { | ||
if (!(key in sharedData)) { | ||
sharedData[key] = defaults[key]; | ||
} | ||
} | ||
return sharedData; | ||
} | ||
// If multiple instances of source-map-support are loaded into the same | ||
// context, they shouldn't overwrite each other. By storing handlers, caches, | ||
// and other state on a shared object, different instances of | ||
// source-map-support can work together in a limited way. This does require | ||
// that future versions of source-map-support continue to support the fields on | ||
// this object. If this internal contract ever needs to be broken, increment | ||
// sharedDataVersion. (This version number is not the same as any of the | ||
// package's version numbers, which should reflect the *external* API of | ||
// source-map-support.) | ||
var sharedData = initializeSharedData({ | ||
// Only install once if called multiple times | ||
// Remember how the environment looked before installation so we can restore if able | ||
/** @type {HookState} */ | ||
errorPrepareStackTraceHook: undefined, | ||
/** @type {HookState} */ | ||
processEmitHook: undefined, | ||
/** @type {HookState} */ | ||
moduleResolveFilenameHook: undefined, | ||
/** @type {Array<(request: string, parent: any, isMain: boolean, options: any, redirectedRequest: string) => void>} */ | ||
onConflictingLibraryRedirectArr: [], | ||
// If true, the caches are reset before a stack trace formatting operation | ||
emptyCacheBetweenOperations: false, | ||
// Maps a file path to a string containing the file contents | ||
fileContentsCache: {}, | ||
// Maps a file path to a source map for that file | ||
sourceMapCache: {}, | ||
// Priority list of retrieve handlers | ||
retrieveFileHandlers: [], | ||
retrieveMapHandlers: [], | ||
// Priority list of internally-implemented handlers. | ||
// When resetting state, we must keep these. | ||
internalRetrieveFileHandlers: [], | ||
internalRetrieveMapHandlers: [], | ||
}); | ||
// Supports {browser, node, auto} | ||
var environment = "auto"; | ||
// Maps a file path to a string containing the file contents | ||
var fileContentsCache = {}; | ||
// Maps a file path to a source map for that file | ||
var sourceMapCache = {}; | ||
// Regex for detecting source maps | ||
var reSourceMap = /^data:application\/json[^,]+base64,/; | ||
// Priority list of retrieve handlers | ||
var retrieveFileHandlers = []; | ||
var retrieveMapHandlers = []; | ||
function isInBrowser() { | ||
@@ -62,3 +130,3 @@ if (environment === "browser") | ||
function handlerExec(list) { | ||
function handlerExec(list, internalList) { | ||
return function(arg) { | ||
@@ -71,2 +139,8 @@ for (var i = 0; i < list.length; i++) { | ||
} | ||
for (var i = 0; i < internalList.length; i++) { | ||
var ret = internalList[i](arg); | ||
if (ret) { | ||
return ret; | ||
} | ||
} | ||
return null; | ||
@@ -76,5 +150,5 @@ }; | ||
var retrieveFile = handlerExec(retrieveFileHandlers); | ||
var retrieveFile = handlerExec(sharedData.retrieveFileHandlers, sharedData.internalRetrieveFileHandlers); | ||
retrieveFileHandlers.push(function(path) { | ||
sharedData.internalRetrieveFileHandlers.push(function(path) { | ||
// Trim the path to make sure there is no extra whitespace. | ||
@@ -90,4 +164,4 @@ path = path.trim(); | ||
} | ||
if (path in fileContentsCache) { | ||
return fileContentsCache[path]; | ||
if (path in sharedData.fileContentsCache) { | ||
return sharedData.fileContentsCache[path]; | ||
} | ||
@@ -113,3 +187,3 @@ | ||
return fileContentsCache[path] = contents; | ||
return sharedData.fileContentsCache[path] = contents; | ||
}); | ||
@@ -169,4 +243,4 @@ | ||
// constructor). | ||
var retrieveSourceMap = handlerExec(retrieveMapHandlers); | ||
retrieveMapHandlers.push(function(source) { | ||
var retrieveSourceMap = handlerExec(sharedData.retrieveMapHandlers, sharedData.internalRetrieveMapHandlers); | ||
sharedData.internalRetrieveMapHandlers.push(function(source) { | ||
var sourceMappingURL = retrieveSourceMapURL(source); | ||
@@ -199,3 +273,3 @@ if (!sourceMappingURL) return null; | ||
function mapSourcePosition(position) { | ||
var sourceMap = sourceMapCache[position.source]; | ||
var sourceMap = sharedData.sourceMapCache[position.source]; | ||
if (!sourceMap) { | ||
@@ -205,3 +279,3 @@ // Call the (overrideable) retrieveSourceMap function to get the source map. | ||
if (urlAndMap) { | ||
sourceMap = sourceMapCache[position.source] = { | ||
sourceMap = sharedData.sourceMapCache[position.source] = { | ||
url: urlAndMap.url, | ||
@@ -218,3 +292,3 @@ map: new SourceMapConsumer(urlAndMap.map) | ||
var url = supportRelativeURL(sourceMap.url, source); | ||
fileContentsCache[url] = contents; | ||
sharedData.fileContentsCache[url] = contents; | ||
} | ||
@@ -224,3 +298,3 @@ }); | ||
} else { | ||
sourceMap = sourceMapCache[position.source] = { | ||
sourceMap = sharedData.sourceMapCache[position.source] = { | ||
url: null, | ||
@@ -445,34 +519,41 @@ map: null | ||
// This function is part of the V8 stack trace API, for more info see: | ||
// https://v8.dev/docs/stack-trace-api | ||
function prepareStackTrace(error, stack) { | ||
if (emptyCacheBetweenOperations) { | ||
fileContentsCache = {}; | ||
sourceMapCache = {}; | ||
} | ||
/** @param {HookState} hookState */ | ||
function createPrepareStackTrace(hookState) { | ||
return prepareStackTrace; | ||
// node gives its own errors special treatment. Mimic that behavior | ||
// https://github.com/nodejs/node/blob/3cbaabc4622df1b4009b9d026a1a970bdbae6e89/lib/internal/errors.js#L118-L128 | ||
// https://github.com/nodejs/node/pull/39182 | ||
var errorString; | ||
if (kIsNodeError) { | ||
if(kIsNodeError in error) { | ||
errorString = `${error.name} [${error.code}]: ${error.message}`; | ||
// This function is part of the V8 stack trace API, for more info see: | ||
// https://v8.dev/docs/stack-trace-api | ||
function prepareStackTrace(error, stack) { | ||
if(!hookState.enabled) return hookState.originalValue.apply(this, arguments); | ||
if (sharedData.emptyCacheBetweenOperations) { | ||
sharedData.fileContentsCache = {}; | ||
sharedData.sourceMapCache = {}; | ||
} | ||
// node gives its own errors special treatment. Mimic that behavior | ||
// https://github.com/nodejs/node/blob/3cbaabc4622df1b4009b9d026a1a970bdbae6e89/lib/internal/errors.js#L118-L128 | ||
// https://github.com/nodejs/node/pull/39182 | ||
var errorString; | ||
if (kIsNodeError) { | ||
if(kIsNodeError in error) { | ||
errorString = `${error.name} [${error.code}]: ${error.message}`; | ||
} else { | ||
errorString = ErrorPrototypeToString(error); | ||
} | ||
} else { | ||
errorString = ErrorPrototypeToString(error); | ||
var name = error.name || 'Error'; | ||
var message = error.message || ''; | ||
errorString = name + ": " + message; | ||
} | ||
} else { | ||
var name = error.name || 'Error'; | ||
var message = error.message || ''; | ||
errorString = name + ": " + message; | ||
} | ||
var state = { nextPosition: null, curPosition: null }; | ||
var processedStack = []; | ||
for (var i = stack.length - 1; i >= 0; i--) { | ||
processedStack.push('\n at ' + wrapCallSite(stack[i], state)); | ||
state.nextPosition = state.curPosition; | ||
var state = { nextPosition: null, curPosition: null }; | ||
var processedStack = []; | ||
for (var i = stack.length - 1; i >= 0; i--) { | ||
processedStack.push('\n at ' + wrapCallSite(stack[i], state)); | ||
state.nextPosition = state.curPosition; | ||
} | ||
state.curPosition = state.nextPosition = null; | ||
return errorString + processedStack.reverse().join(''); | ||
} | ||
state.curPosition = state.nextPosition = null; | ||
return errorString + processedStack.reverse().join(''); | ||
} | ||
@@ -489,3 +570,3 @@ | ||
// Support the inline sourceContents inside the source map | ||
var contents = fileContentsCache[source]; | ||
var contents = sharedData.fileContentsCache[source]; | ||
@@ -535,16 +616,23 @@ // Support files on disk | ||
function shimEmitUncaughtException () { | ||
var origEmit = process.emit; | ||
const originalValue = process.emit; | ||
var hook = sharedData.processEmitHook = { | ||
enabled: true, | ||
originalValue, | ||
installedValue: undefined | ||
}; | ||
var isTerminatingDueToFatalException = false; | ||
var fatalException; | ||
process.emit = function (type) { | ||
const hadListeners = origEmit.apply(this, arguments); | ||
if (type === 'uncaughtException' && !hadListeners) { | ||
isTerminatingDueToFatalException = true; | ||
fatalException = arguments[1]; | ||
process.exit(1); | ||
process.emit = sharedData.processEmitHook.installedValue = function (type) { | ||
const hadListeners = originalValue.apply(this, arguments); | ||
if(hook.enabled) { | ||
if (type === 'uncaughtException' && !hadListeners) { | ||
isTerminatingDueToFatalException = true; | ||
fatalException = arguments[1]; | ||
process.exit(1); | ||
} | ||
if (type === 'exit' && isTerminatingDueToFatalException) { | ||
printFatalErrorUponExit(fatalException); | ||
} | ||
} | ||
if (type === 'exit' && isTerminatingDueToFatalException) { | ||
printFatalErrorUponExit(fatalException); | ||
} | ||
return hadListeners; | ||
@@ -554,4 +642,4 @@ }; | ||
var originalRetrieveFileHandlers = retrieveFileHandlers.slice(0); | ||
var originalRetrieveMapHandlers = retrieveMapHandlers.slice(0); | ||
var originalRetrieveFileHandlers = sharedData.retrieveFileHandlers.slice(0); | ||
var originalRetrieveMapHandlers = sharedData.retrieveMapHandlers.slice(0); | ||
@@ -573,2 +661,43 @@ exports.wrapCallSite = wrapCallSite; | ||
// Use dynamicRequire to avoid including in browser bundles | ||
var Module = dynamicRequire(module, 'module'); | ||
// Redirect subsequent imports of "source-map-support" | ||
// to this package | ||
const {redirectConflictingLibrary = true, onConflictingLibraryRedirect} = options; | ||
if(redirectConflictingLibrary) { | ||
if (!sharedData.moduleResolveFilenameHook) { | ||
const originalValue = Module._resolveFilename; | ||
const moduleResolveFilenameHook = sharedData.moduleResolveFilenameHook = { | ||
enabled: true, | ||
originalValue, | ||
installedValue: undefined, | ||
} | ||
Module._resolveFilename = sharedData.moduleResolveFilenameHook.installedValue = function (request, parent, isMain, options) { | ||
if (moduleResolveFilenameHook.enabled) { | ||
// Match all source-map-support entrypoints: source-map-support, source-map-support/register | ||
let requestRedirect; | ||
if (request === 'source-map-support') { | ||
requestRedirect = './'; | ||
} else if (request === 'source-map-support/register') { | ||
requestRedirect = './register'; | ||
} | ||
if (requestRedirect !== undefined) { | ||
const newRequest = require.resolve(requestRedirect); | ||
for (const cb of sharedData.onConflictingLibraryRedirectArr) { | ||
cb(request, parent, isMain, options, newRequest); | ||
} | ||
request = newRequest; | ||
} | ||
} | ||
return originalValue.call(this, request, parent, isMain, options); | ||
} | ||
} | ||
if (onConflictingLibraryRedirect) { | ||
sharedData.onConflictingLibraryRedirectArr.push(onConflictingLibraryRedirect); | ||
} | ||
} | ||
// Allow sources to be found by methods other than reading the files | ||
@@ -578,6 +707,6 @@ // directly from disk. | ||
if (options.overrideRetrieveFile) { | ||
retrieveFileHandlers.length = 0; | ||
sharedData.retrieveFileHandlers.length = 0; | ||
} | ||
retrieveFileHandlers.unshift(options.retrieveFile); | ||
sharedData.retrieveFileHandlers.unshift(options.retrieveFile); | ||
} | ||
@@ -589,6 +718,6 @@ | ||
if (options.overrideRetrieveSourceMap) { | ||
retrieveMapHandlers.length = 0; | ||
sharedData.retrieveMapHandlers.length = 0; | ||
} | ||
retrieveMapHandlers.unshift(options.retrieveSourceMap); | ||
sharedData.retrieveMapHandlers.unshift(options.retrieveSourceMap); | ||
} | ||
@@ -598,4 +727,2 @@ | ||
if (options.hookRequire && !isInBrowser()) { | ||
// Use dynamicRequire to avoid including in browser bundles | ||
var Module = dynamicRequire(module, 'module'); | ||
var $compile = Module.prototype._compile; | ||
@@ -605,4 +732,4 @@ | ||
Module.prototype._compile = function(content, filename) { | ||
fileContentsCache[filename] = content; | ||
sourceMapCache[filename] = undefined; | ||
sharedData.fileContentsCache[filename] = content; | ||
sharedData.sourceMapCache[filename] = undefined; | ||
return $compile.call(this, content, filename); | ||
@@ -616,14 +743,20 @@ }; | ||
// Configure options | ||
if (!emptyCacheBetweenOperations) { | ||
emptyCacheBetweenOperations = 'emptyCacheBetweenOperations' in options ? | ||
if (!sharedData.emptyCacheBetweenOperations) { | ||
sharedData.emptyCacheBetweenOperations = 'emptyCacheBetweenOperations' in options ? | ||
options.emptyCacheBetweenOperations : false; | ||
} | ||
// Install the error reformatter | ||
if (!errorFormatterInstalled) { | ||
errorFormatterInstalled = true; | ||
Error.prepareStackTrace = prepareStackTrace; | ||
if (!sharedData.errorPrepareStackTraceHook) { | ||
const originalValue = Error.prepareStackTrace; | ||
sharedData.errorPrepareStackTraceHook = { | ||
enabled: true, | ||
originalValue, | ||
installedValue: undefined | ||
}; | ||
Error.prepareStackTrace = sharedData.errorPrepareStackTraceHook.installedValue = createPrepareStackTrace(sharedData.errorPrepareStackTraceHook); | ||
} | ||
if (!uncaughtShimInstalled) { | ||
if (!sharedData.processEmitHook) { | ||
var installHandler = 'handleUncaughtExceptions' in options ? | ||
@@ -651,3 +784,2 @@ options.handleUncaughtExceptions : true; | ||
if (installHandler && hasGlobalProcessEventEmitter()) { | ||
uncaughtShimInstalled = true; | ||
shimEmitUncaughtException(); | ||
@@ -658,11 +790,40 @@ } | ||
exports.uninstall = function() { | ||
if(sharedData.processEmitHook) { | ||
// Disable behavior | ||
sharedData.processEmitHook.enabled = false; | ||
// If possible, remove our hook function. May not be possible if subsequent third-party hooks have wrapped around us. | ||
if(process.emit === sharedData.processEmitHook.installedValue) { | ||
process.emit = sharedData.processEmitHook.originalValue; | ||
} | ||
sharedData.processEmitHook = undefined; | ||
} | ||
if(sharedData.errorPrepareStackTraceHook) { | ||
// Disable behavior | ||
sharedData.errorPrepareStackTraceHook.enabled = false; | ||
// If possible or necessary, remove our hook function. | ||
// In vanilla environments, prepareStackTrace is `undefined`. | ||
// We cannot delegate to `undefined` the way we can to a function w/`.apply()`; our only option is to remove the function. | ||
// If we are the *first* hook installed, and another was installed on top of us, we have no choice but to remove both. | ||
if(Error.prepareStackTrace === sharedData.errorPrepareStackTraceHook.installedValue || typeof sharedData.errorPrepareStackTraceHook.originalValue !== 'function') { | ||
Error.prepareStackTrace = sharedData.errorPrepareStackTraceHook.originalValue; | ||
} | ||
sharedData.errorPrepareStackTraceHook = undefined; | ||
} | ||
if (sharedData.moduleResolveFilenameHook) { | ||
// Disable behavior | ||
sharedData.moduleResolveFilenameHook.enabled = false; | ||
// If possible, remove our hook function. May not be possible if subsequent third-party hooks have wrapped around us. | ||
var Module = dynamicRequire(module, 'module'); | ||
if(Module._resolveFilename === sharedData.moduleResolveFilenameHook.installedValue) { | ||
Module._resolveFilename = sharedData.moduleResolveFilenameHook.originalValue; | ||
} | ||
sharedData.moduleResolveFilenameHook = undefined; | ||
} | ||
sharedData.onConflictingLibraryRedirectArr.length = 0; | ||
} | ||
exports.resetRetrieveHandlers = function() { | ||
retrieveFileHandlers.length = 0; | ||
retrieveMapHandlers.length = 0; | ||
retrieveFileHandlers = originalRetrieveFileHandlers.slice(0); | ||
retrieveMapHandlers = originalRetrieveMapHandlers.slice(0); | ||
retrieveSourceMap = handlerExec(retrieveMapHandlers); | ||
retrieveFile = handlerExec(retrieveFileHandlers); | ||
sharedData.retrieveFileHandlers.length = 0; | ||
sharedData.retrieveMapHandlers.length = 0; | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
43488
9
793
290
2