Skip to content

[web_assets] Scaffold package - #3610

Merged
dcharkes merged 1 commit into
dart-lang:mainfrom
simolus3:web-uri-assets
Sep 23, 2026
Merged

dcharkes merged 1 commit into
dart-lang:mainfrom
simolus3:web-uri-assets

Conversation

@simolus3

@simolus3 simolus3 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Description

This introduces package:web_assets as a first step towards #988. Web assets are very similar to data assets (and the package is admittedly mostly a copy), but specific to the web and only exposed as a URI. This is their main difference to data assets, which will be exposed as a string or byte array. In other words, web_assets are assets a Dart web developer would otherwise have to copy into their web/ directory as static files.

The linked issue contains plenty of motivation, but I want to share two examples for which I want to use this asset type:

  1. package:sqlite3 has web support, and requires a build of sqlite compiled for the web. Currently, users have to manually download a sqlite3.wasm from my GitHub release page into their web/ directory to use that package on the web. An asset that can be resolved to a URI at runtime would be a perfect fit for this. Note that this is not the same as a code asset with a "wasm ABI", I also need this to work with dart compile js.
  2. package:drift, package:sqlite3_web and package:powersync all use web workers to offload database queries onto a background thread and to access file system APIs that unavailable in tabs. Again, users need to download these files manually when these should obviously be contributed by a hook.
  3. While I personally don't need this, it's easy to imagine that a package wanting to use an existing JavaScript library via dart:js_interop might want to provide its JS bundle somehow, hooks are again a great fit for this.

Finally, I think the mechanism of link hooks is also very interesting here:

  • For WebAssembly modules, we could detect which methods are actually called and invoke wasm-metadce to remove unused functions.
  • For worker files (which in the cases I personally care about are compiled with dart compile js), we could detect which functionality provided by the worker is actually used by the client and then pass --define flags along with bool.fromEnvironment in the worker to disable entrypoints we know a client doesn't call.
  • For embedded JavaScript libraries, using recoded usages to perform tree-shaking is also attractive.

Related Issues

Related to #988

PR Checklist

  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
  • I've run dart tool/ci.dart --all locally and resolved all issues identified. This ensures the PR is formatted, has no lint errors, and ran all code generators. This applies to the packages part of the toplevel pubspec.yaml workspace.
  • All existing and new tests are passing. I added new tests to check the change I am making.
  • The PR is actually solving the issue. PRs that don't solve the issue will be closed. Please be respectful of the maintainers' time. If it's not clear what the issue is, feel free to ask questions on the GitHub issue before submitting a PR.
  • I have updated CHANGELOG.md for the relevant packages. (Not needed for small changes such as doc typos).
  • I have updated the pubspec package version if necessary.

@dcharkes dcharkes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For worker files (which in the cases I personally care about are compiled with dart compile js), we could detect which functionality provided by the worker is actually used by the client and then pass --define flags along with bool.fromEnvironment in the worker to disable entrypoints we know a client doesn't call.

Can you explain more about what worker files are and how they are different from things you directly call via js interop?

Do we need to tell the SDK that via a property on the asset or not? E.g. does the SDK need to bundle/link worker files differently?

Or is it more like code assets which can either be (1) dylibs you call directly via @Native() external or (2) that are opened by the dynamic linker at runtime.

For embedded JavaScript libraries, using recoded usages to perform tree-shaking is also attractive.

Yes, lets go! 🔥 That's why we designed the record-use to be used in link hooks and hooks to work with multiple asset types!

I think the PR should be labeled [infra] to make the PR checker happy.

Consider consider adding web_assets to the hooks_runner as dev_dependency and adding a test data project that emits web assets, and then exercising that in an integration test. Then we know you can write hooks that use this and we don't get surprises about it not working after we try to use it in dartdev.

Besides the question whether we should split js and wasm assets LGTM. Happy to land, let it roll into the Dart SDK so that we can work on a PR that wires it up in dart build web-bundle!

"name": "assets/data_0.json",
"package": "simple_link"
},
"type": "web_assets/web"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • For WebAssembly modules, we could detect which methods are actually called and invoke wasm-metadce to remove unused functions.

I think we should distinguish different types of web assets maybe if we want the SDKs (Dart/Flutter/Jaspr/...) to treat different assets differently. (Looking at the file extension is not clean imo.)

  • WASM can possibly be linked and tree-shaken
  • WASMGC can possibly be linked and tree-shaken
  • JS can possibly be minified
  • images/sounds/icons/fonts -> I think these should be data assets and when Dart/Flutter encounter those with a web backend it should copy them to the right place. To make sure things work both in native backends and web backends transparently.

