|
1 | | -# source-map-parser-node |
| 1 | +# source_map_parser_node(Node SDK) |
2 | 2 |
|
3 | | -一个高性能的 Source Map 解析库,基于 Rust + WebAssembly 构建,提供 JavaScript 堆栈解析和 Source Map 位置映射功能。 |
| 3 | +基于 Rust + WebAssembly 的高性能 Source Map 解析库(Node 环境)。提供错误堆栈解析、位置回溯与上下文提取等能力,API 返回已解析好的 JS 对象(内部已完成 JSON.parse)。 |
| 4 | + |
| 5 | +> 注意:本包为 Node SDK(ESM 模块)。使用前需先调用一次 `init()` 完成按需加载。 |
4 | 6 |
|
5 | 7 | ## 安装 |
6 | 8 |
|
7 | 9 | ```bash |
8 | | -npm install source-map-parser-node |
| 10 | +npm install source_map_parser_node |
| 11 | +# 或 |
| 12 | +pnpm add source_map_parser_node |
9 | 13 | ``` |
10 | 14 |
|
11 | 15 | ## 快速开始 |
12 | 16 |
|
13 | | -### 基本用法 |
14 | | - |
15 | | -```javascript |
16 | | -const { lookupToken, mapStackLine } = require('source-map-parser-node'); |
| 17 | +### 初始化 |
17 | 18 |
|
18 | | -// 从文件或网络加载 source map 内容 |
19 | | -const sourceMapContent = fs.readFileSync('bundle.js.map', 'utf8'); |
| 19 | +```ts |
| 20 | +import { init } from 'source_map_parser_node'; |
20 | 21 |
|
21 | | -// 映射单个位置 |
22 | | -const token = lookupToken(sourceMapContent, 10, 25); |
23 | | -console.log(token); |
24 | | -// { |
25 | | -// "src": "src/index.ts", |
26 | | -// "line": 5, |
27 | | -// "column": 10, |
28 | | -// "name": "myFunction" |
29 | | -// } |
30 | | - |
31 | | -// 映射堆栈行 |
32 | | -const stackLine = "at myFunction (bundle.js:10:25)"; |
33 | | -const mapped = mapStackLine(sourceMapContent, stackLine); |
34 | | -console.log(mapped); |
35 | | -// { |
36 | | -// "src": "src/index.ts", |
37 | | -// "line": 5, |
38 | | -// "column": 10, |
39 | | -// "name": "myFunction", |
40 | | -// "original": "at myFunction (bundle.js:10:25)" |
41 | | -// } |
| 22 | +await init(); // 仅需调用一次 |
42 | 23 | ``` |
43 | 24 |
|
44 | | -### 批量处理错误堆栈 |
| 25 | +### 映射单个位置(lookup_token) |
45 | 26 |
|
46 | | -```javascript |
47 | | -const { generateTokenByStackRaw } = require('source-map-parser-node'); |
| 27 | +```ts |
| 28 | +import { init, lookup_token } from 'source_map_parser_node'; |
| 29 | +import fs from 'node:fs'; |
48 | 30 |
|
49 | | -const errorStack = ` |
50 | | -Error: Something went wrong |
51 | | - at myFunction (bundle.js:10:25) |
52 | | - at anotherFunction (bundle.js:15:8) |
53 | | - at main (bundle.js:20:3) |
54 | | -`; |
| 31 | +await init(); |
55 | 32 |
|
56 | | -// 定义 source map 解析器 |
57 | | -const resolver = (sourcePath) => { |
58 | | - if (sourcePath === 'bundle.js') { |
59 | | - return fs.readFileSync('bundle.js.map', 'utf8'); |
60 | | - } |
61 | | - return null; |
62 | | -}; |
63 | | - |
64 | | -const result = generateTokenByStackRaw(errorStack, null, resolver); |
65 | | -console.log(result.success); // 成功映射的 token 列表 |
66 | | -console.log(result.fail); // 映射失败的堆栈信息 |
| 33 | +const sm = fs.readFileSync('bundle.js.map', 'utf8'); |
| 34 | +const tok = lookup_token(sm, 10, 25); |
| 35 | +console.log(tok); |
| 36 | +// { src, line, column, name?, source?, original? } |
67 | 37 | ``` |
68 | 38 |
|
69 | | -## API 参考 |
70 | | - |
71 | | -### lookupToken(sourceMapContent, line, column) |
72 | | - |
73 | | -映射单个位置到源代码位置。 |
74 | | - |
75 | | -- `sourceMapContent`: string - Source Map 内容字符串 |
76 | | -- `line`: number - 编译后代码的行号 |
77 | | -- `column`: number - 编译后代码的列号 |
78 | | - |
79 | | -返回: `{ src: string, line: number, column: number, name?: string }` |
80 | | - |
81 | | -### lookupTokenWithContext(sourceMapContent, line, column, contextLines) |
82 | | - |
83 | | -映射位置并获取上下文代码。 |
84 | | - |
85 | | -- `contextLines`: number - 上下文的行数 |
86 | | - |
87 | | -返回: 包含上下文信息的 token 对象 |
| 39 | +### 映射单行堆栈(map_stack_line) |
88 | 40 |
|
89 | | -### mapStackLine(sourceMapContent, stackLine) |
| 41 | +```ts |
| 42 | +import { init, map_stack_line } from 'source_map_parser_node'; |
| 43 | +import fs from 'node:fs'; |
90 | 44 |
|
91 | | -映射单行堆栈信息。 |
| 45 | +await init(); |
92 | 46 |
|
93 | | -- `stackLine`: string - 堆栈行字符串,如 "at myFunction (bundle.js:10:25)" |
94 | | - |
95 | | -返回: 映射后的堆栈信息对象 |
96 | | - |
97 | | -### mapStackTrace(sourceMapContent, stackTrace) |
98 | | - |
99 | | -映射完整的堆栈跟踪。 |
100 | | - |
101 | | -- `stackTrace`: string - 完整的堆栈跟踪字符串 |
| 47 | +const sm = fs.readFileSync('bundle.js.map', 'utf8'); |
| 48 | +const stackLine = ' at myFunction (bundle.js:10:25)'; |
| 49 | +const mapped = map_stack_line(sm, stackLine); |
| 50 | +console.log(mapped); |
| 51 | +// { src, line, column, name?, source?, original? } |
| 52 | +``` |
102 | 53 |
|
103 | | -返回: 映射后的堆栈信息数组 |
| 54 | +### 映射完整错误堆栈(map_error_stack) |
104 | 55 |
|
105 | | -### mapErrorStack(sourceMapContent, errorStackRaw, contextLines?) |
| 56 | +```ts |
| 57 | +import { init, map_error_stack } from 'source_map_parser_node'; |
| 58 | +import fs from 'node:fs'; |
106 | 59 |
|
107 | | -映射完整的错误堆栈。 |
| 60 | +await init(); |
108 | 61 |
|
109 | | -- `errorStackRaw`: string - 原始错误堆栈字符串 |
110 | | -- `contextLines`: number (可选) - 上下文行数 |
| 62 | +const sm = fs.readFileSync('bundle.js.map', 'utf8'); |
| 63 | +const errorStack = [ |
| 64 | + 'Error: Something went wrong', |
| 65 | + ' at myFunction (bundle.js:10:25)', |
| 66 | + ' at anotherFunction (bundle.js:15:8)', |
| 67 | +].join('\n'); |
111 | 68 |
|
112 | | -返回: 映射后的错误堆栈对象 |
| 69 | +const result = map_error_stack(sm, errorStack, 2); |
| 70 | +console.log(result.error_message); |
| 71 | +console.log(result.frames_with_context?.length); |
| 72 | +``` |
113 | 73 |
|
114 | | -### generateTokenByStackRaw(stackRaw, formatter?, resolver?, onError?) |
| 74 | +## 批量处理错误堆栈(generate_token_by_stack_raw) |
115 | 75 |
|
116 | | -批量处理错误堆栈并生成 token。 |
| 76 | +当你持有“原始错误堆栈文本(含首行消息)”,并且可以按路径解析对应的 Source Map 内容时,推荐用批量 API: |
117 | 77 |
|
118 | | -- `stackRaw`: string - 原始堆栈文本 |
119 | | -- `formatter`: Function (可选) - 源文件路径格式化函数 |
120 | | -- `resolver`: Function (可选) - Source Map 内容解析器 |
121 | | -- `onError`: Function (可选) - 错误处理回调 |
| 78 | +```ts |
| 79 | +import { init, generate_token_by_stack_raw } from 'source_map_parser_node'; |
| 80 | +import fs from 'node:fs'; |
122 | 81 |
|
123 | | -返回: `{ success: Token[], fail: GenerateFailStack[], stacks: Stack[] }` |
| 82 | +await init(); |
124 | 83 |
|
125 | | -## 高级用法 |
| 84 | +const errorStack = [ |
| 85 | + 'Error: test', |
| 86 | + ' at foo (bundle.js:10:25)', |
| 87 | + ' at bar (bundle.js:15:8)', |
| 88 | +].join('\n'); |
126 | 89 |
|
127 | | -### 自定义源文件路径映射 |
| 90 | +// 可选:统一重写源文件路径(例如附加 .map 或绝对化) |
| 91 | +const formatter = (p: string) => p; |
128 | 92 |
|
129 | | -```javascript |
130 | | -const formatter = (sourcePath) => { |
131 | | - // 添加 .map 后缀 |
132 | | - return sourcePath + '.map'; |
| 93 | +// 必要:按路径返回 Source Map 内容字符串 |
| 94 | +const resolver = (p: string) => { |
| 95 | + if (p.endsWith('bundle.js')) return fs.readFileSync('bundle.js.map', 'utf8'); |
| 96 | + return undefined; // 无法解析的帧将被计入 fail |
133 | 97 | }; |
134 | 98 |
|
135 | | -const resolver = (formattedPath) => { |
136 | | - return fs.readFileSync(formattedPath, 'utf8'); |
| 99 | +const onError = (line: string, message: string) => { |
| 100 | + console.warn('[map fail]', line, message); |
137 | 101 | }; |
138 | 102 |
|
139 | | -const result = generateTokenByStackRaw(errorStack, formatter, resolver); |
| 103 | +const r = generate_token_by_stack_raw(errorStack, formatter, resolver, onError); |
| 104 | +console.log(r.success.length, r.fail.length); |
140 | 105 | ``` |
141 | 106 |
|
142 | | -### 异步 Source Map 加载 |
| 107 | +## 便捷辅助(自动 init):mapErrorStackWithResolver |
143 | 108 |
|
144 | | -```javascript |
145 | | -async function asyncResolver(sourcePath) { |
146 | | - const response = await fetch(`/source-maps/${sourcePath}.map`); |
147 | | - return await response.text(); |
148 | | -} |
| 109 | +对于最常见的“拿到错误堆栈 + 我能根据路径拿到 sourcemap 内容”的场景,可以直接使用内置辅助方法;它会自动调用 `init()` 并返回与批量 API 同结构结果: |
149 | 110 |
|
150 | | -// 注意:当前版本需要同步 resolver,异步场景需要在外部处理 |
151 | | -``` |
152 | | - |
153 | | -## 性能特性 |
| 111 | +```ts |
| 112 | +import { mapErrorStackWithResolver } from 'source_map_parser_node'; |
154 | 113 |
|
155 | | -- 🚀 基于 Rust + WebAssembly 构建,性能卓越 |
156 | | -- 📦 零依赖,轻量级包体积 |
157 | | -- 🔍 支持多种 JavaScript 引擎堆栈格式(V8、Firefox、Safari) |
158 | | -- 🗺️ 完整的 Source Map v3 规范支持 |
159 | | -- 🎯 精确的位置映射和上下文提取 |
| 114 | +const mapStore = new Map<string, string>(); |
| 115 | +mapStore.set('https://example.com/app.min.js', '{"version":3,...}'); |
160 | 116 |
|
161 | | -## 浏览器支持 |
| 117 | +const result = await mapErrorStackWithResolver({ |
| 118 | + errorStack: 'Error: boom\n at fn (https://example.com/app.min.js:1:10)', |
| 119 | + resolveSourceMap: (p) => mapStore.get(p), |
| 120 | + formatter: (p) => p, |
| 121 | +}); |
| 122 | +console.log(result.success.length); |
| 123 | +``` |
162 | 124 |
|
163 | | -支持所有现代浏览器和 Node.js 环境: |
| 125 | +## API 参考(与导出一致,全部已 JSON.parse) |
| 126 | + |
| 127 | +- init(): Promise<LowLevelModule> |
| 128 | + |
| 129 | + - 说明:按需加载并缓存 wasm 模块。除 `mapErrorStackWithResolver` 外,使用其它 API 前需手动调用一次。 |
| 130 | + |
| 131 | +- lookup_token(sm: string, line: number, column: number): SourceMapToken | null |
| 132 | +- lookup_token_with_context(sm: string, line: number, column: number, context_lines: number): Token | null |
| 133 | +- lookup_context(sm: string, line: number, column: number, context_lines: number): WasmContextSnippet | null |
| 134 | +- map_stack_line(sm: string, stack_line: string): SourceMapToken | null |
| 135 | +- map_stack_line_with_context(sm: string, stack_line: string, context_lines: number): Token | null |
| 136 | +- map_stack_trace(sm: string, stack_trace: string): SourceMapToken[] |
| 137 | +- map_error_stack(sm: string, error_stack_raw: string, context_lines?: number): MappedErrorStack |
| 138 | +- generate_token_by_single_stack(line: number, column: number, sm: string, context_offset?: number): Token | null |
| 139 | +- generate_token_by_stack_raw(stack_raw: string, formatter?: (p: string) => string, resolver?: (p: string) => string | undefined, on_error?: (rawLine: string, message: string) => void): GenerateResult |
| 140 | +- mapErrorStackWithResolver(options: { errorStack: string; resolveSourceMap: (p: string) => string | undefined; formatter?: (p: string) => string; onError?: (rawLine: string, message: string) => void; }): Promise<GenerateResult> |
| 141 | + |
| 142 | +返回类型(节选): |
| 143 | + |
| 144 | +```ts |
| 145 | +import type { |
| 146 | + SourceMapToken, |
| 147 | + Token, |
| 148 | + GenerateResult, |
| 149 | + MappedErrorStack, |
| 150 | + WasmContextSnippet, |
| 151 | +} from 'source_map_parser_node'; |
| 152 | +``` |
164 | 153 |
|
165 | | -- Node.js 14+ |
166 | | -- Chrome 60+ |
167 | | -- Firefox 60+ |
168 | | -- Safari 14+ |
169 | | -- Edge 79+ |
| 154 | +> 可选参数使用标准的可选写法(不再使用 `| null` 暴露在 API 表面),内部会自动处理与 wasm 层期望的对接。 |
170 | 155 |
|
171 | | -## 开发构建 |
| 156 | +## 运行环境与特性 |
172 | 157 |
|
173 | | -```bash |
174 | | -# 安装 wasm-pack |
175 | | -cargo install wasm-pack |
| 158 | +- Node.js 18+(ESM 模块) |
| 159 | +- 内部使用 Rust + WebAssembly,性能优异 |
| 160 | +- 返回值均为已解析的 JS 对象(无需再手动 JSON.parse) |
176 | 161 |
|
177 | | -# 构建 WASM 包 |
178 | | -wasm-pack build --target nodejs |
| 162 | +## 本地开发(可选) |
179 | 163 |
|
180 | | -# 运行测试 |
181 | | -wasm-pack test --node |
| 164 | +```bash |
| 165 | +pnpm install |
| 166 | +pnpm run build # 构建 wasm + 打包库 + 生成 d.ts |
| 167 | +pnpm test # 运行 vitest 测试 |
182 | 168 | ``` |
183 | 169 |
|
184 | 170 | ## 许可证 |
185 | 171 |
|
186 | | -MIT License |
| 172 | +MIT License |
0 commit comments