Skip to content

fix(cli): emit hashed web.resource URLs in release builds - #5701

Open
zxs1633079383 wants to merge 4 commits into
DioxusLabs:mainfrom
zxs1633079383:codex/issue-5689-hashed-web-resources
Open

fix(cli): emit hashed web.resource URLs in release builds#5701
zxs1633079383 wants to merge 4 commits into
DioxusLabs:mainfrom
zxs1633079383:codex/issue-5689-hashed-web-resources

Conversation

@zxs1633079383

@zxs1633079383 zxs1633079383 commented Jul 23, 2026

Copy link
Copy Markdown

Summary

  • register local [web.resource] CSS and JavaScript files in the web release build AppManifest
  • resolve generated <link> and <script> URLs from the exact manifest entries emitted by the asset writer
  • percent-encode every generated URL path segment with the structured url::Url API, while preserving / separators
  • preserve legacy browser-resource behavior for root-relative, protocol-relative, data:, blob:, HTTP(S), and other valid URL schemes
  • propagate resource/HTML preparation failures instead of panicking or writing a guaranteed 404 URL

Fixes #5689

Behavior

Release builds

Local resource paths are resolved relative to the selected crate manifest directory, canonicalized, registered with matching CSS/JS options, and looked up by source path plus options. The generated HTML references the exact content-hashed file under assets/. base_path and generated asset paths are appended one segment at a time, so spaces, #, literal percent sequences, and non-ASCII names are encoded without encoding path separators.

Browser references remain verbatim. This includes /styles.css, //cdn.example.com/styles.css, data:, blob:, HTTP(S), and any other string accepted as a URL. The ambiguous //server/share spelling intentionally keeps its existing protocol-relative browser meaning. On Windows, a local UNC path must use the native backslash spelling (\\server\share).

Development builds

Configured paths continue to be emitted verbatim. This keeps the existing dev-server and watcher behavior, including [web.resource.dev].

Compatibility and errors

This does not change Manganis hashing or asset processing. It connects WebResourceConfig to the existing AppManifest -> write_assets/process_file_to -> index.html pipeline. Public-directory passthrough, Manganis static-head assets, wasm/js loader assets, server builds, and non-web bundles are unchanged.

write_index_html now returns preparation and filesystem errors with context instead of unwrapping the generated HTML. Resource classification and URL construction are linear in the total configured path bytes; registration retains the manifest lookup/insertion costs already used by the asset pipeline.

Validation

  • cargo test -p dioxus-cli build::web::tests --no-default-features (11 passed)
  • cargo test -p dioxus-cli --no-default-features (170 passed)
  • cargo clippy -p dioxus-cli --tests --no-default-features -- -D warnings
  • cargo fmt --package dioxus-cli -- --check
  • git diff --check
  • config-to-output integration test: parses DioxusConfig, registers resources through the production helper, renders final HTML, and writes/asserts the matching hashed disk assets
  • release CLI harness: special-character CSS/JS names produced encoded hashed URLs matching raw filenames on disk; the encoded base path appeared once
  • development CLI harness: common and dev-only special-character paths remained verbatim
  • negative CLI harness: a missing special-character stylesheet stopped the build with the configured path in the error chain

The full dx harness is intentionally not committed as a repository test: it compiles roughly 173 crates, may download WASM tooling, depends on network/platform state, and takes about one to two minutes. The production-helper integration test is deterministic and fast; the full CLI path was exercised manually as listed above.

# 影响范围
影响 dx Web release 构建中 Dioxus.toml `[web.resource]` 的本地 CSS/JS 注册、AppManifest 映射与 index.html 注入;新增 CLI 对 url workspace 依赖用于结构化识别 HTTP(S) 外部资源。

# 改动影响面
调用链从 WebResourceConfig 经 BuildRequest::bundle_web 注册到 AppManifest,再由既有 write_assets/process_file_to 产出哈希文件并由 inject_resources 写入同一 URL。开发构建继续使用配置源路径,HTTP(S) 与协议相对 CDN 资源保持原样;未修改 Manganis 哈希算法、renderer、server 或其他平台打包。资源解析与 manifest 查询总复杂度为 O(r log n),额外空间 O(r)。

