From dc1ee66fe3f7653289e662a9d92b665f496f9108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=AE=E7=94=9F=EF=BC=88=E5=AD=90=E8=99=9A=EF=BC=89?= <2234839456@qq.com> Date: Sat, 15 Aug 2026 07:02:22 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20temp-cleaner=20=E5=9C=A8?= =?UTF-8?q?=20LLRT=20=E4=B8=8A=E7=9A=84=20unlink=20=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 interface.ts: 接受 options.unlink 和 options.rm 作为 FS 适配器的可配置方法 - 修改 llrt.ts: 使用箭头函数 (path) => fs.rm(path) 避免 this 绑定问题 - 修改 app.ts: 通过 __RUNTIME__ 条件编译,仅在 LLRT 运行时引入 llrt.ts - 修改 tsdown.config.ts: format 从字符串数组改为字符串类型,添加 as const - 修改 tsconfig.node.json: 添加 node 类型支持 - 新增 backend/global.d.ts: 声明 __RUNTIME__ 全局变量类型 - 修改 .gitignore: 添加 dist_backend_node 目录 - 修改 build-backend.ts: 支持 Node.js 版本构建输出到 dist_backend_node 修复问题: - LLRT fs/promises 没有 unlink 方法,只有 rm 方法 - CJS 格式不支持 top-level await,改用 IIFE + require - 条件编译避免 Node.js 版本包含不必要的 LLRT 适配代码 --- .gitignore | 1 + backend/app.ts | 9 +++++++-- backend/global.d.ts | 2 ++ backend/interface.ts | 18 ++++++++++++++++-- backend/server/llrt.ts | 26 ++++++++++++++++++++++++++ scripts/build-backend.ts | 4 ++-- tsconfig.node.json | 3 ++- tsdown.config.ts | 23 +++++++++++++++++++---- 8 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 backend/global.d.ts 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,避免清掉第一个的输出 */