-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[wrangler] Fix named-only module Worker format detection #15518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| "wrangler": patch | ||
| --- | ||
|
|
||
| Detect named-only module Worker entrypoints correctly | ||
|
|
||
| Wrangler now distinguishes named-only module Workers from legacy Service Workers that happen to have named exports. A default export identifies a module Worker; otherwise, legacy `addEventListener` registration identifies Service Worker format. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,13 +5,22 @@ import { COMMON_ESBUILD_OPTIONS } from "./bundle"; | |
| import { getEntryPointFromMetafile } from "./entry-point-from-metafile"; | ||
| import type { CfScriptFormat } from "@cloudflare/workers-utils"; | ||
|
|
||
| const SERVICE_WORKER_EVENT_LISTENER = | ||
| "__WRANGLER_SERVICE_WORKER_EVENT_LISTENER__"; | ||
|
|
||
| /** | ||
| * A function to "guess" the type of worker. | ||
| * We do this by running a lightweight build of the actual script, | ||
| * and looking at the meta-file generated by esbuild. If it has a default | ||
| * export (or really, any exports), that means it's a "modules" worker. | ||
| * Else, it's a "service-worker" worker. This seems hacky, but works remarkably | ||
| * well in practice. | ||
| * Guesses the Worker format by running a lightweight build and inspecting its | ||
| * generated exports. For JavaScript and TypeScript entrypoints, the heuristic | ||
| * is: | ||
| * | ||
| * - No exports: Service Worker, regardless of event listener syntax. | ||
| * - A default export: Module Worker. | ||
| * - Named-only exports are ambiguous: | ||
| * - A recognized global `addEventListener` reference: Service Worker. | ||
| * - No recognized global `addEventListener` reference: Module Worker. | ||
| * | ||
| * An `addEventListener` reference does not necessarily need to be a call that | ||
| * registers an event listener. | ||
| */ | ||
| export async function guessWorkerFormat( | ||
| entryFile: string, | ||
|
|
@@ -23,6 +32,9 @@ export async function guessWorkerFormat( | |
| return { format: "modules", exports: [] }; | ||
| } | ||
|
|
||
| // Let esbuild mark references to the global binding so strings, comments, and | ||
| // locally shadowed functions named `addEventListener` aren't false positives. | ||
| // The marker identifies references, without determining how they are used. | ||
| const result = await esbuild.build({ | ||
| ...COMMON_ESBUILD_OPTIONS, | ||
| entryPoints: [entryFile], | ||
|
|
@@ -31,28 +43,37 @@ export async function guessWorkerFormat( | |
| bundle: false, | ||
| write: false, | ||
| ...(tsconfig && { tsconfig }), | ||
| define: { | ||
| addEventListener: SERVICE_WORKER_EVENT_LISTENER, | ||
| "globalThis.addEventListener": SERVICE_WORKER_EVENT_LISTENER, | ||
| "self.addEventListener": SERVICE_WORKER_EVENT_LISTENER, | ||
| }, | ||
| logLevel: "silent", | ||
| }); | ||
|
|
||
| // result.metafile is defined because of the `metafile: true` option above. | ||
| const metafile = result.metafile; | ||
|
|
||
| const { exports } = getEntryPointFromMetafile(entryFile, metafile); | ||
| const usesServiceWorkerEventListener = result.outputFiles.some(({ text }) => | ||
| text.includes(SERVICE_WORKER_EVENT_LISTENER) | ||
| ); | ||
|
Comment on lines
+58
to
+60
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Marker collisions select legacy format A named-only module containing Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is fine. This isn't a security measure, it's a heuristic to attempt to reduce misclassifications. |
||
|
|
||
| let guessedWorkerFormat: CfScriptFormat; | ||
| if (exports.length > 0) { | ||
| if (exports.includes("default")) { | ||
| guessedWorkerFormat = "modules"; | ||
| } else { | ||
| logger.warn( | ||
| `The entrypoint ${path.relative( | ||
| process.cwd(), | ||
| entryFile | ||
| )} has exports like an ES Module, but hasn't defined a default export like a module worker normally would. Building the worker using "service-worker" format...` | ||
| ); | ||
| guessedWorkerFormat = "service-worker"; | ||
| } | ||
| } else { | ||
| if (exports.length === 0) { | ||
| guessedWorkerFormat = "service-worker"; | ||
| } else if (exports.includes("default")) { | ||
| guessedWorkerFormat = "modules"; | ||
| } else if (usesServiceWorkerEventListener) { | ||
| logger.warn( | ||
| `The entrypoint ${path.relative( | ||
| process.cwd(), | ||
| entryFile | ||
| )} has exports like an ES Module, but hasn't defined a default export like a module worker normally would. Building the worker using "service-worker" format...` | ||
| ); | ||
| guessedWorkerFormat = "service-worker"; | ||
| } else { | ||
| guessedWorkerFormat = "modules"; | ||
| } | ||
|
|
||
| return { format: guessedWorkerFormat, exports }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Imported listeners select module format
A named-export Worker can import its
addEventListenerregistration from another file. The unbundled detection output omits that listener and selects module format.Prompt for agents
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is an intentional and accepted limitation.