Files
OJ2/apps/api/src/vendor/jieba.ts
T
xuyueandClaude Opus 5 ed56a209ea
Deploy / deploy (push) Has been cancelled
chore(格式): Prettier 统一到全仓,后端和契约一次性格式化
原来只有 `apps/web` 在 Prettier 下(配置在 `apps/web/.prettierrc.toml`、脚本在
web 的 package.json),后端和契约从来没格式化过 —— 手写在 100 列上下,`db/schema.ts`
还是 drizzle-kit pull 留下的 tab 缩进。两套口径分叉久了,跨端改一处就得记着「这边
什么风格」。

- 配置搬到根目录 `.prettierrc.toml`,内容不变(`semi=false`,其余全默认,
  printWidth 80 —— 和前端已有的格式一致,不另立一套宽度);
- 脚本统一成根目录 `bun run fmt`,覆盖 `apps/*/src`、`apps/web/tests` 和两个构建
  配置;web 自己那份 `fmt` 和重复的 prettier 依赖删掉;
- `.prettierignore` 挡掉两类不该碰的:drizzle-kit 生成的 `src/db/meta/` 结构快照
  (它是 db:generate 的比对输入,只该由 drizzle-kit 写)、unplugin 每次 dev 都会
  重写的 `auto-imports.d.ts` / `components.d.ts`;
- 全量跑了一遍。纯格式,无行为改动:api typecheck / check:routes / check:ast、
  前端 type-check 全过,起 api 打了接口确认正常。前端这 39 个文件的小改动是
  prettier 版本漂移(类型断言的换行口径变了),不是新配置带来的。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:27:34 -06:00

72 lines
3.4 KiB
TypeScript

/**
* 加载 jieba。开发和编译两种形态走不同的路,因为没有一条路两边都能用。
*
* ## 编译形态(生产)
*
* `@node-rs/jieba` 的 `index.js` 在运行时探测平台再 `require` 对应的子包,
* `dict.js` 用 `__dirname` 去读同目录的 `dict.txt`。这两件事在
* `bun build --compile` 之后都不成立 —— 二进制里 `__dirname` 和模块解析根都是
* `/$bunfs/root`,子包和 dict.txt 都不在那儿。实测编译后直接
* `Failed to load native binding`,而且**只在离开仓库目录后才报**(在仓库里跑时
* 它顺着 cwd 摸到了 node_modules,假装没事),是那种到服务器上才炸的坑。
*
* 所以编译形态下把 `.node` 和 `dict.txt` 用 `with { type: "file" }` 内嵌成资源,
* 运行时 Bun 把它们摊在 `/$bunfs/root/` 下,`require()` 和 `readFileSync` 都能正常拿到。
*
* ## 开发形态
*
* 但 `.node` 的资源导入**只有打包器认,运行时不认**:`bun run` 遇到
* `import x from "….node" with { type: "file" }` 会报
* “To load Node-API modules, use require() or process.dlopen instead of import.”,
* 整个服务起不来。所以开发时老老实实用包自己的入口,那条路在 `bun run` 下是好的。
*
* 两个分支都必须是**动态** import:静态 import 在模块加载时就会求值,
* 用 `isCompiled` 判断也来不及,dev 一样会撞上上面那个报错。
*
* 平台写死 linux-x64-gnu:部署目标是 debian 基底的容器,本机开发也是 x64 glibc。
* 换基底镜像或 CPU 架构必须**同时**改两处 —— 这里的 import,和 apps/api/package.json
* 里 `@node-rs/jieba-linux-x64-gnu` 那条依赖。
*
* 那条依赖为什么要显式写:它本是 `@node-rs/jieba` 的 optionalDependency,本机装出来的
* node_modules 是扁平的,靠提升就能解析到,所以本地构建一直是好的。但容器里 bun 用
* isolated 布局(包都在 `node_modules/.bun/` 下),提升不到,`bun build` 直接报
* `Could not resolve`。代码既然真的直接 import 它,就该是直接依赖。
*/
import { isCompiled } from "../runtime"
export interface JiebaInstance {
cut(text: string, hmm?: boolean): string[]
loadDict(dict: Buffer): void
}
/** 内置词典建一个分词器。idf.txt 用不到,不内嵌,省二进制体积 */
export async function withBuiltinDict(): Promise<JiebaInstance> {
if (!isCompiled) {
const { Jieba } = await import("@node-rs/jieba")
const { dict } = await import("@node-rs/jieba/dict")
return Jieba.withDict(dict) as unknown as JiebaInstance
}
const { readFileSync } = await import("node:fs")
const addonPath = (
await import("@node-rs/jieba-linux-x64-gnu/jieba.linux-x64-gnu.node", {
with: { type: "file" },
})
).default as unknown as string
const dictPath = (
await import("@node-rs/jieba/dict.txt", { with: { type: "file" } })
).default as unknown as string
let addon: { Jieba: { withDict(dict: Buffer): JiebaInstance } }
try {
addon = require(addonPath)
} catch (error) {
throw new Error(
`加载 jieba 原生模块失败(${addonPath})。若已更换容器基底或 CPU 架构,` +
`需同步修改 src/vendor/jieba.ts 里写死的 linux-x64-gnu 导入。原始错误:${String(error)}`,
)
}
return addon.Jieba.withDict(readFileSync(dictPath))
}