# 功能改进/开发/新增
release 本地样式和脚本使用 crate_dir 解析并注册,index.html 引用实际 `assets/*-dxh*` 产物;base_path 只拼接一次。缺失文件或 manifest 无对应 source/options 映射时返回带配置路径的构建错误,避免生成必然 404 的 HTML。新增 release CSS/JS、dev、base_path、外部 URL 和失败路径回归测试。

# 验证
- cargo test -p dioxus-cli build::web::tests --no-default-features:5 passed
- cargo test -p dioxus-cli --no-default-features:164 passed
- cargo clippy -p dioxus-cli --tests --no-default-features -- -D warnings:通过
- cargo fmt --package dioxus-cli -- --check:通过
- 临时 CLI harness release/dev 实构建:release index.html 与磁盘产物使用一致哈希 CSS/JS 且 `/docs` 仅出现一次;dev 保留源路径;缺失 CSS 构建以明确解析错误失败。
# 影响范围

仅调整 dioxus-cli 的 web.resource 注册、发布 URL 生成、index.html 写入错误传播与对应测试;不改变 dev 资源原样注入、浏览器 URL、其他平台 renderer 或普通 Manganis 资源流程。

# 改动影响面

按 browser/local 两类资源明确依赖方向:配置解析与 HTML 注入由 CLI 编排,本地文件继续交给 AppManifest 生成哈希路径。发布 URL 通过 url::Url 的 path_segments_mut 逐段编码,保留路径分隔符;处理复杂度为 O(资源路径总字节数 + manifest 查询成本),不引入二次遍历热点。

# 功能改进/开发/新增

兼容根相对、协议相对、data/blob/http/https 等浏览器引用原样输出;明确 //server/share 保持协议相对语义,Windows 反斜杠 UNC 仍作为本地路径。补齐空格、#、百分号与非 ASCII 文件名编码,移除 index.html 准备阶段 unwrap,并增加配置到最终 HTML 和磁盘资源的一体化测试。

# 验证

通过 cargo test -p dioxus-cli build::web::tests --no-default-features(11/11)、cargo test -p dioxus-cli --no-default-features(170/170)、cargo clippy -p dioxus-cli --tests --no-default-features -- -D warnings、cargo fmt --package dioxus-cli -- --check 与 git diff --check。手工 dx release/dev/缺失资源验证覆盖特殊字符、哈希文件落盘、dev 原样路径和明确失败。
@zxs1633079383

Copy link
Copy Markdown
Author

Review follow-up is pushed in 6f67ead34.

Changes since the initial revision:

  • generated release URLs now use structured, per-segment percent encoding for spaces, #, literal %xx text, and non-ASCII filenames
  • legacy browser references remain verbatim, including root-relative paths, protocol-relative paths, data:, blob:, and other valid schemes
  • //server/share is explicitly treated as protocol-relative; native backslash UNC remains local on Windows
  • write_index_html now propagates preparation/I/O errors instead of unwrapping
  • tests now include parsed config -> production registration -> final HTML -> matching hashed disk assets, plus the preparation-error path

Final local validation: focused web tests 11/11, full dioxus-cli tests 170/170, clippy with -D warnings, fmt check, and git diff --check. I also reran release, dev, and missing-resource CLI harnesses with spaces, #, %xx, and non-ASCII names. The PR body records why the heavyweight full dx harness remains a manual validation rather than a committed test.

# 影响范围

仅收窄 dioxus-cli 对 `[web.resource]` 普通相对字符串的 browser/local 分类;不调整 BuildRequest 编排、AppManifest 哈希流程、开发构建或其他平台打包。

# 改动影响面

配置合同优先于 Unix 文件名歧义:相对字符串含 query、fragment,或全部百分号均构成有效 `%HH` 转义时按浏览器引用原样输出;无这些 URL 成分的普通相对路径仍作为本地文件注册。分类单次扫描路径字节,时间 O(n)、额外空间 O(1)。

