diff --git a/commons/lib/SourceLoader.ts b/commons/lib/SourceLoader.ts index 434a15e..7670b89 100644 --- a/commons/lib/SourceLoader.ts +++ b/commons/lib/SourceLoader.ts @@ -1,5 +1,5 @@ -import { fileURLToPath } from 'url'; import * as fs from 'fs'; +import { URL } from 'url'; import ISourceCodeLocation from '../interfaces/ISourceCodeLocation'; import { SourceMapSupport } from './SourceMapSupport'; @@ -20,32 +20,35 @@ export default class SourceLoader { if (!codeLocation) return null; const sourcePosition = SourceMapSupport.getOriginalSourcePosition(codeLocation, true); + console.log('sourcePosition', sourcePosition); const code = sourcePosition.content; - if (!this.sourceLines[sourcePosition.filename]) { - const file = code || this.getFileContents(sourcePosition.filename); + if (!this.sourceLines[sourcePosition.source]) { + const file = code || this.getFileContents(sourcePosition.source); + console.log('filecontens', file); if (!file) return null; - this.sourceLines[sourcePosition.filename] = file.split(/\r?\n/); + this.sourceLines[sourcePosition.source] = file.split(/\r?\n/); } (sourcePosition as any).code = - this.sourceLines[sourcePosition.filename][sourcePosition.line - 1]; + this.sourceLines[sourcePosition.source][sourcePosition.line - 1]; return sourcePosition as any; } static getFileContents(filepath: string, cache = true): string { const cacheKey = SourceMapSupport.getCacheKey(filepath); + console.log('cacche key', cacheKey); if (cache && this.fileContentsCache[cacheKey]) return this.fileContentsCache[cacheKey]; // Trim the path to make sure there is no extra whitespace. - filepath = filepath.trim(); + let lookupFilepath: string | URL = filepath.trim(); if (filepath.startsWith('file://')) { - filepath = fileURLToPath(filepath); + lookupFilepath = new URL(filepath); } let data: string = null; try { - data = fs.readFileSync(filepath, 'utf8'); + data = fs.readFileSync(lookupFilepath, 'utf8'); } catch (err) { // couldn't read } diff --git a/commons/lib/SourceMapSupport.ts b/commons/lib/SourceMapSupport.ts index 495aca6..d96b5f7 100644 --- a/commons/lib/SourceMapSupport.ts +++ b/commons/lib/SourceMapSupport.ts @@ -31,6 +31,7 @@ export class SourceMapSupport { } = {}; private static resolvedPathCache: { [file_url: string]: string } = {}; + private static cacheKeys: { [file_url: string]: string } = {}; private static stackPathsToClear = new Set(); static clearStackPath(stackPath: string): void { @@ -39,6 +40,7 @@ export class SourceMapSupport { static resetCache(): void { this.sourceMapCache = {}; + this.cacheKeys = {}; this.resolvedPathCache = {}; } @@ -77,11 +79,7 @@ export class SourceMapSupport { position: ISourceCodeLocation, includeContent = false, ): ISourceCodeLocation & { name?: string; content?: string } { - const cacheKey = this.getCacheKey(position.filename); - let sourceMap = this.sourceMapCache[cacheKey]; - if (!sourceMap) { - sourceMap = this.retrieveSourceMap(position.filename); - } + const sourceMap = this.retrieveSourceMap(position.filename); // Resolve the source URL relative to the URL of the source map if (sourceMap && sourceMap.map) { @@ -163,6 +161,10 @@ export class SourceMapSupport { rawMap: sourceMapData, }; this.sourceMapCache[cacheKey] = sourceMap; + // Overwrite trace-mapping's resolutions, because they do not handle Windows paths the way we want. + sourceMap.map.resolvedSources = sourceMap.map.sources.map(s => + supportRelativeURL(sourceMap.url, s), + ); // Load all sources stored inline with the source map into the file cache // to pretend like they are already loaded. They may not exist on disk. @@ -190,7 +192,7 @@ export class SourceMapSupport { } public static getCacheKey(pathOrFileUrl): string { - if (this.resolvedPathCache[pathOrFileUrl]) return this.resolvedPathCache[pathOrFileUrl]; + if (this.cacheKeys[pathOrFileUrl]) return this.cacheKeys[pathOrFileUrl]; let result = pathOrFileUrl.trim(); @@ -204,10 +206,30 @@ export class SourceMapSupport { } catch { // keep original url } - this.resolvedPathCache[pathOrFileUrl] = result; + this.cacheKeys[pathOrFileUrl] = result; return result; } + private static resolvePath(base: string, relative: string): string { + if (!base) return relative; + const key = `${base}__${relative}`; + + if (!this.resolvedPathCache[key]) { + let protocol = base.startsWith(fileUrlPrefix) ? fileUrlPrefix : ''; + + let basePath = path.dirname(base).slice(protocol.length); + + // handle file:///C:/ paths + if (protocol && /^\/\w:/.test(basePath)) { + protocol += '/'; + basePath = basePath.slice(1); + } + + this.resolvedPathCache[key] = protocol + path.resolve(basePath, relative); + } + return this.resolvedPathCache[key]; + } + private static prepareStackTrace(error: Error, stack: NodeJS.CallSite[]): string { // node gives its own errors special treatment. Mimic that behavior // https://github.com/nodejs/node/blob/3cbaabc4622df1b4009b9d026a1a970bdbae6e89/lib/internal/errors.js#L118-L128 @@ -363,12 +385,12 @@ function CallSiteToString(): string { return line; } +// Matches the scheme of a URL, eg "https://" +const schemeRegex = /^[\w+.-]+:\/\//; function isAbsoluteUrl(input: string): boolean { return schemeRegex.test(input); } -// Matches the scheme of a URL, eg "https://" -const schemeRegex = /^[\w+.-]+:\/\//; function isSchemeRelativeUrl(input: string): boolean { return input.startsWith('//'); } diff --git a/commons/test/SourceLoader.test.ts b/commons/test/SourceLoader.test.ts index 22391f0..903410c 100644 --- a/commons/test/SourceLoader.test.ts +++ b/commons/test/SourceLoader.test.ts @@ -10,6 +10,7 @@ it('can lookup source code', () => { return callsite; } const site = loadCallsite(); - expect(SourceLoader.getSource(site[0]).code).toContain(`callsite ??= Callsite.getSourceCodeLocation();`); + console.log(site); + expect(SourceLoader.getSource(site[0]).code).toBe(` callsite ??= Callsite.getSourceCodeLocation();`); expect(SourceLoader.getSource(site[1]).code).toBe(` const site = loadCallsite();`); });