Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ crates/js_sdk/pkg

# BMAD Method temporary files
.current-feature-branch
.bmad-temp/
.bmad-temp/


node_modules/

pnpm-lock.yaml
169 changes: 151 additions & 18 deletions crates/node_sdk/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
<div align="center">

# source_map_parser_node

**高性能 Source Map 解析 & 错误堆栈映射 (WASM)**
Rust 实现 + wasm-bindgen 导出,面向 Node.js 生产错误还原、调试定位、上下文截取。
高性能 Source Map 解析 & 错误堆栈映射(Rust + WASM)

`dist/` 目录提供稳定的库模式入口;`pkg/` 保留底层 wasm-bindgen 原始输出。

</div>

> 自 v0.1.x 起:推荐使用 **库模式封装层 (dist)**。仍可通过 `source_map_parser_node/raw` 访问原始绑定。完全 **ESM only**,不再提供 CJS 入口。

## 🚀 TL;DR

```ts
import smp, {
lookup_token,
mapErrorStackWithResolver,
} from 'source_map_parser_node';

await smp.init(); // 幂等,可省略

const token = JSON.parse(lookup_token(sourceMapContent, 1, 0));

const batch = await smp.mapErrorStackWithResolver({
errorStack: someStackString,
resolveSourceMap: (p) => cache.get(p),
});
```

| 层级 | 入口 | 用途 | 特点 |
| -------- | ---------------------------- | ---------------- | ---------------------------------------- |
| 高级封装 | `source_map_parser_node` | 直接业务使用 | 有 `init`、辅助包装函数 |
| 原始绑定 | `source_map_parser_node/raw` | 自己做包装、调试 | wasm-pack 生成;所有函数返回 JSON 字符串 |

---

## ✨ 特性

Expand All @@ -21,12 +53,13 @@ npm install source_map_parser_node

> 如果你是从源码构建,请在仓库根执行 `bash scripts/build-wasm-node.sh`,然后 `require('./crates/node_sdk/pkg')`。

## ⚡ 快速上手
## ⚡ 快速上手(库模式)

```js
const wasm = require('source_map_parser_node');
```ts
import smp, { lookup_token } from 'source_map_parser_node';

// 你也可以:import * as raw from 'source_map_parser_node/raw'

