UNPKG

@sentry-internal/lynx-plugin

Version:
111 lines (108 loc) 4.51 kB
import { URL } from 'node:url'; import * as fs from 'node:fs'; import * as path from 'node:path'; import * as process from 'node:process'; import { SourceMapConsumer } from 'source-map'; import { logger } from '@sentry/core'; import { PREFIX } from './prefix.mjs'; import { tryCatch } from './utils/tryCatch.mjs'; async function symbolicateFrames({ frames, projectRootPath = process.cwd(), serverPublicPath = path.join(projectRootPath, 'dist'), }) { var _a; const symbolicatedFrames = []; for (const frame of frames) { const { lineno, colno, filename } = frame; if (!lineno || !colno) { logger.warn(`${PREFIX} Skipping frame without lineno or colno: ${JSON.stringify(frame, null, 2)}`); symbolicatedFrames.push(frame); continue; } const filepath = lynxUrlToPath(filename); if (!filepath) { logger.warn(`${PREFIX} Skipping frame without filename: ${JSON.stringify(frame, null, 2)}`); symbolicatedFrames.push(frame); continue; } const resolvedPath = path.resolve(serverPublicPath, filepath); const { data: fileContents, error } = await tryCatch(() => fs.promises.readFile(resolvedPath, 'utf8')); if (error) { logger.warn(`${PREFIX} Failed to read file ${resolvedPath}: ${error}`); symbolicatedFrames.push(frame); continue; } const sourceMapUrl = extractSourceMapUrl(fileContents); if (!sourceMapUrl) { logger.warn(`${PREFIX} No source map url found in ${resolvedPath}`); symbolicatedFrames.push(frame); continue; } // Example: http://localhost:3000/assets/main.mjs.map const sourceMapPath = (_a = urlToPath(sourceMapUrl)) === null || _a === void 0 ? void 0 : _a.slice(1); // remove leading '/' if (!sourceMapPath) { logger.warn(`${PREFIX} Source map url ${sourceMapUrl} is not a valid path`); symbolicatedFrames.push(frame); continue; } const resolvedSourceMapPath = path.resolve(serverPublicPath, sourceMapPath); const { data: sourceMapContents, error: sourceMapError } = await tryCatch(() => fs.promises.readFile(resolvedSourceMapPath, 'utf8')); if (sourceMapError) { logger.warn(`${PREFIX} Failed to read source map ${sourceMapPath}: ${sourceMapError}`); symbolicatedFrames.push(frame); continue; } const sourceMap = await new SourceMapConsumer(sourceMapContents); const original = sourceMap.originalPositionFor({ line: lineno, column: colno, }); const originalSourcePath = original.source ? urlToPath(original.source) : undefined; const relativeSourcePath = originalSourcePath && path.isAbsolute(originalSourcePath) ? path.relative(projectRootPath, originalSourcePath) : undefined; symbolicatedFrames.push({ ...frame, filename: relativeSourcePath || originalSourcePath, lineno: original.line || undefined, colno: original.column || undefined, }); } return symbolicatedFrames; } function lynxUrlToPath(filename) { var _a; if (!filename) { return undefined; } if (!filename.startsWith('file://')) { logger.warn(`${PREFIX} Filename ${filename} does not start with file://`); } return (_a = urlToPath(filename)) === null || _a === void 0 ? void 0 : _a.slice(1); // remove leading '/'; } function urlToPath(filename) { if (!filename) { return undefined; } const { data: url, error } = tryCatch(() => new URL(filename)); if (error) { logger.warn(`${PREFIX} Filename ${filename} is not a valid URL`); return filename; } return url.pathname; } // https://github.com/lynx-family/lynx/blob/c9ba416cd90ef6d04565385a399dc245e616cced/devtool/lynx_devtool/resources/lynx-logbox/src/utils/getSourceMap.ts#L69 function extractSourceMapUrl(fileContents) { const regex = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/gm; let match = null; for (;;) { let next = regex.exec(fileContents); if (next == null) { break; } match = next; } if (!(match && match[1])) { return undefined; } return match[1].toString(); } export { lynxUrlToPath, symbolicateFrames, urlToPath }; //# sourceMappingURL=symbolicator.mjs.map