diff --git a/.gitignore b/.gitignore index efc7d5c..f6ed60d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ app *.tar dist_backend +dist_backend_node .pilot verify_font_baseline diff --git a/backend/app.ts b/backend/app.ts index c052016..959c77d 100644 --- a/backend/app.ts +++ b/backend/app.ts @@ -1,3 +1,10 @@ +/** 首先加载 fs 适配层,必须在所有其他导入之前!仅在 LLRT 运行时引入 */ +(() => { + if (typeof __RUNTIME__ !== "undefined" && __RUNTIME__ === "llrt") { + require("./server/llrt"); + } +})(); + import { mimeTypes } from "./server/mime_type"; import type { cMiddleware } from "./server/req_res"; import { SimpleHttpServer } from "./server/server"; @@ -15,8 +22,6 @@ import { handleFontMeta } from "./routes/font_meta"; import { startTempCleaner } from "./temp_cleaner"; import { initMemoryGate } from "./subset_queue"; import { subsetConcurrency } from "./config"; -import "./server/node"; -import "./server/llrt"; const ROOT_DIR = "dist"; diff --git a/backend/global.d.ts b/backend/global.d.ts new file mode 100644 index 0000000..8872c3e --- /dev/null +++ b/backend/global.d.ts @@ -0,0 +1,2 @@ +/** 构建时由 tsdown 注入的运行时标识 */ +declare const __RUNTIME__: "llrt" | "node"; \ No newline at end of file diff --git a/backend/interface.ts b/backend/interface.ts index fbd5ea5..c0bd98b 100644 --- a/backend/interface.ts +++ b/backend/interface.ts @@ -18,20 +18,34 @@ export let mkdir: (path: string) => Promise; export let unlink: (path: string) => Promise; +/** LLRT 专用:保存 rm 函数引用,避免闭包问题 */ +let llrtRm: ((path: string) => Promise) | undefined; + export const implInterface = (options: { stat: typeof stat; readFile: typeof readFile; writeFile: typeof writeFile; readdir: typeof readdir; mkdir: typeof mkdir; - unlink: typeof unlink; + unlink?: typeof unlink; + /** LLRT 没有 unlink,提供 rm 作为替代 */ + rm?: (path: string) => Promise; }) => { stat = options.stat; readFile = options.readFile; writeFile = options.writeFile; readdir = options.readdir; mkdir = options.mkdir; - unlink = options.unlink; + // 保存 rm 引用到模块级变量 + llrtRm = options.rm; + /** LLRT 的 fs/promises 没有 unlink,需要用 rm 代替 */ + unlink = async (path) => { + if (options.unlink) { + await options.unlink(path); + } else if (llrtRm) { + await llrtRm(path); + } + }; }; export function path_join(...paths: string[]) { diff --git a/backend/server/llrt.ts b/backend/server/llrt.ts index 3c6ddc1..2c18982 100644 --- a/backend/server/llrt.ts +++ b/backend/server/llrt.ts @@ -1 +1,27 @@ import "web-streams-polyfill/polyfill"; +import { implInterface } from "../interface"; +import * as fs from "fs/promises"; + +/** + * LLRT 的 fs 适配层 + * + * LLRT 的 fs.readdir 返回字符串数组,需要转换为 { name, isFile } 对象。 + * LLRT 的 fs/promises 没有 unlink 方法,implInterface 中会自动用 rm 代替。 + */ +implInterface({ + stat: fs.stat, + readFile: fs.readFile, + writeFile: fs.writeFile, + async readdir(path) { + const names = await fs.readdir(path); + return Promise.all( + names.map(async (name) => { + const s = await fs.stat(`${path}/${name}`); + return { name, isFile: () => s.isFile() }; + }) + ); + }, + mkdir: (path) => fs.mkdir(path, { recursive: true }), + /** LLRT 没有 unlink,用箭头函数包装 rm 避免 this 问题 */ + rm: (path: string) => fs.rm(path), +}); diff --git a/scripts/build-backend.ts b/scripts/build-backend.ts index c1524bc..280c38e 100644 --- a/scripts/build-backend.ts +++ b/scripts/build-backend.ts @@ -102,9 +102,9 @@ function runTsdown() { /** woff2 已使用纯 JS 实现(vendor/fonteditor-core/woff2/index.js),无需复制 wasm */ -/** 使用 LLRT compile 生成 .lrt 文件 */ +/** 使用 LLRT compile 生成 .lrt 文件(仅 LLRT 构建需要) */ function runLlrtCompile() { - console.log("\n--- Running LLRT compile ---"); + console.log("\n--- Running LLRT compile (LLRT only) ---"); execSync(`${LLRT_BIN} compile ./dist_backend/app.cjs ./dist_backend/app.lrt`, { stdio: "inherit", cwd: ROOT_DIR, diff --git a/tsconfig.node.json b/tsconfig.node.json index 51cc05e..a2cf0a6 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -16,7 +16,8 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "types": ["node"] }, "include": [ "vite.config.ts", diff --git a/tsdown.config.ts b/tsdown.config.ts index d951bd8..dc51339 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -5,12 +5,10 @@ import { readFileSync } from "node:fs"; const packageVersion = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8")).version; const shared = { - format: ["cjs"], + format: "cjs", clean: true, sourcemap: true, outDir: "dist_backend", - /** 构建期把 PACKAGE_VERSION 替换为字面量字符串,运行时无任何文件读取 */ - define: { PACKAGE_VERSION: JSON.stringify(packageVersion) }, outputOptions: { /** 禁用代码拆分,确保单文件输出(tsdown 默认会拆分大依赖为 chunk) */ codeSplitting: false, @@ -19,13 +17,30 @@ const shared = { /** 所有依赖都打进 bundle(LLRT scratch 镜像无 node_modules) */ alwaysBundle: [/.*/], }, -}; +} as const; export default [ defineConfig({ ...shared, + /** LLRT 构建 */ + define: { + PACKAGE_VERSION: JSON.stringify(packageVersion), + __RUNTIME__: JSON.stringify("llrt"), + }, entry: ["backend/app.ts"], }), + defineConfig({ + ...shared, + /** Node.js 构建(用于本地开发测试) */ + define: { + PACKAGE_VERSION: JSON.stringify(packageVersion), + __RUNTIME__: JSON.stringify("node"), + }, + /** 第二个配置不 clean,避免清掉第一个的输出 */ + clean: false, + entry: ["backend/app.ts"], + outDir: "dist_backend_node", + }), defineConfig({ ...shared, /** 第二个配置不 clean,避免清掉第一个的输出 */