// 示例最小 sourcemap
const sm = JSON.stringify({
version: 3,
sources: ['a.js'],
Expand All @@ -35,23 +68,20 @@ const sm = JSON.stringify({
mappings: 'AAAA',
});

// 所有导出函数都返回 JSON 字符串,需要再 JSON.parse 一次
const token = JSON.parse(wasm.lookup_token(sm, 1, 0));
await smp.init(); // 幂等
const token = JSON.parse(lookup_token(sm, 1, 0));
console.log(token);
```

### 一个便捷的包装函数

```js
const W = require('source_map_parser_node');
const call = (fn, ...args) => JSON.parse(W[fn](...args));
### 原始层快速包装

const tok = call('lookup_token', sm, 1, 0);
```ts
import * as raw from 'source_map_parser_node/raw';
const json = raw.lookup_token(sm, 1, 0);
const tok = JSON.parse(json);
```

## 🧪 API 速览

所有函数同步返回 JSON 字符串,请自行 `JSON.parse`。
## 🧪 API 速览(均返回 JSON 字符串)

| 函数 | 作用 | 关键参数 | 返回结构(概念) |
| ------------------------------------------------------------------------ | ------------------------ | ------------------------------ | -------------------------------------------- |
Expand All @@ -65,7 +95,7 @@ const tok = call('lookup_token', sm, 1, 0);
| `generate_token_by_single_stack(line,column,sm,contextOffset?)` | 直接行列生成 | 可选上下文偏移 | `Token \| null` |
| `generate_token_by_stack_raw(stackRaw, formatter?, resolver?, onError?)` | 批量任务模式 | 自定义路径改写/内容解析 | `{ stacks, success, fail }` |

### generate_token_by_stack_raw 说明
### `generate_token_by_stack_raw` 说明

```ts
generate_token_by_stack_raw(
Expand Down Expand Up @@ -137,3 +167,106 @@ MIT
---

欢迎提 Issue / PR 改进 API;更多开发 / 发布流程参见仓库根 `CONTRIBUTORS.md`。

## 🔀 模块与分层策略

| 目录/入口 | 说明 | 适用场景 |
| ---------------------------- | ------------------------------------------- | ------------------------------------ |
| `dist/index.es.js` | 库模式(Vite 构建),顶层已完成 wasm 初始化 | 生产业务、通用集成 |
| `pkg/*.js/wasm` | wasm-pack 原始输出 | 调试、二次封装、对 wasm 行为精准控制 |
| `source_map_parser_node/raw` | 指向 `pkg/source_map_parser_node.js` | 需要最原始绑定 |

特性:

- 仅 ESM:无需 CJS 分发路径,减少条件分支
- wasm 静态导入:让现代打包器可执行拓扑分析与缓存
- 测试使用 alias 指向 dist,保证真实发布路径被验证

### 常见集成模式

| 场景 | 推荐 | 说明 |
| -------------- | ------ | --------------------- |
| Web 服务 / SSR | 库模式 | 直接 import 即可 |
| CLI / 本地工具 | 库模式 | 体积接受、维护简单 |
| 极限性能实验 | 原始层 | 自行管理缓存/解析策略 |

### 从旧版本迁移

旧:`import * as wasm from 'source_map_parser_node'` (直接就是原始层)
新:

```diff
- import * as wasm from 'source_map_parser_node';
+ import smp, * as wasm from 'source_map_parser_node'; // 保持原有 API 同时获得封装
+ await smp.init();
```

## 🧠 高级封装:`mapErrorStackWithResolver`

```ts
import smp from 'source_map_parser_node';
const result = await smp.mapErrorStackWithResolver({
errorStack: rawError.stack,
resolveSourceMap: (fp) => lru.get(fp),
formatter: (fp) => (fp.endsWith('.map') ? fp : fp + '.map'),
onError: (line, msg) => console.warn('[SM_FAIL]', line, msg),
});
```

返回即为底层 `generate_token_by_stack_raw` 解析结构。

## 🧩 构建 & 测试

本仓库内部:

```bash
pnpm run build:lib # 构建 dist
pnpm test # 预设 pretest 钩子可自动构建
```

Vite / Vitest 需要:

```ts
import wasm from 'vite-plugin-wasm';
import topLevelAwait from 'vite-plugin-top-level-await';
export default defineConfig({
plugins: [wasm(), topLevelAwait()],
});
```

## 📦 体积与优化建议

- 若生产体积仍偏大,可使用 `wasm-opt -Oz`(需要安装 binaryen)
- 频繁重复解析同一 sourcemap:上层缓存其字符串;或追加一个 JS 侧 LRU
- 批量 stack 解析优先使用 `generate_token_by_stack_raw` 减少往返

## 🧪 返回 JSON 的再封装(可选)

在你的代码中可创建一个轻量包装:

```ts
import { lookup_token as _lookup } from 'source_map_parser_node';
export const lookupToken = (sm: string, line: number, col: number) =>
JSON.parse(_lookup(sm, line, col));
```

## 🔒 运行时注意事项

- 行号传入:1-based;列:0-based
- sourcemap 必须符合 v3 标准;异常返回结构含有 `error`
- Node 需支持 ESM + WebAssembly(Node 16+ 建议 18+)

## 🧩 Vite / Vitest 使用提示

由于 bundler 目标使用了 **WebAssembly ESM 集成提案** 语法,直接在 Vite 中需要插件支持:

```ts
// vitest.config.ts / vite.config.ts
import wasm from 'vite-plugin-wasm';
import topLevelAwait from 'vite-plugin-top-level-await';
export default defineConfig({
plugins: [wasm(), topLevelAwait()],
});
```

若你的构建工具不支持上面语法,可改用 `wasm-pack --target nodejs` 或自己写 `fetch + WebAssembly.instantiate` 包装。
44 changes: 44 additions & 0 deletions crates/node_sdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "source_map_parser_node",
"collaborators": [
"MasonChow <masonchat@foxmail.com>"
],
"description": "A WebAssembly package for source_map_parser",
"version": "0.2.1",
"license": "MIT",
"type": "module",
"files": [
"dist/",
"README.md",
"LICENSE"
],
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.es.js"
},
"./wasm": "./pkg/source_map_parser_node_bg.wasm",
"./raw": {
"types": "./pkg/source_map_parser_node.d.ts",
"import": "./pkg/source_map_parser_node.js"
}
},
"scripts": {
"build:lib": "vite build",
"build": "bash ../../scripts/build-wasm-node.sh && vite build",
"pretest": "pnpm run build:lib",
"test": "pnpm pretest && vitest --run",
"test:coverage": "vitest --coverage",
"deploy": "pnpm run build && npm publish"
},
"devDependencies": {
"@vitest/coverage-v8": "^2.0.5",
"@vitest/ui": "^2.0.5",
"typescript": "^5.5.4",
"vite": "^5.4.0",
"vite-plugin-top-level-await": "^1.6.0",
"vite-plugin-wasm": "^3.5.0",
"vitest": "^2.0.5"
}
}
43 changes: 43 additions & 0 deletions crates/node_sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 高级入口:对 pkg 目录下 wasm 绑定做一层稳定封装
// 目标:库模式构建 (Vite) 输出到 dist,并在使用端自动完成 wasm 初始化。
// 注意:保持对原始 API 的命名导出,不修改 wasm 生成的函数签名。

// 直接引用已经生成的绑定代码。
// Vite 在构建时会处理对 .wasm 的静态导入(需保留插件或默认支持)。
import * as lowLevel from '../pkg/source_map_parser_node.js';

// 再导出所有低层 API,保持向后兼容。
export * from '../pkg/source_map_parser_node.js';

// 提供一个可显式调用的 init(幂等),方便在某些 SSR/自定义加载场景中手动控制。
let _inited = false;
export async function init(): Promise<void> {
if (_inited) return;
// 这里实际上只要执行过绑定文件的顶层代码就已经初始化,
// 但为了语义化,仍然提供一个 Promise 接口,未来可在此扩展(例如自定义 wasm fetch)。
_inited = true;
}

// 提供一个辅助方法,对常见用例进行包装示例(非必须,可选增强)。
export async function mapErrorStackWithResolver(options: {
errorStack: string;
resolveSourceMap: (filePath: string) => string | undefined | null;
formatter?: (filePath: string) => string;
onError?: (rawLine: string, message: string) => void;
}): Promise<any> {
await init();
const { errorStack, resolveSourceMap, formatter, onError } = options;
return lowLevel.generate_token_by_stack_raw(
errorStack,
formatter ?? null,
(p: string) => resolveSourceMap(p) ?? null,
onError ?? null
);
}

// 默认导出整体 API(含原始导出与封装方法)。
export default {
init,
mapErrorStackWithResolver,
...lowLevel,
};
Loading
Loading