Repository navigation
Conversation
… as code With `experimental.resolveNewUrlToAsset`, a user plugin whose `load` hook returns code for the referenced file without a `moduleType` (for example a wasm loader) left the asserted `asset` type on the module, and parsing failed with an internal error. Keep the plugin code as the module, and emit the file as an asset so `new URL()` is rewritten to it. Refs rolldown#11325
Merging this PR will not alter performance1 warning
Comparing Footnotes
|
|
The asset is emitted only when the plugin returns no |
…11420) Fixes case 3 of #11325. Case 1 is #11418 and case 2 is #11419; this one is independent of both. ## Problem With `experimental.resolveNewUrlToAsset`, the query and fragment were dropped when `new URL()` was rewritten: ```js new URL("./sprite.svg?v=1", import.meta.url) // -> new URL("assets/sprite-[hash].svg", import.meta.url) new URL("./sprite.svg#icon", import.meta.url) // -> new URL("assets/sprite-[hash].svg", import.meta.url) ``` so `url.search` and `url.hash` were lost, which breaks uses like SVG sprite fragments. ## Fix In the `new URL()` finalizer, after computing the asset path, append the part of the original specifier that follows the path (everything from the first `?` or `#`, using `clean_url`). This matches Vite's `vite:asset-import-meta-url`, which keeps the postfix when it rewrites the URL. Resolution and the emitted asset are unchanged: all of `?v=1`, `#icon`, `?v=1#icon` and the plain path still share one emitted file. ## Tests New fixture `topics/new_url/keep_query_and_hash` covering a query, a fragment, both, and the plain path. With snapshot updates disabled (`INSTA_UPDATE=no CI=true`) it fails on `main` and passes here. No existing snapshot changed. `cargo test -p rolldown` passes except `test262_module_code`, which fails the same way on `main` locally because the `test262` submodule is not checked out. `cargo fmt --check` and `cargo clippy -p rolldown --all-targets` are clean. AI disclosure: I used Claude Code to help trace the code path and write the change and test. I reviewed the change and ran the tests above myself. Co-authored-by: Yunfei He <i.heyunfei@gmail.com>
Fixes case 1 of #11325. Cases 2 (percent-encoded paths) and 3 (query and hash) are separate and not touched here.
Problem
With
experimental.resolveNewUrlToAsset, thenew URL()import record assertsModuleType::Asset. The builtin asset-module plugin runs itsloadasPost, so a user plugin can answer first. When that plugin returns code without amoduleType, asunwasmdoes for every.wasmfile,load_sourceapplied the assertedassettype to the plugin's code and parsing failed:This breaks every Nitro build that uses
unwasmwith this option (nitrojs/nitro#4930).Fix
In
load_source, when the asserted type isAssetand a load hook returned code but no module type:FileEmitter(sameassetFileNamesandsanitizeFileNamehandling asemit_file_async), associated with the module, and watched.The
new URL()finalizer then finds the asset throughfile_ref_for_moduleas usual, so the output isnew URL("assets/m-[hash].wasm", import.meta.url)and the asset is emitted with the original bytes. A plugin that returns amoduleTypeis still trusted as before.The issue suggested either a clear error or ignoring the plugin's result for the asset. I went with emitting the asset while keeping the plugin's module, because the
new URL()caller wants the file, and the plugin's code stays available to anyimportof the same id.Not covered
The asserted type comes from whichever record reaches the module first. If a module is first reached by a plain
import, it has no asserted type and itsnew URL()is left unrewritten. That is the existing behavior with or without plugins and is unchanged here. I noted it ininternal-docs/plugin-asset-module/implementation.md.Tests
New Rust integration test
plugin/new_url_asset_loaded_by_plugin: a plugin loads.wasmas JS without a module type, and the test checks that the wasm bytes are emitted as an asset and thatnew URL()points at it. It fails onmainwith theUNHANDLEABLE_ERRORabove.Also checked the issue's JS reproduction against the built package:
src/load.jsnow producesnew URL("assets/m-BYieBQW-.wasm", import.meta.url)plus the asset.cargo test -p rolldownpasses excepttest262_module_code, which fails the same way onmainlocally because thetest262submodule is not checked out.cargo fmt --checkandcargo clippy -p rolldown --all-targetsare clean.AI disclosure: I used Claude Code to help trace the code path and write the change and test. I reviewed the change and ran the tests above myself.