Skip to content

Produce valid file URLs for import.meta.url on Windows in Turbopack#94179

Merged
sokra merged 2 commits into
vercel:canaryfrom
sokra:sokra/file-url-windows
May 29, 2026
Merged

Produce valid file URLs for import.meta.url on Windows in Turbopack#94179
sokra merged 2 commits into
vercel:canaryfrom
sokra:sokra/file-url-windows

Conversation

@sokra
Copy link
Copy Markdown
Member

@sokra sokra commented May 28, 2026

What?

import.meta.url evaluated in a Turbopack-compiled module on Windows produced
an invalid file URI: the path portion contained backslashes (\) and was not
URL-encoded, e.g.

file://C:\Users\me\project\apps\web\app\page.tsx

This affects any code that hands the value to URL, fileURLToPath, source
maps, dev tools, or anything else that expects a valid file URI.

This PR makes import.meta.url return a correct, percent-encoded file URI on
all platforms, e.g. file:///C:/Users/me/project/apps/web/app/page.tsx.

Why?

The existing codegen in references/esm/meta.rs emitted

`file://${__turbopack_context__.P("<rel/path>")}`

where __turbopack_context__.P is the runtime helper resolveAbsolutePath.
On the Node-side runtime (shared-node/node-externals-utils.ts) that helper
returns path.join(ABSOLUTE_ROOT, modulePath), which is OS-native — so on
Windows it returns a backslash-separated path with no percent-encoding. The
result was then concatenated into a file://... template literal, producing
an invalid URI.

The initial hint that turbopack-core/src/source_map/utils.rs was the culprit
turned out to be a false lead: that code path uses
FileSystemPath::try_join, which already normalizes \ to / on Windows.
The bug is in the codegen + runtime helper for import.meta.url, not in
source-map handling.

How?

  • Introduce a new runtime function __turbopack_resolve_file_url__
    (shortcut F) whose contract is to return a complete file:// URI for a
    given relative module path.
    • Node implementation (shared-node/node-externals-utils.ts): uses
      url.pathToFileURL(resolveAbsolutePath(modulePath)).href. pathToFileURL
      handles drive letters (file:///C:/...), normalizes slashes, and
      percent-encodes path segments.
    • Browser implementation (browser/runtime/base/runtime-base.ts): returns
      `file:///ROOT/${modulePath ?? ''}` — the browser runtime
      intentionally does not expose the real filesystem path, so it returns the
      same stable placeholder as before, just with a valid file:/// prefix
      instead of having callers concatenate one.
  • Change the import.meta.url codegen in references/esm/meta.rs to call
    __turbopack_resolve_file_url__($formatted) directly instead of
    concatenating a file:// prefix to the absolute path. The pre-existing
    encode_path is kept only to keep the embedded JS string literal safe
    (the runtime helper is responsible for the URI-level encoding).
  • Update TurbopackBaseContext and add a ResolveFileUrl type alias in
    runtime-types.d.ts so the new shortcut is typed.
  • Register the new shortcut in TURBOPACK_RUNTIME_FUNCTION_SHORTCUTS so user
    code can also reference __turbopack_resolve_file_url__ (mirroring the
    existing __turbopack_resolve_absolute_path__). P is left in place and
    unchanged because it's still used elsewhere.
  • Regenerate the affected Turbopack snapshot fixtures (import-meta/*,
    comptime/typeof, runtime/default_*_runtime, workers/*, debug-ids/*,
    etc.). The functional change in the generated code is uniform:
    `file://${__turbopack_context__.P(<rel>)}`
    __turbopack_context__.F(<rel>).
  • Drop the // TODO: These file URIs are wrong on turbopack+windows comment
    and the .replaceAll('\\', '/') workarounds from
    test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts,
    so the assertion now verifies that the URI is correctly slashed and encoded
    on all platforms.

Verification

  • cargo clippy --workspace --all-targets — clean
  • cargo fmt --check — clean
  • cargo test -p turbopack-tests --test snapshot — 87/87 pass
  • cargo test -p turbopack-tests --test execution — 219/219 pass (1 ignored)
  • pnpm test-dev-turbo test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts — 9/9 pass
  • pnpm test-start-turbo test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts — 6/6 pass
  • pnpm test-dev-webpack test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts — 9/9 pass (verifying webpack path still works)

Windows itself can't be exercised in the sandbox, but the new code path no
longer depends on path.sep or any OS-native string manipulation for the
URI: url.pathToFileURL is the canonical Node API for producing a file URI
from any OS-native path, and the test assertion has been tightened so that a
regression on Windows would fail in CI rather than be papered over.

`import.meta.url` was emitted as `` `file://${__turbopack_context__.P(...)}` ``,
where `P` (`resolveAbsolutePath`) returned an OS-native absolute path. On
Windows that contained backslashes and was not URL-encoded, producing an
invalid file URI.

Introduce a new runtime helper `F` (`__turbopack_resolve_file_url__`) that
delegates to `url.pathToFileURL` in Node (handles drive letters, slash
normalization, and percent-encoding) and returns a stable
`file:///ROOT/...` placeholder in the browser runtime. The `import.meta.url`
codegen now calls this helper directly instead of concatenating a
`file://` prefix to the absolute path.

Drop the `.replaceAll('\\', '/')` workaround and the Windows TODO from the
`non-root-project-monorepo` e2e test.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Tobias Koppers <sokra@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

Tests Passed

Commit: 77ecc73

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

Stats skipped

Commit: 77ecc73
View workflow run

@sokra sokra marked this pull request as ready for review May 28, 2026 01:45
@sokra sokra requested review from bgw and lukesandberg May 28, 2026 01:45
Comment thread turbopack/crates/turbopack-ecmascript/src/references/esm/meta.rs
- Percent-encode path segments in the browser runtime's `resolveFileUrl`
  so the placeholder URI is a valid file URI even when the module path
  contains non-URL-safe characters.
- Add fixture pages whose source file (`with space.ts`) contains a space
  in the filename, and assert that the resulting `import.meta.url`
  contains `with%20space.ts` for RSC, SSR, and client-side.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Tobias Koppers <sokra@users.noreply.github.com>
@sokra sokra enabled auto-merge (squash) May 29, 2026 07:27
@sokra sokra merged commit 859c4ed into vercel:canary May 29, 2026
282 of 287 checks passed
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.

3 participants