[wrangler] Fix named-only module Worker format detection - #15518
[wrangler] Fix named-only module Worker format detection#15518taylorlee wants to merge 1 commit into
Conversation
🦋 Changeset detectedLatest commit: 84f7e87 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
| bundle: false, | ||
| write: false, | ||
| ...(tsconfig && { tsconfig }), | ||
| define: { addEventListener: SERVICE_WORKER_EVENT_LISTENER }, |
There was a problem hiding this comment.
define only rewrites the bare identifier. self.addEventListener(...) is a standard legacy Service Worker handler form, so a Worker with named exports using that form now has no marker and is incorrectly uploaded as a modules Worker. Mark the global property forms as well.
| define: { addEventListener: SERVICE_WORKER_EVENT_LISTENER }, | |
| define: { | |
| addEventListener: SERVICE_WORKER_EVENT_LISTENER, | |
| "globalThis.addEventListener": SERVICE_WORKER_EVENT_LISTENER, | |
| "self.addEventListener": SERVICE_WORKER_EVENT_LISTENER, | |
| }, |
There was a problem hiding this comment.
Good point. Also a good opportunity to clarify which types of dynamic registrations we're actually trying to catch here. Fixed.
|
I'm Bonk, and I've done a quick review of your PR. Updates Worker-format detection for named-only module entrypoints.
|
| @@ -31,28 +35,33 @@ export async function guessWorkerFormat( | |||
| bundle: false, | |||
There was a problem hiding this comment.
🔴 Imported listeners select module format
A named-export Worker can import its addEventListener registration from another file. The unbundled detection output omits that listener and selects module format.
Prompt for agents
guessWorkerFormat builds only the entry file with bundle:false, then searches its emitted text for the listener marker. Wrangler otherwise supports bundling legacy Service Workers whose event registration lives in an imported module. For a named-export entrypoint importing such a module, the marker never appears and the new fallback selects module format. Detect listener registration across the dependency graph without changing normal module resolution behavior, and cover a named-export entrypoint with an imported listener.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
That is an intentional and accepted limitation.
| const usesServiceWorkerEventListener = result.outputFiles.some(({ text }) => | ||
| text.includes(SERVICE_WORKER_EVENT_LISTENER) | ||
| ); |
There was a problem hiding this comment.
🟡 Marker collisions select legacy format
A named-only module containing __WRANGLER_SERVICE_WORKER_EVENT_LISTENER__ anywhere in emitted text passes the marker check. Wrangler builds it as a legacy Worker.
Prompt for agents
The listener check uses text.includes() with a fixed valid identifier. User source can independently emit the same identifier or string, so named-only module Workers can become false positives. Replace textual sentinel detection with collision-resistant structural metadata or another mechanism that distinguishes esbuild's replacement from user-authored output, and add a collision test.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
That is fine. This isn't a security measure, it's a heuristic to attempt to reduce misclassifications.
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/codemods
@cloudflare/config
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-plugin
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
Fixes #15309. The legacy heuristic of assuming anything without a default export is going more harm than good. It also seems like it must have regressed at some point, because it used to be possible to have DO-class-only Workers a [few years ago](cloudflare/cloudflare-docs#5185), even though trying that now would fail by detecting SW syntax. After looking at a bunch of options and various forms of required back-compat, I think the best path forward is to resolve the ambigious case in favor of modules workers. Current heuristic: * default export -> module worker * otherwise -> service worker New heuristic: * no exports -> service worker * default export -> module worker * ambigious exports: - recognized global addEventListener reference -> service worker - otherwise -> module worker This does that the most tricky dynamic constructions of SW can be falsely interpreted as module workers if they have some named exports. I think that's acceptable for a 2 reasons: 1. it is less harmful than misclassifying MW as SW 2. many of the most dynamic constructions (of either MW or SW) can't actually be deployed in practice (due to preexisting validator limitations), so the real breakage is minimal. Alternatives considered: export-only heuristics, source regexes, entrypoint-specific AST matching, an additional parser, and full dependency bundling. These were avoided due to compatibility risks, false positives, added complexity, or resolution overhead.
ca32d10 to
84f7e87
Compare
Fixes #15309.
The legacy heuristic of assuming anything without a default export is
going more harm than good. It also seems like it must have regressed at
some point, because it used to be possible to have DO-class-only
Workers a few years ago,
even though trying that now would fail by detecting SW syntax.
After looking at a bunch of options and various forms of required
back-compat, I think the best path forward is to resolve the ambigious
case in favor of modules workers.
Current heuristic:
New heuristic:
This does that the most tricky dynamic constructions of SW can be
falsely interpreted as module workers if they have some named exports. I
think that's acceptable for a 2 reasons:
actually be deployed in practice (due to preexisting validator limitations),
so the real breakage is minimal.
Alternatives considered: export-only heuristics, source regexes,
entrypoint-specific AST matching, an additional parser, and full dependency
bundling. These were avoided due to compatibility risks, false positives,
added complexity, or resolution overhead.
A picture of a cute animal (not mandatory, but encouraged)