So, should we consider doing web_assets/js and web_assets/wasm (and potentially web_assets/wasmgc)? Then we enable ourselves to add properties that only apply to one of those types later.

(I know that will lead to duplicated code, but most of the schema/parsing is generated anyways.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have renamed the asset type to WebUriAsset, so that we can add WebJsAsset and friends when needed.

@simolus3

simolus3 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Can you explain more about what worker files are and how they are different from things you directly call via js interop?

Workers are basically the Isolate.spawnUri of the web. They take a URI parameter and make the browser start executing the script at that URI in a new thread. Communicating with workers is also similar to how things work with isolates, one uses postMessage to send objects to workers and a message event handler to receive objects from a worker. Dart code using workers would create workers and invoke these functions via dart:js_interop.

The main difference to interop with JS libraries is that workers can't be bundled directly into the main Dart program (there's is no equivalent of Isolate.run taking a JS function to run as a worker). If we had a "this is a JavaScript library I want to invoke from Dart" asset type, it wouldn't be unreasonable to expect embedders to merge compiled Dart programs and that library into a single JavaScript file (the JS ecosystem has many tools doing exactly that). This wouldn't work for workers, which need to be represented by a URL at runtime.

(of course, one can also represent JavaScript libraries by their URL at runtime and then import them, but I'd say that's a stopgap solution until we have proper JS library asset types)

I don't think it makes sense to have an asset type specifically for workers as there are other web APIs identifying resources via URLs, and those would work in exactly the same way. URL assets are the most generic asset type for the web: Embedders are not allowed to alter their contents in any way.

I agree with your suggestion of adding additional asset types that can be transformed (WASM, WASMGC, JS). I can rename the current URL asset to web_assets/url (or web_assets/generic? 🤔) to make it clear that we'll have additional asset types in the future.

images/sounds/icons/fonts -> I think these should be data assets and when Dart/Flutter encounter those with a web backend it should copy them to the right place.

An AssetBundle in Flutter (which I understand is where data assets currently end up in) doesn't expose URLs, so the assumption with data assets is that you'll only need to load them as a Future<Uint8List> at runtime (which is important, it means embedders could store data assets in an archive, embed them in the executable or a data section in WASM instead of preserving separate files). If we had a way to resolve data assets to URIs (which would restrict embedders!), the asset type added in this PR wouldn't be necessary.

@dcharkes

dcharkes commented Sep 9, 2026 •

Copy link
Copy Markdown
Collaborator

(there's is no equivalent of Isolate.run taking a JS function to run as a worker)

Is that something we should consider solving? Because if we solve it, you can run Dart code as a web worker, and if that Dart code happens to use js-interop and a js asset it's the same as all other JS interop.

URL assets are the most generic asset type for the web: Embedders are not allowed to alter their contents in any way.

To me that kind of sounds like data assets. If the contents are not altered in any way, no minifying, etc. However, Data assets can only be accessed by bytes right now, they are not guaranteed to have a file path on native or an url on web. And embedders might inline the bytes into the app if they see fit.

So, if we then need a kind-of-data-asset-but-it-must-have-a-url on the web, then web_assets/web are those. And we would introduces web_assets/wasm and web_assets/js later. Maybe web_assets/data_url or web_assets/url would be better? And then UrlAsset(...) which is an asset that is at runtime accessible via an url.

Oh, I am basically retyping what you said without reading your full message 😄 that means we are on the same page! 👍

Then adding UrlAsset in this PR SGTM!

(Url assets basically give you the capability lazy load things on the web. Ditto we could have a data_assets/file FileAsset for which embedders must provide a file path instead of just the bytes. I'm not yet entirely sure if I want all of those capabilities/contracts as different asset types or as properties on one asset type. But maybe different asset types is cleaner. The only result is that if it's different asset types you end up branching in your hooks with: if it's web report a UrlAsset() and if it's native report a DataAsset() -> but if you actually also use different code at runtime, that is not that ugly. E.g. on web you load from a url, and on native you load the bytes. This line of thought is to be continued and explored later. For now I think url assets make sense.)

@simolus3 simolus3 changed the title web_assets: Scaffold package [web_assets] Scaffold package Sep 11, 2026
@github-actions github-actions Bot added the type-infra A repository infrastructure change or enhancement label Sep 11, 2026
@simolus3
simolus3 marked this pull request as ready for review September 14, 2026 06:41
@dcharkes

Copy link
Copy Markdown
Collaborator

CI seems red. @simolus3

note to self:

  • need to publish the first version of the package manually and transfer it to labs.dart.dev
  • then set up auto-publishing

@simolus3

Copy link
Copy Markdown
Contributor Author

@dcharkes I have fixed the test failures, could you take another look?

@codecov

codecov Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.48921% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.97%. Comparing base (57b4bad) to head (e56bcbe).

Files with missing lines Patch % Lines
pkgs/web_assets/lib/src/web_assets/extension.dart 61.90% 8 Missing ⚠️
pkgs/web_assets/lib/src/web_assets/validation.dart 87.30% 8 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff            @@
##             main    #3610    +/-   ##
========================================
  Coverage   87.96%   87.97%            
========================================
  Files         307      311     +4     
  Lines       19662    19801   +139     
========================================
+ Hits        17296    17419   +123     
- Misses       2366     2382    +16     
Flag Coverage Δ
native_pkgs_macos 84.78% <88.48%> (+0.08%) ⬆️
native_pkgs_ubuntu 83.13% <88.48%> (+0.12%) ⬆️
native_pkgs_windows 87.26% <88.48%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
code_assets 95.72% <ø> (ø)
data_assets 91.56% <ø> (ø)
ffi 100.00% <ø> (ø)
ffigen 91.42% <ø> (ø)
hooks 86.40% <ø> (ø)
hooks_runner 89.76% <ø> (ø)
jni 66.34% <ø> (ø)
jni_flutter ∅ <ø> (∅)
jni_util ∅ <ø> (∅)
jnigen 82.77% <ø> (ø)
json_syntax_generator ∅ <ø> (∅)
native_test_helpers 85.71% <ø> (ø)
native_toolchain_c 95.94% <ø> (ø)
objective_c 79.53% <ø> (ø)
pub_formats ∅ <ø> (∅)
record_use 81.48% <ø> (ø)
snippet_tool 66.23% <ø> (ø)
swift2objc 92.95% <ø> (ø)
swiftgen 80.80% <ø> (ø)
test_case_selector 97.28% <ø> (ø)
Files with missing lines Coverage Δ
pkgs/web_assets/lib/src/web_assets/config.dart 100.00% <100.00%> (ø)
pkgs/web_assets/lib/src/web_assets/web_asset.dart 100.00% <100.00%> (ø)
pkgs/web_assets/lib/src/web_assets/extension.dart 61.90% <61.90%> (ø)
pkgs/web_assets/lib/src/web_assets/validation.dart 87.30% <87.30%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dcharkes

Copy link
Copy Markdown
Collaborator

simolus3 and others added 3 commits 12 hours ago

@simolus3 did you use multiple email addresses? If yes they all need to sign the cla. 😅 Or just squash and reupload with 1 email address.

Also, we've switched to codecov from coveralls, and apparently the default is now that you have to have a certain percentage covered. (It's easy to task an agent to write some extra tests. See my last 10 PRs on this repo 😆)

Comment thread pkgs/web_assets/example/README.md Resolved
@simolus3

Copy link
Copy Markdown
Contributor Author

I've added a few more tests which should cover most of these files now.

@dcharkes
dcharkes merged commit c44550b into dart-lang:main Sep 23, 2026
48 checks passed
Hassnaa9 pushed a commit to Hassnaa9/native that referenced this pull request Sep 27, 2026
copybara-service Bot pushed a commit to dart-lang/sdk that referenced this pull request Oct 6, 2026
Revisions updated by `dart tools/rev_sdk_deps.dart`.

native (https://github.com/dart-lang/native/compare/f628b26..27f7678):
  27f767827  Mon Sep 28 14:51:28 2026 +0200  Daco Harkes  [hooks_runner] Remove `KernelAssets` and `Target` (dart-lang/native#3703)
  ddc7d6deb  Mon Sep 28 05:40:03 2026 +0200  Jakob Kordež  [ffigen] Skip typedefs to function pointers with unsupported parameter types (dart-lang/native#3694)
  177d283d9  Mon Sep 28 04:30:28 2026 +0200  Jakob Kordež  [ffigen] Generate bindings for C++ enums, structs and unions nested in namespaces and records (dart-lang/native#3644)
  5ee8f67d8  Mon Sep 28 11:39:14 2026 +1000  Liam Appelbe  [objective_c] Clarify block isolate scoping (dart-lang/native#3692)
  25f090116  Fri Sep 25 12:09:12 2026 +1000  Liam Appelbe  [ffigen][jnigen] Deprecate YAML config (dart-lang/native#3654)
  8364413eb  Fri Sep 25 10:09:50 2026 +1000  Liam Appelbe  [jnigen] Document nullability (dart-lang/native#3690)
  e8d518574  Thu Sep 24 18:02:46 2026 +1000  Liam Appelbe  [ffigen] Code snippet-ify ffigen and objective_c (dart-lang/native#3685)
  663385955  Thu Sep 24 03:21:53 2026 +0300  Manar Elhabbal  [ffigen] prevent setting both cpp and objectiveC in ffigenerator (dart-lang/native#3687)
  1cf40952a  Thu Sep 24 09:27:09 2026 +1000  Liam Appelbe  [objective_c] Remove arm64e build hack (dart-lang/native#3680)
  c44550b6e  Wed Sep 23 13:01:33 2026 +0200  Simon Binder  [web_assets] Scaffold package (dart-lang/native#3610)
  57b4bad69  Wed Sep 23 16:21:27 2026 +1000  Liam Appelbe  [swift2objc] Code snippet-ify swift2objc (dart-lang/native#3684)
  5cee1d0be  Wed Sep 23 10:16:29 2026 +0530  Rachit Vikram Singh  [ffigen] Add Input.appendCompilerOptions to append clang args to defaults (`#3630`) (dart-lang/native#3676)
  225a648df  Wed Sep 23 14:37:13 2026 +1000  Liam Appelbe  [jnigen] Code snippet-ify jnigen and co (dart-lang/native#3641)
  2030decf9  Wed Sep 23 13:51:24 2026 +1000  Liam Appelbe  [infra] Move snippets_tool to its own package (dart-lang/native#3683)
  6fa2beea2  Wed Sep 23 12:14:28 2026 +1000  Liam Appelbe  [objective_c] Revert `#3664` (dart-lang/native#3681)
  ed7605f8a  Tue Sep 22 12:03:22 2026 +0530  Sivapriya  [jni] Add deep conversion utils (dart-lang/native#3635)
  8574b7422  Tue Sep 22 08:31:11 2026 +0200  Lukas Klingsbo  [ffigen] Allow package:cli_util 0.5.x and 0.6.x (dart-lang/native#3673)

test (https://github.com/dart-lang/test/compare/9946c95..4f92d53):
  4f92d530  Fri Sep 25 16:46:22 2026 -0700  Nate Bosch  Unskip a test on windows (dart-lang/test#2772)
  7fb84866  Fri Sep 25 14:52:39 2026 -0700  Nate Bosch  Bundle skips at the end of the GitHub reporter (dart-lang/test#2768)
  603e5ec5  Thu Sep 24 14:00:41 2026 -0700  Nate Bosch  Clean up the runner's temporary directories (dart-lang/test#2763)
  4bf04d90  Thu Sep 24 12:41:15 2026 -0700  Nate Bosch  Refactor signal handling in executable.dart (dart-lang/test#2765)
  2c660a84  Tue Sep 22 15:33:34 2026 -0700  Nate Bosch  Regenerate mono_repo output (dart-lang/test#2767)

tools (https://github.com/dart-lang/tools/compare/3614ce0..d87eaf7):
  d87eaf79  Thu Sep 24 08:31:33 2026 -0700  Kevin Moore  fix(benchmark_harness): migrate subprocess spawning to package:cli_util (dart-lang/tools#2585)
  8ccf8ccf  Wed Sep 23 05:09:10 2026 +0530  Aman Maurya  [string_scanner] Extract a mixin for LineScanner and deduplicate eager scanning logic (dart-lang/tools#2569)
  afcdd6d9  Tue Sep 22 15:38:07 2026 +0200  Morgan :)  Document that watch events converge on filesystem state. (dart-lang/tools#2607)
  f2157cbc  Tue Sep 22 04:52:58 2026 +0300  Hasan Armoush  [code_builder] Add null-aware cascades, cascade index, and fluent cascade helpers (dart-lang/tools#2540)

web (https://github.com/dart-lang/web/compare/e5c6c02..ec71a4c):
  ec71a4c  Mon Sep 28 10:07:01 2026 -0700  Kevin Moore  [js_interop_gen] Require `code_builder: ^4.12.0` and update golden headers (dart-lang/web#576)

[email protected]

Change-Id: I766bc5b2b1297e3251990821bb73387ba685e39b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/556320
Commit-Queue: Nate Bosch <[email protected]>
Reviewed-by: Brian Quinlan <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package:hooks type-infra A repository infrastructure change or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants