Skip to content

[Bug]: dev mode — new URL('./asset', import.meta.url) not resolved in HMR/lazy patches (404 until full reload) #9817

Description

@h-a-n-a

Describe the bug

In dev mode (full-bundle / experimental.devMode), an asset referenced via new URL('./asset', import.meta.url) is not resolved when the referencing module is delivered through an HMR patch or a lazy-compilation chunk. The patch ships the raw specifier, so the browser resolves it against the patch's own URL and 404s. It only works after a full page reload (which serves the fully-generated bundle).

This is a sibling of #9812. That issue is the __ROLLDOWN_ASSET__#<refId> placeholder produced by import x from './asset' (resolved in the asset plugin's renderChunk, a generate-phase hook). This issue is the other asset-reference mechanism — new URL(..., import.meta.url) (and, by the same code path, import.meta.ROLLUP_FILE_URL_<refId>) — which is resolved in the module finalizer (link phase, crates/rolldown/src/module_finalizers/mod.rshandle_new_url_with_string_literal_and_import_meta_url ~L1057, rewrite_rollup_file_url ~L1009).

Root cause is the same divergence: the HMR/lazy codegen (crates/rolldown/src/hmr/hmr_stage.rs) reuses the scan phase (so the asset is emitted by the load hook) but reimplements finalize+codegen with HmrAstFinalizer + EcmaCompiler::print_with, and never runs link or generate. HmrAstFinalizer does no asset-reference resolution at all (grep it for new_url / rollup_file_url / file_ref — empty), so every finalizer/renderChunk asset rewrite is silently absent from patches:

Reference form Resolved in (full pipeline) In HMR/lazy patch?
import x from './x.png'__ROLLDOWN_ASSET__#ref generate (renderChunk) fixed for #9812
new URL('./x', import.meta.url) link (module_finalizers AST rewrite) broken (this issue)
import.meta.ROLLUP_FILE_URL_x link (module_finalizers AST rewrite) broken (same path, untested)

Reproduction

Reproducible branch: 06-17-hmr-new-url-asset (built on the #9812 placeholder fix).

Test: packages/test-dev-server/tests/playground/lazy-compilation/__tests__/new-url.spec.ts (scenario fixture in playground/lazy-compilation/new-url/). The playground config sets experimental.resolveNewUrlToAsset: true (the feature is opt-in; without it new URL is left raw everywhere, not just in HMR).

just build-rolldown
cd packages/test-dev-server/tests
pnpm test:browser playground/lazy-compilation   # `new-url` fails; the other scenarios pass

The scenario lazily imports a module that does img.src = new URL('./new-url-image.png', import.meta.url).href.

Observed (captured)

First lazy load (HMR codegen):

img.src = http://localhost:PORT/@vite/new-url-image.png    ← raw specifier, unresolved
404 http://localhost:PORT/@vite/new-url-image.png

Full build (e.g. the existing crates/rolldown/tests/rolldown/topics/new_url/binary_asset snapshot) resolves correctly:

const url = new URL("assets/foo-u1ylrPOy.bin", import.meta.url);

So: the asset is emitted, but the new URL(...) first argument is never rewritten in the patch → wrong URL → 404 until a full reload.

Expected behavior

A new URL('./asset', import.meta.url) (and import.meta.ROLLUP_FILE_URL_x) reference in a module added by an HMR patch or compiled lazily should resolve to the same hashed asset URL the full build produces, and the asset should be served on the first request — not only after a refresh.

System Info

rolldown main (1.1.1 dev), reproduced on macOS. Related: #9812, vitejs/vite#22596.

Validations

  • Follow our Code of Conduct
  • Read the Contributing Guidelines.
  • Read the docs.
  • Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • The provided reproduction is a minimal reproducible example of the bug.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions