import { readFileSync, writeFileSync } from 'node:fs' import path from 'node:path' function normalize(value) { return value.replaceAll('\\', '/') } function externalSourceName(normalized) { const nodeModulesMarker = '/node_modules/' const markerIndex = normalized.lastIndexOf(nodeModulesMarker) if (markerIndex >= 0) { return `external/node_modules/${normalized.slice(markerIndex + nodeModulesMarker.length)}` } return `external/${normalized.split('/').filter(Boolean).at(-1) ?? 'unknown-source'}` } /** * root 内绝对路径变成项目相对路径;root 外绝对路径只保留稳定的外部标识, * 绝不把 `../` 剥掉后伪装成项目源码,也不泄露开发机用户名或目录。 */ export function sanitizeSourcePath(source, projectRoot) { if (typeof source !== 'string') return source const normalizedSource = normalize(source) const normalizedRoot = normalize(projectRoot).replace(/\/+$/, '') const windowsAbsolute = path.win32.isAbsolute(source) const posixAbsolute = path.posix.isAbsolute(normalizedSource) if (!windowsAbsolute && !posixAbsolute) return normalizedSource const pathApi = windowsAbsolute ? path.win32 : path.posix const comparableSource = windowsAbsolute ? source : normalizedSource const comparableRoot = windowsAbsolute ? projectRoot : normalizedRoot const relative = pathApi.relative(comparableRoot, comparableSource) const outside = relative === '..' || relative.startsWith(`..${pathApi.sep}`) || pathApi.isAbsolute(relative) return outside ? externalSourceName(normalizedSource) : normalize(relative || '.') } export function sanitizeSourceMapFile(sourceMapFile, projectRoot) { const sourceMap = JSON.parse(readFileSync(sourceMapFile, 'utf8')) delete sourceMap.sourcesContent if (Array.isArray(sourceMap.sources)) { sourceMap.sources = sourceMap.sources.map(source => sanitizeSourcePath(source, projectRoot)) } writeFileSync(sourceMapFile, JSON.stringify(sourceMap)) }