Last active
January 29, 2021 12:30
-
-
Save SagnikPradhan/84b3cb0da59d1e48b8f68dd5a8876151 to your computer and use it in GitHub Desktop.
Spent all day working on this
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from "fs"; | |
import path from "path"; | |
import { SourceMapConsumer } from "source-map"; | |
type Nullable<T> = T | null; | |
export interface ParsedCallSite { | |
fileName: Nullable<string>; | |
line: Nullable<number>; | |
column: Nullable<number>; | |
functionName: Nullable<string>; | |
} | |
const files = new Map<string, string>(); | |
const readFile = (path: string) => { | |
const cached = files.get(path); | |
if (cached) return cached; | |
const file = fs.readFileSync(path).toString(); | |
files.set(path, file); | |
return file; | |
}; | |
/** | |
* Parse callSite | |
* | |
* @param callSites - NodeJS CallSite | |
*/ | |
export function parseCallSite(callSite: NodeJS.CallSite): ParsedCallSite { | |
const { column, line, fileName, functionName } = extractDetailsFromCallsite( | |
callSite | |
); | |
// Check for any sourcemaps | |
if (column && line && fileName && !fileName.startsWith("node:")) { | |
const sourceMapPath = getSourceMapPath(fileName); | |
if (sourceMapPath) { | |
const sourceMapDetails = extractDetailsFromSourceMap({ | |
line, | |
column, | |
sourceMapPath, | |
}); | |
return { | |
column: sourceMapDetails.column, | |
fileName: sourceMapDetails.source, | |
line: sourceMapDetails.line, | |
functionName, | |
}; | |
} | |
} | |
return { | |
fileName, | |
line, | |
column, | |
functionName, | |
}; | |
} | |
/** | |
* Get required details from the callsite. | |
* | |
* @param callsite - Node.JS CallSite | |
*/ | |
function extractDetailsFromCallsite(callsite: NodeJS.CallSite) { | |
function getFunctionName() { | |
const thisName = callsite.getTypeName(); | |
const methodName = callsite.getMethodName() || callsite.getFunctionName(); | |
if (!!thisName && !!methodName) return `${thisName}.${methodName}`; | |
else if (!!thisName) return thisName; | |
else if (!!methodName) return `unknown.${methodName}`; | |
else return null; | |
} | |
const details = { | |
fileName: callsite.getFileName(), | |
line: callsite.getLineNumber(), | |
column: callsite.getColumnNumber(), | |
functionName: getFunctionName(), | |
}; | |
return details; | |
} | |
/** | |
* Extract details from source map. | |
* | |
* @param options - Options | |
* @param options.sourceMapPath - Source map path | |
* @param options.line - Line | |
* @param options.coumn - Column | |
*/ | |
function extractDetailsFromSourceMap({ | |
sourceMapPath, | |
line, | |
column, | |
}: { | |
sourceMapPath: string; | |
line: number; | |
column: number; | |
}) { | |
const sourceMap = JSON.parse(readFile(sourceMapPath)); | |
const consumer = new SourceMapConsumer(sourceMap); | |
return consumer.originalPositionFor({ | |
line, | |
column, | |
}); | |
} | |
/** | |
* Get source map path. | |
* | |
* @param mainFilePath - Main file path | |
*/ | |
function getSourceMapPath(mainFilePath: string) { | |
const content = readFile(mainFilePath); | |
const sourceMapPath = content | |
.split(/\r?\n/g) | |
.find((line) => line.includes("//# sourceMa" + "ppingURL=")) | |
?.split("=")[1]; | |
return sourceMapPath | |
? path.join(path.dirname(mainFilePath), sourceMapPath) | |
: null; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment