Repository navigation
feat(web): emit precache_manifest.json when --web-content-hash is enabled - #192953
Merged
auto-submit[bot] merged 2 commits intoSep 21, 2026
Merged
Conversation
…bled - Emit build/web/precache_manifest.json in WebServiceWorker when --web-content-hash is enabled, providing deterministic URL, SHA-256 prefix hash, byte size, and urlHashed metadata for custom service workers and PWAs. - Filter non-runtime build artifacts (*.map, *.symbols, *.info.json, dotfiles, flutter_service_worker.js, precache_manifest.json, and canvaskit/** when --web-resources-cdn is active). - Clean up stale precache_manifest.json when --web-content-hash is disabled and prevent Depfile input/output cycles. - Keep --web-content-hash hidden while runtime raw-key asset resolution (rootBundle.load) is completed. Fixes flutter#191916 Part of flutter#149031
kevmoo
force-pushed
the
web-content-hash-phase-3
branch
from
September 18, 2026 21:54
e8d540c to
f2d9b59
Compare
kevmoo
marked this pull request as ready for review
September 18, 2026 22:38
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces content hashing for static web assets and adds a 'precache_manifest.json' file to improve browser caching after deployments. It includes logic to generate this manifest, exclude non-runtime files, and handle stale manifest deletion. Additionally, it updates the 'web-content-hash' flag documentation and adds comprehensive tests for the new functionality. A syntax error regarding the conditional list element in 'web.dart' was identified, and a missing documentation comment for a public constant was noted.
mdebbar
approved these changes
Sep 21, 2026
This was referenced Sep 22, 2026
pull Bot
pushed a commit
to Mu-L/flutter
that referenced
this pull request
Sep 23, 2026
…hide --web-content-hash (flutter#193040) ## Description Completes the `--web-content-hash` architecture by adding runtime raw-key asset resolution in `web_ui`, achieving 100% content-hashing coverage across all files in `build/web/assets/` (eliminating manifest and shader exclusions), preserving `AssetManifest.listAssets()` and `AssetMetadata.main`, validating custom `web/flutter_bootstrap.js` / `web/index.html` templates, and un-hiding `--web-content-hash` (`--no-web-content-hash`) in `flutter build web --help`. ### Why this is needed Phase 2 (flutter#191919) and Phase 3 (flutter#192953) renamed physical asset files on disk (`<name>.<hash>.<ext>`), rewrote target values inside `AssetManifest.bin`, `AssetManifest.bin.json`, `AssetManifest.json`, and `FontManifest.json`, and emitted `precache_manifest.json`. Two critical gaps remained before exposing `--web-content-hash` to users: 1. **Cold-cache 404s on raw-key asset requests**: - While `Image.asset` resolves variant paths via `AssetManifest` before calling `rootBundle.load(variant.key)`, direct callers of `rootBundle.load(key)` / `rootBundle.loadString(key)` (`PlatformAssetBundle`) and third-party web plugins (`vector_graphics` / `flutter_svg` `AssetBytesLoader`, `lottie`, `rive`, `video_player_web`, `audioplayers_web`) pass the raw declared key (`assets/data/config.json`) directly to `ui_web.AssetManager.getAssetUrl(String asset)`. - Without runtime key translation in `ui_web.AssetManager.getAssetUrl(asset)`, raw-key asset fetches requested un-hashed paths (`assets/assets/data/config.json`) and 404'd on cold caches. 2. **Un-hashed manifest and shader files under `build/web/assets/`**: - `AssetManifest.*`, `FontManifest.json`, `NOTICES`, `NOTICES.Z`, and `shaders/**` were previously excluded from hashing, which meant a single `"source": "assets/**" -> Cache-Control: public, max-age=31536000, immutable` CDN rule would freeze `AssetManifest.bin.json` and `FontManifest.json` across deployments (`v1 -> v2`). ### What this PR changes 1. **100% `build/web/assets/` Immutability & Clean `AssetManifest.listAssets()` (`packages/flutter_tools/lib/src/web/content_hash.dart`)**: - Removes `_excludedFilenames` and `_excludedDirPrefixes` so every file under `build/web/assets/` (including `shaders/**`, `NOTICES`, `NOTICES.Z`, `FontManifest.json`, `AssetManifest.json`, `AssetManifest.bin`, and `AssetManifest.bin.json`) is content-hashed on disk. - Keeps `AssetManifest.bin` and `AssetManifest.json` strictly limited to user/package manifest entries (storing `Uri.decodeFull` variant paths so `Image.asset` on spaced filenames encodes cleanly to `%2520` rather than triple-encoding to `%252520`), and passes non-manifest SDK files (`NOTICES`, `shaders/ink_sparkle.frag`, `shaders/stretch_effect.frag`, `AssetManifest.bin`, `AssetManifest.json`) via `_flutter.buildConfig.extraAssets` (~200 bytes) alongside `"assetManifest"` and `"fontManifest"`. - Sorts discovered asset paths deterministically before manifest assembly so incremental and clean builds emit identical manifest hashes. - Fails fast (`throwToolExit`) if custom `web/flutter_bootstrap.js` or `web/index.html` templates omit `{{flutter_build_config}}` or contain malformed `_flutter.buildConfig` assignments. 2. **Preserve `AssetMetadata.main` (`packages/flutter/lib/src/services/asset_manifest.dart`)**: - Updates `_AssetManifestBin` (`asset_manifest.dart`) so `AssetMetadata.main` remains `true` for the primary 1.0x asset variant when content hashes are embedded in variant filenames. 3. **Incremental Build `.filecache` Tracking (`packages/flutter_tools/lib/src/build_system/targets/web.dart`)**: - Declares `{OUTPUT_DIR}/*/index.html` and `{OUTPUT_DIR}/flutter_bootstrap.js` in `WebReleaseBundle.inputs` and `WebReleaseBundle.outputs` when `webContentHash` is enabled so `.filecache` records post-injection file hashes. 4. **Synchronous Engine-Private Runtime Key Translation (`web_ui`)**: - Extends `FlutterJS` with `JSBuildConfig` (`assetManifest`, `fontManifest`, `extraAssets`) in `js_loader.dart`. - Updates `fetchFontManifest` in `fonts.dart` to fetch `flutter?.buildConfig?.fontManifest?.toDart ?? 'FontManifest.json'`. - Adds `_loadContentHashedAssetManifest(ui_web.assetManager)` inside `initializeEngineServices()` `Future.wait` in `initialization.dart` to register `assetManifest`, `fontManifest`, `extraAssets`, and decoded `AssetManifest.bin.<hash>.json` mappings in engine-private `setContentHashedAssetMap` / `resolveContentHashedAsset` (keeping the public `dart:ui_web` `AssetManager` API unchanged). - Resolves logical keys and variant paths synchronously inside `ui_web.AssetManager.getAssetUrl(String asset)`. 5. **Un-hide `--web-content-hash` (`packages/flutter_tools/lib/src/web/web_options.dart` & `build_web.dart`)**: - Removes `hide: true` from `WebOptions.webContentHash` and notes that local `canvaskit/**` (`--no-web-resources-cdn`) remains un-hashed (`urlHashed: false` in `precache_manifest.json`). ## Related Issues - Fixes flutter#191915 - Fixes flutter#193031 - Part of flutter#149031 ## Tests - `packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart` - `packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart` & `args_test.dart` - `packages/flutter/test/services/asset_manifest_test.dart` - `engine/src/flutter/lib/web_ui/test/engine/assets_test.dart` ## Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing.
This was referenced Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Implements Phase 3 of Flutter Web Content Hashing (#191916):
build/web/precache_manifest.jsonwhen--web-content-hashis enabled:WebServiceWorker.build(packages/flutter_tools/lib/src/web/content_hash.dart) afterhashWebAssetsrewrites hashed assets.{ "version": 1, "entries": [...] }sorted lexicographically by"url"for deterministic output."url": POSIX path relative tobuild/web/"hash": 8-character lowercase hex SHA-256 prefix of the file's bytes"size": File byte length (int)"urlHashed":bool(trueif the filename already contains.$hash.or ends with.$hashso custom service workers/PWAs can skip redundant?v=<hash>cache-busting query parameters;falsefor fixed-URL files such asindex.html,flutter_bootstrap.js,assets/AssetManifest.bin,assets/FontManifest.json,assets/NOTICES, andversion.json).*.map), symbols (*.symbols), compiler info (*.info.json), dotfiles (.prefix),flutter_service_worker.js,precache_manifest.jsonitself, andcanvaskit/**(unlesskUseLocalCanvasKitFlag == 'true'/--no-web-resources-cdn).precache_manifest.jsoninbuild/web/when--web-content-hashisfalse.precache_manifest.jsonfromWebServiceWorkerinput discovery while registering it as aDepfileoutput to prevent input/output cycles inservice_worker.d.hide: trueon--web-content-hash:hide: trueonWebOptions.webContentHashwhile runtime raw-key asset resolution (rootBundle.load,ui_web.AssetManager.getAssetUrl) is completed in companion tasks, and updates the--helpdescription to clarify thatassets/AssetManifest.bin.jsonandassets/FontManifest.jsonmust be served with revalidation while they remain unhashed.Note
On
master, passing the hidden--web-content-hashflag currently causes raw-key asset loads (rootBundle.load(...),vector_graphics, DOM media plugins) to 404 on cold cache because physical files are content-hashed on disk but runtime resolution has not yet landed. Phase 2.5 will add runtime key-to-hashed asset resolution in the web engine (ui_web.AssetManager) before--web-content-hashis un-hidden for general use.Related Issues
Tests
packages/flutter_tools/test/general.shard/build_system/targets/web_test.dartverifyingprecache_manifest.jsonschema/sorting/filtering,kUseLocalCanvasKitFlag == 'true'inclusion ofcanvaskit/, and stale manifest deletion whenwebContentHash: false.