# 功能改进/开发/新增

修复 `assets/main.css?v=123`、`assets/app.js#x` 与 `assets/my%20theme.css` 被误当字面本地文件的问题;补充原样输出测试,并验证普通路径、无效百分号转义继续进入本地资源链路。相应集成测试使用无歧义本地文件名继续覆盖结构化 URL 编码和哈希落盘。

# 验证

通过 cargo test -p dioxus-cli build::web::tests --no-default-features(12/12)、cargo test -p dioxus-cli --no-default-features(171/171)、cargo clippy -p dioxus-cli --tests --no-default-features -- -D warnings、cargo fmt --package dioxus-cli -- --check 与 git diff --check。
@zxs1633079383

Copy link
Copy Markdown
Author

Compatibility blocker follow-up is pushed in 4e18706bb.

The resource classifier now preserves ambiguous relative browser references verbatim when they contain a query (assets/main.css?v=123), fragment (assets/app.js#x), or valid percent escapes (assets/my%20theme.css). Plain relative paths still register as local files, and malformed/incomplete percent sequences remain local as well. This intentionally gives the historical browser-reference contract priority over ambiguous Unix filenames; absolute local paths remain unambiguous.

The change stays inside the existing classifier and does not expand the BuildRequest wiring. Tests cover the three compatibility examples, the local-path counterexamples, and the existing config-to-HTML/disk integration boundary. Final validation: focused web tests 12/12, full dioxus-cli tests 171/171, clippy with -D warnings, fmt check, and git diff --check.

# 影响范围

仅调整 dioxus-cli `[web.resource]` 相对歧义引用的解析层与对应测试;不改变 BuildRequest 编排、AppManifest 哈希实现、开发构建或其他平台资源流程。

# 改动影响面

纯分类层新增 AmbiguousRelativeBrowserRef,不执行文件系统 I/O;拥有 crate_dir 的 location 解析层检查字面路径。歧义字符串对应路径存在时按 Local 注册,不存在时按 Browser 原样输出,非 NotFound 元数据错误继续向上传播。每个歧义资源增加一次 metadata 查询,路径分类仍为 O(n)、额外空间 O(1)。

# 功能改进/开发/新增

恢复真实 `theme # %20 你好.css` 等文件的哈希注册、逐段 URL 编码与磁盘特殊字符文件名,同时保留不存在的 query、fragment、有效百分号相对 URL 原样注入。若本地文件与浏览器引用两种解释同时成立,现存本地文件优先,形成确定规则。

# 验证

通过 cargo test -p dioxus-cli build::web::tests --no-default-features(13/13)、cargo test -p dioxus-cli --no-default-features(172/172)、cargo clippy -p dioxus-cli --tests --no-default-features -- -D warnings、cargo fmt --package dioxus-cli -- --check 与 git diff --check。
@zxs1633079383

Copy link
Copy Markdown
Author

Final ambiguity review is addressed in ff935a19b with an existence-first rule.

The pure classifier now returns AmbiguousRelativeBrowserRef for relative query, fragment, and valid percent-encoded strings without doing I/O. The crate-aware resolution layer then applies one deterministic rule:

  1. If the literal path exists under crate_dir, it is local and is registered, hashed, emitted to disk with its special-character filename, and percent-encoded segment-by-segment in HTML.
  2. If the literal path is absent, the configured value remains a verbatim browser reference.
  3. Metadata errors other than not-found are propagated rather than silently changing interpretation.

This covers both sides explicitly: a real theme # %20 你好.css file is hashed with an encoded URL and a matching special-character disk artifact, while absent assets/main.css?v=123, assets/app.js#x, and assets/my%20theme.css remain verbatim. If both interpretations are possible, an existing local file wins.

No additional BuildRequest wiring was introduced. Final validation: focused web tests 13/13, full dioxus-cli tests 172/172, clippy with -D warnings, fmt check, and git diff --check.

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.

[web.resource] writes the unhashed source path into index.html instead of the content-hashed output filename

1 participant