Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit ddc45f3

Browse files
committed
Add support for modules option
- If set to `true`, JavaScript is parsed and modules are collected automatically. Note if the specifier to a dynamic `import()` or `require()` is not a string literal, an exception will be raised. - If set to an array, modules can be defined manually. This is intended for use by Wrangler (which has its own module collection logic) and end users needing dynamic specifiers. - Otherwise, the script is assumed to be a service worker.
1 parent eb101ae commit ddc45f3

File tree

4 files changed

+364
-32
lines changed

4 files changed

+364
-32
lines changed

packages/tre/src/helpers.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ export class MiniflareError<
4040
}
4141
}
4242

43+
export type MiniflareCoreErrorCode =
44+
| "ERR_RUNTIME_UNSUPPORTED" // System doesn't support Cloudflare Workers runtime
45+
| "ERR_DISPOSED" // Attempted to use Miniflare instance after calling dispose()
46+
| "ERR_MODULE_PARSE" // SyntaxError when attempting to parse/locate modules
47+
| "ERR_MODULE_STRING_SCRIPT" // Attempt to resolve module within string script
48+
| "ERR_MODULE_DYNAMIC_SPEC" // Attempted to import/require a module without a literal spec
49+
| "ERR_MODULE_RULE"; // No matching module rule for file
50+
export class MiniflareCoreError extends MiniflareError<MiniflareCoreErrorCode> {}
51+
4352
export class HttpError extends MiniflareError<number> {
4453
constructor(code: number, message?: string, cause?: Error) {
4554
super(code, message, cause);

packages/tre/src/index.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import assert from "assert";
22
import http from "http";
33
import path from "path";
4-
import util from "util";
54
import getPort from "get-port";
6-
import { bold, dim, green, grey } from "kleur/colors";
5+
import { bold, green, grey } from "kleur/colors";
76
import stoppable from "stoppable";
87
import { HeadersInit, Request, Response } from "undici";
98
import { z } from "zod";
109
import {
1110
DeferredPromise,
1211
HttpError,
13-
MiniflareError,
12+
MiniflareCoreError,
1413
OptionalZodTypeOf,
1514
UnionToIntersection,
1615
ValueOf,
@@ -257,9 +256,6 @@ export class Miniflare {
257256
body: req.method === "GET" || req.method === "HEAD" ? undefined : req,
258257
});
259258

260-
console.log(request.method, req.url);
261-
console.log(await request.clone().text());
262-
263259
let response: Response | undefined;
264260
const customService = request.headers.get(HEADER_CUSTOM_SERVICE);
265261
if (customService !== null) {
@@ -359,15 +355,7 @@ export class Miniflare {
359355
}
360356
}
361357

362-
const config: Config = { services, sockets };
363-
// const inspectedConfig = util.inspect(config, {
364-
// depth: null,
365-
// colors: true,
366-
// maxStringLength: 50,
367-
// });
368-
// console.log(grey("Assembled config:"), dim(inspectedConfig));
369-
370-
return config;
358+
return { services, sockets };
371359
}
372360

373361
get ready() {
@@ -376,7 +364,10 @@ export class Miniflare {
376364

377365
#checkDisposed() {
378366
if (this.#disposeController.signal.aborted) {
379-
throw new MiniflareError("ERR_DISPOSED", "Cannot use disposed instance");
367+
throw new MiniflareCoreError(
368+
"ERR_DISPOSED",
369+
"Cannot use disposed instance"
370+
);
380371
}
381372
}
382373

packages/tre/src/plugins/core/index.ts

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1+
import { readFileSync } from "fs";
12
import fs from "fs/promises";
23
import { Request, Response } from "undici";
34
import { z } from "zod";
45
import { Awaitable, JsonSchema } from "../../helpers";
5-
import { Service, Worker, Worker_Binding } from "../../runtime";
6+
import { Service, Worker_Binding, Worker_Module } from "../../runtime";
67
import { BINDING_SERVICE_LOOPBACK, Plugin } from "../shared";
8+
import {
9+
ModuleDefinitionSchema,
10+
ModuleLocator,
11+
ModuleRuleSchema,
12+
STRING_SCRIPT_PATH,
13+
convertModuleDefinition,
14+
} from "./modules";
715

816
// (request: Request) => Awaitable<Response>
917
export const ServiceFetch = z
@@ -15,7 +23,17 @@ export const CoreOptionsSchema = z.object({
1523
name: z.string().optional(),
1624
script: z.string().optional(),
1725
scriptPath: z.string().optional(),
18-
modules: z.boolean().optional(),
26+
modules: z
27+
.union([
28+
// Automatically collect modules by parsing `script`/`scriptPath`...
29+
z.boolean(),
30+
// ...or manually define modules
31+
// (used by Wrangler which has its own module collection code)
32+
z.array(ModuleDefinitionSchema),
33+
])
34+
.optional(),
35+
modulesRules: z.array(ModuleRuleSchema).optional(),
36+
1937
compatibilityDate: z.string().optional(),
2038
compatibilityFlags: z.string().array().optional(),
2139

@@ -144,20 +162,7 @@ export const CORE_PLUGIN: Plugin<
144162
];
145163

146164
// Define regular user worker if script is set
147-
let workerScript: Partial<Worker> | undefined;
148-
if (options.script !== undefined) {
149-
workerScript = options.modules
150-
? { modules: [{ name: "<script>", esModule: options.script }] }
151-
: { serviceWorkerScript: options.script };
152-
} else if (options.scriptPath !== undefined) {
153-
if (options.modules) {
154-
// TODO: collect modules
155-
} else {
156-
const script = await fs.readFile(options.scriptPath, "utf8");
157-
workerScript = { serviceWorkerScript: script };
158-
}
159-
}
160-
165+
const workerScript = getWorkerScript(options);
161166
if (workerScript !== undefined) {
162167
const name = `${SERVICE_USER_PREFIX}:${options.name ?? ""}`;
163168
services.push({
@@ -202,3 +207,37 @@ export const CORE_PLUGIN: Plugin<
202207
return services;
203208
},
204209
};
210+
211+
function getWorkerScript(
212+
options: z.infer<typeof CoreOptionsSchema>
213+
): { serviceWorkerScript: string } | { modules: Worker_Module[] } | undefined {
214+
if (Array.isArray(options.modules)) {
215+
// If `modules` is a manually defined modules array, use that
216+
return { modules: options.modules.map(convertModuleDefinition) };
217+
}
218+
219+
// Otherwise get code, preferring string `script` over `scriptPath`
220+
let code;
221+
if (options.script !== undefined) {
222+
code = options.script;
223+
} else if (options.scriptPath !== undefined) {
224+
code = readFileSync(options.scriptPath, "utf8");
225+
} else {
226+
// If neither `script`, `scriptPath` nor `modules` is defined, this worker
227+
// doesn't have any code
228+
return;
229+
}
230+
231+
if (options.modules) {
232+
// If `modules` is `true`, automatically collect modules...
233+
const locator = new ModuleLocator(options.modulesRules);
234+
// If `script` and `scriptPath` are set, resolve modules in `script`
235+
// against `scriptPath`.
236+
locator.visitEntrypoint(code, options.scriptPath ?? STRING_SCRIPT_PATH);
237+
return { modules: locator.modules };
238+
} else {
239+
// ...otherwise, `modules` will either be `false` or `undefined`, so treat
240+
// `code` as a service worker
241+
return { serviceWorkerScript: code };
242+
}
243+
}

0 commit comments

Comments
 (0)