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