工人 -- -- 工人
实验:此软件包正在积极开发中,其API可能会不经通知而更改. 未建议用于生产.
在Cloudflare软件上装入并服务全高的应用程序 装入器装订 (闭塞β. 动态生成具有活性npm依赖性的工人,或者用客户端捆绑和静态资产服务来构建完整的应用程序.
运行时间要求
此软件包只运行 在Cloudflare工人运行时间(workerd(中文(简体) ) - 生产和生产 wrangler dev它通过一个 esbuild-wasm WebAssembly.Module 输入只有 Worker 模块加载器可以解析.
它会 没有 在平地节点下工作。 特别是,从使用默认节点池的 Vitest/Jest 测试中导入它,会显示一个指向这里的错误. 要测试取决于 @cloudflare/worker-bundler中的测试 @cloudflare/vitest-pool-workers 所以测试身体在内部执行 workerd。见 packages/worker-bundler/src/tests/vitest.config.ts 在此重播中进行示例设置.
安装
npm install @cloudflare/worker-bundler
快速启动
装入工人
提供源代码和依赖性——捆绑者处理其余:
import { createWorker } from "@cloudflare/worker-bundler";
const worker = env.LOADER.get("my-worker", async () => {
const { mainModule, modules } = await createWorker({
files: {
"src/index.ts": `
import { Hono } from 'hono';
import { cors } from 'hono/cors';
const app = new Hono();
app.use('*', cors());
app.get('/', (c) => c.text('Hello from Hono!'));
app.get('/json', (c) => c.json({ message: 'It works!' }));
export default app;
`,
"package.json": JSON.stringify({
dependencies: { hono: "^4.0.0" }
})
}
});
return { mainModule, modules, compatibilityDate: "2026-01-01" };
});
await worker.getEntrypoint().fetch(request);
构建一个全Stack App
使用 createApp 将服务器 Worker、客户端 JavaScript 和静态资产捆绑在一起。 资产单独返还给东道方服务——它们没有嵌入隔离区:
import {
createApp,
handleAssetRequest,
createMemoryStorage
} from "@cloudflare/worker-bundler";
const result = await createApp({
files: {
"src/server.ts": `
export default {
fetch(request) {
return new Response("API response");
}
}
`,
"src/client.ts": `
document.getElementById("app").textContent = "Hello from the client!";
`
},
server: "src/server.ts",
client: "src/client.ts",
assets: {
"/index.html":
"<!DOCTYPE html><div id='app'></div><script src='/client.js'></script>",
"/favicon.ico": favicon
},
assetConfig: {
not_found_handling: "single-page-application"
}
});
const worker = env.LOADER.get("my-app", () => ({
mainModule: result.mainModule,
modules: result.modules,
compatibilityDate: "2026-01-01"
}));
// Serve assets on the host, forward the rest to the isolate
const storage = createMemoryStorage(result.assets);
const assetResponse = await handleAssetRequest(
request,
result.assetManifest,
storage,
result.assetConfig
);
if (assetResponse) return assetResponse;
return worker.getEntrypoint().fetch(request);
静态资产有适当的内容类型、ETags和缓存。 无法匹配资产的请求会落入您的服务器密码中 .
API 密码
createWorker(options)
Bundles 源文件输入一个工人.
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
files | Record<string, string> | FileSystem | 所需经费 | 输入文件——普通对象或任何 FileSystem 执行 |
entryPoint | string | 自动检测 | 输入点文件路径 |
bundle | boolean | true | 将所有依赖性合并为一个文件 |
externals | string[] | [] | 排除捆绑的模块cloudflare:* 总是外部) |
target | string | 'es2022' | 目标环境 |
minify | boolean | false | 缩小输出 |
sourcemap | boolean | false | 生成内线源图 |
registry | string | 'https://registry.npmjs.org' | npm 注册 URL |
jsx | "transform" | "preserve" | "automatic" | 新建默认 | JSX 变换模式 |
jsxImportSource | string | 新建默认 | JSX运行时间导入源(例如. "react", (中文(简体) ), "preact"(中文(简体) ) |
define | Record<string, string> | {} | 在捆绑时间应用的常数替换 |
loader | Record<string, BundlerLoader> | {} | 扩能加载器(如:. { ".svg": "text", ".wasm": "binary" }) (中文(简体) ). 见 高级捆绑选项 用于支持的集. |
conditions | string[] | 新建默认 | 决议期间的一揽子出口条件 |
virtualModules | Record<string, string> | {} | 由生成的 JavaScript 模块源支持的精确导入指定别名 。 见 高级捆绑选项。 。 。 . |
__dangerouslyUseEsBuildPluginsDoNotUseOrYouWillBeFired | unknown[] | [] | 逃出舱口:外置的esbuild插件,在内部虚拟-fs插件前运行. 不为铸币局所覆盖——见 高级捆绑选项。 。 。 . |
回返情况:
{
mainModule: string;
modules: Record<string, string | Module>;
wranglerConfig?: WranglerConfig;
warnings?: string[];
}
createApp(options)
构建一个全存储应用程序:服务器Worker +客户端捆绑+静态资产.
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
files | Record<string, string> | FileSystem | 所需经费 | 所有源文件——普通对象或任何 FileSystem |
server | string | 自动检测 | 服务器入口( 工人获取处理器) |
client | string | string[] | —— 说 | 为浏览器捆绑的客户端入口 |
assets | Record<string, string | ArrayBuffer> | —— 说 | 静态资产(路径名-内容) |
assetConfig | AssetConfig | —— 说 | 资产服务配置 |
bundle | boolean | true | Bundle 服务器依赖性 |
externals | string[] | [] | 排除捆绑的模块 |
target | string | 'es2022' | 服务器目标环境 |
minify | boolean | false | 缩小输出 |
sourcemap | boolean | false | 生成源地图 |
registry | string | npm 默认 | npm 注册 URL |
jsx | "transform" | "preserve" | "automatic" | 新建默认 | JSX 转换模式(适用于服务器和客户端捆绑) |
jsxImportSource | string | 新建默认 | JSX运行时间导入源(例如. "react", (中文(简体) ), "preact"(中文(简体) ) |
define | Record<string, string> | {} | 在捆绑时间(服务器和客户端)应用的常数替换 |
loader | Record<string, BundlerLoader> | {} | 扩能加载器(如:. { ".svg": "text" }) (中文(简体) ). 见 高级捆绑选项 用于支持的集. |
conditions | string[] | 新建默认 | 决议期间的一揽子出口条件 |
virtualModules | Record<string, string> | {} | 由生成的 JavaScript 模块源支持的精确导入指定化名 |
__dangerouslyUseEsBuildPluginsDoNotUseOrYouWillBeFired | unknown[] | [] | 逃出舱门:外加出舱插件. 见 高级捆绑选项。 。 。 . |
返回所有从 createWorker 加:
{
assets: Record<string, string | ArrayBuffer>; // Combined assets for host-side serving
assetManifest: AssetManifest; // Metadata (content types, ETags) per asset
assetConfig?: AssetConfig; // The asset config used
clientBundles?: string[]; // Output paths of client bundles
}
handleAssetRequest(request, manifest, storage, config?)
独立的 Aync 资产请求处理器 。 该清单载有路由元数据(内容类型、ETags),而存储后端则按要求提供内容:
import { handleAssetRequest, buildAssets } from "@cloudflare/worker-bundler";
const { manifest, storage } = await buildAssets({
"/index.html": "<h1>Hello</h1>",
"/app.js": "console.log('hi')"
});
export default {
async fetch(request) {
const assetResponse = await handleAssetRequest(request, manifest, storage, {
not_found_handling: "single-page-application"
});
if (assetResponse) return assetResponse;
// Fall through to your API logic
return new Response("API");
}
};
回返a Promise<Response | null> a 包括: Response 如果资产匹配,或 null 掉下来.
存储钩
资产处理员离职 显示 (路径元数据) 存储 (内容检索). 这样可以插入任何后端.
AssetStorage 接口
interface AssetStorage {
get(pathname: string): Promise<ReadableStream | ArrayBuffer | string | null>;
}
内建:内存存储
最简单的选择——将一切存储在记忆中:
import {
buildAssetManifest,
createMemoryStorage
} from "@cloudflare/worker-bundler";
const assets = { "/index.html": "<h1>Hi</h1>" };
const manifest = await buildAssetManifest(assets);
const storage = createMemoryStorage(assets);
或使用同时返回的便利函数:
import { buildAssets } from "@cloudflare/worker-bundler";
const { manifest, storage } = await buildAssets({
"/index.html": "<h1>Hi</h1>"
});
自定义: KV 存储
import type { AssetStorage } from "@cloudflare/worker-bundler";
function createKVStorage(kv: KVNamespace): AssetStorage {
return {
async get(pathname) {
return kv.get(pathname, "arrayBuffer");
}
};
}
自定义: R2 存储
function createR2Storage(bucket: R2Bucket, prefix = "assets"): AssetStorage {
return {
async get(pathname) {
const obj = await bucket.get(`${prefix}${pathname}`);
return obj?.body ?? null;
}
};
}
自定义: 工作空间存储
import type { Workspace } from "@cloudflare/shell";
function createWorkspaceStorage(workspace: Workspace): AssetStorage {
return {
async get(pathname) {
return workspace.readFile(pathname);
}
};
}
所有存储后端都使用相同的功能 handleAssetRequest - 通过一个不同的 storage 参数.
资产配置
HTML 处理
控制如何解决 HTML 文件以及如何处理跟踪斜线 :
| 模式 | 行为 |
|---|---|
auto-trailing-slash | /about 服务 about.html· ; /blog/ 服务 blog/index.html |
force-trailing-slash | 重定向 /about → /about/,从 index.html |
drop-trailing-slash | 重定向 /about/ → /about,从 .html |
none | 仅匹配精确路径 |
默认 : auto-trailing-slash
非基金处理
| 模式 | 行为 |
|---|---|
single-page-application | 服务 /index.html 用于所有未匹配的路径 |
404-page | 服务于最近的 404.html 走进目录 |
none | 回返 null (通过你的工人) |
默认 : none
重定向
{
redirects: {
static: {
"/old-page": { status: 301, to: "/new-page" }
},
dynamic: {
"/blog/:slug": { status: 302, to: "/posts/:slug" },
"/docs/*": { status: 301, to: "/wiki/*" }
}
}
}
动态重定向支持 :placeholder 标志和 * 平板图案.
自定义信头
{
headers: {
"/*": {
set: { "X-Frame-Options": "DENY" }
},
"/api/*": {
set: { "Access-Control-Allow-Origin": "*" },
unset: ["X-Frame-Options"]
}
}
}
图案使用格子语法. 规则按顺序适用——以后的规则可以优先于更早的规则.
缓存
资产处理器自动设置缓存头 :
- HTML 和非阴影文件数字 :
public, max-age=0, must-revalidate - 隐藏文件 (例如:.
app.a1b2c3d4.js:public, max-age=31536000, immutable - ETag 支持: 有条件的请求
If-None-Match返回时304 Not Modified
出入口检测
优先权顺序 createWorker (和 createApp 服务器条目 :
- 明确
entryPoint编号 :server选项 main字段在 wrangler 配置中exports, (中文(简体) ),module,或main输入package.json- 默认路径 :
src/index.ts, (中文(简体) ),src/index.js, (中文(简体) ),index.ts, (中文(简体) ),index.js
挂载为永久对象
createApp 捆绑代码并收集资产——产出如何挂载是呼叫者的担忧. 如果用户代码导出一个 DurableObject 子类,装入 getDurableObjectClass 并把它挂起,作为常存的表面.
getDurableObjectClass(name) 决定 a 名称 类导出- 因此导出类与 export class App没有 export default class App。默认的入站槽无法包含一个演员类,且默认导出 DurableObject 当引用面值时,将显示为不透明的内部错误.
const result = await createApp({
files: {
"src/server.ts": `
import { DurableObject } from 'cloudflare:workers';
export class App extends DurableObject {
async fetch(request: Request) {
const url = new URL(request.url);
if (url.pathname === '/api/count') {
const count = ((await this.ctx.storage.get('count')) as number) ?? 0;
await this.ctx.storage.put('count', count + 1);
return Response.json({ count });
}
return new Response('Not found', { status: 404 });
}
}
`
},
assets: {
"/index.html": "<!DOCTYPE html><h1>Counter</h1>"
}
});
// Load the bundle and get the DO class
const worker = env.LOADER.get(loaderId, () => ({
mainModule: result.mainModule,
modules: result.modules,
compatibilityDate: "2026-06-11"
}));
// Mount as a facet for persistent storage
const facet = this.ctx.facets.get("app", () => ({
class: worker.getDurableObjectClass("App"),
id: "app"
}));
// Serve assets on the host, forward the rest to the facet
const storage = createMemoryStorage(result.assets);
const assetResponse = await handleAssetRequest(
request,
result.assetManifest,
storage,
result.assetConfig
);
if (assetResponse) return assetResponse;
return facet.fetch(request);
面部获得自己孤立的SQLite存储器,这些存储器独立于主机DO存储器而持续. 在保存存储时中止代码更改的面取新代码 :
this.ctx.facets.abort("app", new Error("Rebuilding"));
更多实例
多重依赖
const { mainModule, modules } = await createWorker({
files: {
"src/index.ts": `
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
const schema = z.object({ name: z.string() });
app.post('/greet', zValidator('json', schema), (c) => {
const { name } = c.req.valid('json');
return c.json({ message: \`Hello, \${name}!\` });
});
export default app;
`,
"package.json": JSON.stringify({
dependencies: {
hono: "^4.0.0",
"@hono/zod-validator": "^0.4.0",
zod: "^3.23.0"
}
})
}
});
与 Wrangler 配置
const { mainModule, modules, wranglerConfig } = await createWorker({
files: {
"src/index.ts": `
export default {
fetch: () => new Response('Hello!')
}
`,
"wrangler.toml": `
main = "src/index.ts"
compatibility_date = "2026-01-01"
compatibility_flags = ["nodejs_compat"]
`
}
});
const worker = env.LOADER.get("my-worker", () => ({
mainModule,
modules,
compatibilityDate: wranglerConfig?.compatibilityDate ?? "2026-01-01",
compatibilityFlags: wranglerConfig?.compatibilityFlags
}));
仅变形模式
跳过捆绑以保留模块结构 :
const { mainModule, modules } = await createWorker({
files: {
/* ... */
},
bundle: false
});
高级捆绑选项
createWorker 财务报告和审定财务报表 createApp 涵盖常见情况(目标、缩小、来源图、外部)。 以下选项是高级消费者的逃生舱口——RSC型变换,定制资产管道,图书馆捆绑.
本节所有选项都需要
bundle: true(默认). 仅变形模式( E)bundle: false) 跳过 esbuild 全部并使用 sulrase + naive 模块分辨率代替,所以define, (中文(简体) ),loader, (中文(简体) ),conditions,插件,和 JSX 选项在那里没有效果. 设置其中任何一种bundle: false添加一个警告到result.warnings而不是默默地失败.
jsx, (中文(简体) ), jsxImportSource
明确选择JSX运行时间. 与 jsx: "automatic" 您不需要在每个文件中导入 React(或任何运行时间) .
await createApp({
files,
client: "src/client.tsx",
jsx: "automatic",
jsxImportSource: "react"
});
define
编译时间常数替换. 每个值必须是一个有效的 JavaScript 表达式作为字符串(所以在多引号中将字符串值相接).
await createWorker({
files,
define: {
"process.env.NODE_ENV": '"production"',
__DEV__: "false",
__VERSION__: JSON.stringify(pkg.version)
}
});
loader
告诉捆绑者如何解释任意文件扩展名 。 内置处理 .ts 编号 : .tsx 编号 : .js 编号 : .jsx 编号 : .json 编号 : .css 保存,除非您超过它。 更长的扩展匹配,所以 ".d.ts" 胜过 ".ts"。 。 。 .
await createWorker({
files,
loader: {
".svg": "text",
".wasm": "binary",
".sql": "text"
}
});
受支助者 BundlerLoader 值被故意缩小到每个现代捆绑者可以表达的可移植子集:
| 装入器 | 含义 |
|---|---|
js | 普通 Java 脚本 |
jsx | JSX(按 jsx 选项) |
ts | 类型脚本 |
tsx | 类型脚本 + JSX |
json | 解析为 JSON, 曝光为默认导出 |
css | 解析为 CSS |
text | 嵌入文件内容为 UTF-8 字符串默认导出 |
binary | 嵌入文件内容为 Uint8Array 默认导出 |
base64 | 嵌入文件内容作为 Base64 字符串默认导出 |
dataurl | 嵌入文件内容为 data: URL 字符串默认导出 |
兴起的 file 编号 : copy 加载器是故意不暴露的:它们释放出这个捆绑器目前丢弃的二次输出文件(只有第一个输出被读取). 如果你需要这个——或者其他任何特定建设——的帮助 __dangerouslyUseEsBuildPluginsDoNotUseOrYouWillBeFired 换句话说.
conditions
包出口条件,按优先顺序排列. 当捆绑运送多重建造的图书馆时有用 package.json#exports。 。 。 .
await createWorker({
files,
conditions: ["workerd", "worker", "browser"]
});
virtualModules
别用精确的导入指定符来生成 JavaScript 模块 。 这个有用 当主机框架想要提供虚拟运行时模块而不曝光时 创建插件细节给呼叫者 .
const { mainModule, modules } = await createWorker({
files: {
"src/index.ts": `
import { readFileSync } from "node:fs";
export default {
fetch() {
return new Response(readFileSync("/message.txt", "utf8"));
}
};
`
},
virtualModules: {
"node:fs": `
const files = { "/message.txt": "hello from a virtual module" };
export function readFileSync(path) {
return files[path];
}
export default { readFileSync };
`,
fs: `export * from "node:fs"; export { default } from "node:fs";`
}
});
virtualModules 键是精确的比对。 如果你想要两者 "fs" 财务报告和审定财务报表
"node:fs" 工作,提供两个别名。 生成源与
其余的工人,因此可以通过入境点或过境进口
依赖性.
__dangerouslyUseEsBuildPluginsDoNotUseOrYouWillBeFired
在使用前读取这个. 此选项是 没有 由铸币局负责 这个名称是API合同:它可以改变形状,被更名,或者在任何发行中被删除. 运行时间将您连接到 esbuild —— 如果此软件包切换捆绑器( 如滚动) , 编写此 API 的插件将会被打破 .
通过额外的 Esbuild 插件 。 他们逃跑了 在此之前 捆绑器的内部虚拟文件系统插件,所以插件 onResolve 编号 : onLoad 声称先起火(例如RSC插件声称) "server-function:*" 在虚拟fs试图从磁盘读取之前).
此选项为 unknown[] 在公共边界,所以公布 .d.ts 不取决于 esbuild-wasm类型 传送时将插件阵列投影 :
import type { Plugin } from "esbuild-wasm";
const myRscPlugin: Plugin = {
name: "rsc",
setup(build) {
build.onResolve({ filter: /^server-function:/ }, (args) => ({
path: args.path,
namespace: "rsc"
}));
build.onLoad({ filter: /.*/, namespace: "rsc" }, (args) => ({
contents: generateServerFunctionStub(args.path),
loader: "ts"
}));
}
};
await createApp({
files,
client: "src/client.tsx",
__dangerouslyUseEsBuildPluginsDoNotUseOrYouWillBeFired: [
myRscPlugin
] as unknown[]
});
工人加载器设置
// wrangler.jsonc (host worker)
{
"worker_loaders": [{ "binding": "LOADER" }]
}
interface Env {
LOADER: WorkerLoader;
}
文件系统
捆绑人运行在一个 FileSystem 接口——用于读取、撰写和列出文件的最小同步抽象。 所有捆绑者 APIs (createWorker, (中文(简体) ), createApp(a) 接受一个平原 Record<string, string> (自动包装在 InMemoryFileSystem或任意 FileSystem 执行.
interface FileSystem {
read(path: string): string | null;
write(path: string, content: string): void;
delete(path: string): void;
list(prefix?: string): string[];
flush(): Promise<void>;
}
执行情况
InMemoryFileSystem —— 说 Map- 支持,没有坚持。 接受种子对象或 Map数字 :
import { InMemoryFileSystem } from "@cloudflare/worker-bundler";
const fs = new InMemoryFileSystem({
"src/index.ts": "export default { fetch() { return new Response('ok'); } };"
});
DurableObjectKVFileSystem ——写作在内存中被缓冲并冲入一批可持久对象KV. 见缓冲写道:
import { DurableObjectKVFileSystem } from "@cloudflare/worker-bundler";
const fs = new DurableObjectKVFileSystem(this.ctx.storage);
fs.write("src/index.ts", code);
const result = await createWorker({ files: fs });
// Persist everything (source + installed node_modules) to KV
await fs.flush();
冲洗后, 文件系统是可持久性的 createWorker 呼叫一个新的 DurableObjectKVFileSystem 在同一存储器上找到 node_modules 已全部显示并跳过安装 .
DurableObjectRawFileSystem ——每篇作文直接去KV,没有缓冲. 写出少数文件时使用, 其中每个字的耐久性优先 :
import { DurableObjectRawFileSystem } from "@cloudflare/worker-bundler";
const fs = new DurableObjectRawFileSystem(this.ctx.storage);
从同步源抓取
这个 FileSystem 接口是同步的,因为 esbuilding 和 TypeScript 需要同步文件访问。 从 Aync 存储后端桥接( 如 Workspace 从 @cloudflare/shell),使用时 createFileSystemSnapshot数字 :
import { createFileSystemSnapshot } from "@cloudflare/worker-bundler";
// Accepts any sync or async iterable of [path, content] pairs
async function* workspaceFiles(workspace) {
for (const entry of workspace.glob("**/*.{ts,tsx,json}")) {
const content = await workspace.readFile(entry.path);
if (content !== null) yield [entry.path, content];
}
}
const fs = await createFileSystemSnapshot(workspaceFiles(workspace));
const result = await createWorker({ files: fs });
总计前
installDependencies 单独导出, 这样您就可以将 npm 软件包与 npm 软件包分开预加温 createWorker数字 :
import {
installDependencies,
DurableObjectKVFileSystem,
createWorker
} from "@cloudflare/worker-bundler";
const fs = new DurableObjectKVFileSystem(this.ctx.storage);
fs.write("package.json", JSON.stringify({ dependencies: { hono: "^4.0.0" } }));
fs.write("src/index.ts", code);
// Install once, flush to KV
await installDependencies(fs);
await fs.flush();
// On subsequent requests, createWorker finds node_modules already
// present and skips installation — no network requests
const result = await createWorker({ files: fs });
这对于将安装步骤(慢,网络绑定)从捆绑步骤(快,CPU-只)中分离出来,以及对于贯穿DO重启的持续安装软件包都是有益的.
语言服务
一个完整的TypeScript语言服务在工人运行时间内部运行。 从中导入 ./typescript 保存主条目 TypeScript 捆绑(~5 MB) 的子路径 :
import { createTypescriptLanguageService } from "@cloudflare/worker-bundler/typescript";
import { InMemoryFileSystem } from "@cloudflare/worker-bundler";
const fileSystem = new InMemoryFileSystem({
"tsconfig.json": JSON.stringify({
compilerOptions: {
lib: ["es2024"],
target: "ES2024",
module: "ES2022",
moduleResolution: "bundler",
strict: true,
skipLibCheck: true
}
}),
"src/index.ts": `
const worker: ExportedHandler = {
async fetch() {
return new Response("Hello, world!");
}
};
export default worker;
`
});
const { fileSystem: tsFileSystem, languageService } =
await createTypescriptLanguageService({ fileSystem });
// Get diagnostics
const errors = languageService.getSemanticDiagnostics("src/index.ts");
回来的 fileSystem 将原始内容包裹并保持 TypeScript 环境同步——通过它写入以立即看到更新的诊断:
// Fix an error
tsFileSystem.write("src/index.ts", correctedCode);
// Diagnostics now reflect the fix
const errors = languageService.getSemanticDiagnostics("src/index.ts");
// => []
与 installDependencies
在创建语言服务之前, 在文件系统中安装类型包以获得全类型覆盖 :
import {
installDependencies,
InMemoryFileSystem
} from "@cloudflare/worker-bundler";
import { createTypescriptLanguageService } from "@cloudflare/worker-bundler/typescript";
const fs = new InMemoryFileSystem({
"package.json": JSON.stringify({
dependencies: { "@cloudflare/workers-types": "^4.20260405.1" }
}),
"tsconfig.json": JSON.stringify({
compilerOptions: {
lib: ["es2024"],
strict: true,
skipLibCheck: true,
types: ["@cloudflare/workers-types/index.d.ts"]
}
}),
"src/index.ts": workerSource
});
await installDependencies(fs);
const { languageService } = await createTypescriptLanguageService({
fileSystem: fs
});
const diagnostics = languageService.getSemanticDiagnostics("src/index.ts");
与工作空间融合
使用时 Workspace 从 @cloudflare/shell 作为持久存储,将源文件抓取到 InMemoryFileSystem 用于类型检查 :
import { createFileSystemSnapshot } from "@cloudflare/worker-bundler";
import { createTypescriptLanguageService } from "@cloudflare/worker-bundler/typescript";
// 1. Snapshot source files from Workspace (async reads)
async function* sourceFiles(workspace) {
for (const entry of workspace.glob("**/*.{ts,tsx,json}")) {
const content = await workspace.readFile(entry.path);
if (content !== null) yield [entry.path, content];
}
}
const fs = await createFileSystemSnapshot(sourceFiles(workspace));
// 2. Create language service (synchronous reads during init)
const { fileSystem, languageService } = await createTypescriptLanguageService({
fileSystem: fs
});
// 3. Get diagnostics
const errors = languageService.getSemanticDiagnostics("src/index.ts");
// 4. On edits, update both the TS environment and durable storage
fileSystem.write("src/index.ts", newContent);
await workspace.writeFile("src/index.ts", newContent);
已知的限制
- 平分节点 模块 ——所有包被安装在一个单一的平面目录中. 如果两个包需要不同不相容的版本相同的过渡依赖,则只安装了一个版本.
- 以 tarball 为单位的文件路径长 ——焦油解析器处理经典头头,但不处理POSIX扩展头. 路径超过100个字符的软件包可能默默地截取了这些文件.
- 仅来自 npm 的文本文件 ——只有文本文件(
.js, (中文(简体) ),.json, (中文(简体) ),.css)取自柏油丸. 二进制文件.wasm或者说.node被跳过. - 无重复深度限制 ——过渡性依赖性解决无深度限制. 病理依赖树可能造成过多的网络请求.
- 运行时获取的脚本 lib 声明 —— 说
createTypescriptLanguageService下载每个调用中的 TypeScript npm tarball (~23 MB) 以提取标准库.d.ts文件(~3.7 MB). 没有缓存——每个创建语言服务的持久对象的冷起步都会产生这种取回。 未来的改进将在构建时预先将这些声明捆绑起来(遵循与TypeScript编译器捆绑相同的模式).
许可证
麻省理工学院