Skip to content

fix(workerd): guard fileURLToPath(import.meta.url) for bundled modules - #2910

Open
JamesbbBriz wants to merge 5 commits into
cloudflare:mainfrom
JamesbbBriz:fix/workerd-import-meta-url-guard
Open

fix(workerd): guard fileURLToPath(import.meta.url) for bundled modules#2910
JamesbbBriz wants to merge 5 commits into
cloudflare:mainfrom
JamesbbBriz:fix/workerd-import-meta-url-guard

Conversation

@JamesbbBriz

@JamesbbBriz JamesbbBriz commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

What & why

While dogfooding a CMS on vinext + Cloudflare Workers, we hit a hard crash: workerd exposes import.meta.url as the non-file sentinel worker for bundled modules. Libraries that resolve paths from it at module init therefore throw in fileURLToPath(import.meta.url) or createRequire(import.meta.url) and fail to load.

Node receives a normal file: URL, which is why this is workerd-specific and worth handling centrally instead of patching applications one by one.

What it does

  • Adds a server-only plugin guarding the two crashing direct call shapes: fileURLToPath(import.meta.url) and createRequire(import.meta.url).
  • Rewrites the argument to a file-scheme check that preserves valid file: URLs and falls back to file:/// for workerd''s worker sentinel or a missing value.
  • Excludes client environments so browser import.meta.url semantics remain untouched.
  • Keeps the AST transform authoritative while the production filter accepts arbitrary comments and whitespace around the call and member access.
  • Remains narrow by design: no binding analysis; unrelated import.meta.url usage is untouched.

Next.js research and deliberate divergence

Searched Next.js canary under packages/next/src, test/e2e, and test/unit. The tree contains Node-oriented direct usages such as module.createRequire(import.meta.url) and fixtures using fileURLToPath(import.meta.url), but no workerd guard or equivalent transform. This is an intentional Cloudflare/workerd-specific divergence: vinext must keep bundled module initialization alive when workerd supplies a non-file module identifier.

Verification

  • vp test run tests/workerd-import-meta-url-guard.test.ts tests/workerd-import-meta-url-build.test.ts: 24 passed.
  • Required deploy/build files: 467 passed; one unrelated Windows host symlink test could not run because the host denies symlink creation (EPERM). Ubuntu CI is running the authoritative matrix.
  • vp check on all changed files and git diff --check pass.
  • Regression coverage executes both transformed Node helper calls with import.meta.url === worker, verifies the fallback, preserves a normal file: URL, and verifies client exclusion.
  • The real production build fixture contains the guard and no bare vulnerable call.

Known boundaries

  • Matching is by callee name; a server module shadowing fileURLToPath gets the same rewrite.
  • TS postfix forms (import.meta.url as string) and computed access are intentionally out of scope.
  • The file:/// fallback is only meaningful under workerd''s POSIX file URL handling.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 544edb2eae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/plugins/workerd-import-meta-url-guard.ts Outdated
@JamesbbBriz
JamesbbBriz force-pushed the fix/workerd-import-meta-url-guard branch from 5d9b461 to 72ffa69 Compare August 14, 2026 15:39
@JamesbbBriz

Copy link
Copy Markdown
Contributor Author

@james-elicx I rebased this onto the latest main, resolved the index.ts conflict, and updated the build integration coverage for the current split/minified server output. The Codex P2 feedback remains addressed; targeted checks pass locally (18/18). Could you please kick off a Big Bonk review when you have a moment? Thanks!

@pkg-pr-new

pkg-pr-new Bot commented Aug 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2910
npm i https://pkg.pr.new/create-vinext-app@2910
npm i https://pkg.pr.new/@vinext/types@2910
npm i https://pkg.pr.new/vinext@2910

commit: b118653

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared b118653 against base 3ec5bdc using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 135.4 KB 135.4 KB ⚫ 0.0%
Client entry size (gzip) vinext 122.8 KB 122.8 KB ⚫ +0.0%
Dev server cold start vinext 2.80 s 2.76 s ⚫ -1.4%
Production build time vinext 2.86 s 2.84 s ⚫ -0.6%
RSC entry closure size (gzip) vinext 114.9 KB 114.9 KB ⚫ -0.0%
Server bundle size (gzip) vinext 195.2 KB 195.2 KB ⚫ +0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@JamesbbBriz

Copy link
Copy Markdown
Contributor Author

/bigbonk

@JamesbbBriz

Copy link
Copy Markdown
Contributor Author

CI follow-up: the failed prefetch test assumed async RSC URL generation completed in call order, so it always treated dashboard-4 as the queued request. The new regression deliberately completes the first digest last, identifies the actual queued cache entry from the four issued request URLs, and still asserts navigation promotes exactly that fifth request. Local verification: deterministic old 4-vs-5 failure, focused test 10/10 repeated, full prefetch-cache file 76/76, and scoped check/diff-check pass. Waiting for CI; not requesting re-review yet.

workerd does not provide import.meta.url for bundled modules (it is
undefined), so libraries that resolve paths from it at module init —
e.g. findUp-style package.json discovery — crash with "The path argument
must be of type string or an instance of URL". Node never sees this
(import.meta.url is always defined there).

Adds a narrow transform plugin that guards only the two crashing call
shapes — fileURLToPath(import.meta.url) and createRequire(import.meta.url)
— rewriting the argument to `import.meta.url ?? "file:///"`. All other
import.meta.url usage is untouched, and the guard is a no-op on Node.

Verified under local workerd (same binary as real Workers): unguarded
fileURLToPath(undefined) crashes module init; guarded, fileURLToPath("file:///")
yields "/" and findUp terminates safely. End-to-end: a full CMS stack
(Payload) passes its 25-check API suite with the app-level workaround
removed — 7 guard sites applied in the bundle, 0 missed.
Adversarial verification found the bounded 80-char window between the
callee and the argument silently misses calls separated by long block
comments (3+ lines), leaving them unguarded in real builds. Widen the
filter to an unbounded window — the AST handler remains authoritative,
so the cost is only an extra parseAst on false positives.

Adds a filter-level regression test for long-comment forms.
… guard

Adds a fixture with a dependency that calls fileURLToPath(import.meta.url)
behind a long block comment (the window-escape shape), and a build-level
test asserting the guard lands in the emitted server bundle while no bare
call remains. Covers the filter+handler pipeline end to end — the unit
suite alone bypasses the filter gate.
@JamesbbBriz
JamesbbBriz force-pushed the fix/workerd-import-meta-url-guard branch from 8c2377f to 008f7a9 Compare August 15, 2026 03:33
@JamesbbBriz

Copy link
Copy Markdown
Contributor Author

Rebase update: latest main now contains the authoritative queued-prefetch stabilization from #2920 (�dbb322f), which fixes the same async setup-order assumption. During rebase our duplicate local test commit conflicted with that upstream fix, so I dropped the duplicate and kept #2920's simpler four-slots-first regression. Rebased #2910 head verification: queued promotion target passes; workerd guard/build tests 20/20; deploy/build matrix 467 pass with the single existing Windows symlink EPERM host limitation; scoped checks and diff-check pass. CI should now run from the conflict-free head. No re-review request yet.

@JamesbbBriz

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 008f7a9e43

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/plugins/workerd-import-meta-url-guard.ts Outdated
@JamesbbBriz

JamesbbBriz commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

@james-elicx I think this is ready for another look now. I fixed the workerd issue from the latest review, and CI is green. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant