Skip to content

tool: build both Wasm and JS targets for flutter run --wasm#186253

Open
shivanshu877 wants to merge 8 commits into
flutter:masterfrom
shivanshu877:feat/172006-wasm-run-fallback-v2
Open

tool: build both Wasm and JS targets for flutter run --wasm#186253
shivanshu877 wants to merge 8 commits into
flutter:masterfrom
shivanshu877:feat/172006-wasm-run-fallback-v2

Conversation

@shivanshu877

Copy link
Copy Markdown
Contributor

Description

Fixes #172006.

flutter build web --wasm already produces both a Wasm and a JS build of the app, so the Flutter web loader can fall back to JS at runtime on browsers that don't support WasmGC. flutter run --wasm only produces the Wasm build, so attempting to run on a non-WasmGC browser (e.g. iOS Safari connected via remote debugging) fails with:

FlutterLoader could not find a build compatible with configuration and environment.

This PR brings the flutter run --wasm path in line with flutter build web --wasm by returning a list of compiler configurations (Wasm + JS when --wasm is set, JS-only otherwise) from ResidentWebRunner._compilerConfigs and passing it through to WebBuilder.buildWeb at both invocation sites.

Repro (from the original issue)

flutter create test_app --platform web
cd test_app
flutter run -d chrome --wasm --web-hostname 0.0.0.0
# Then connect a browser without WasmGC support (iOS Safari, older browsers, etc.)
# Before this PR: "FlutterLoader could not find a build compatible..."
# After  this PR: app loads via the JS fallback build

Tests

Added two regression tests in resident_web_runner_test.dart:

  • --wasm produces both Wasm and JS compiler configs for runtime fallback — asserts the runner emits exactly two configs (one per CompileTarget) when webUseWasm: true.
  • no --wasm produces only a JS compiler config — asserts the default path still emits a single JsCompilerConfig.

Required exposing the otherwise-private getter as @visibleForTesting List<WebCompilerConfig> get debugCompilerConfigs. All 52 tests in resident_web_runner_test.dart pass.

This PR replaces #186237 (closed for a clean conversation timeline).

Pre-launch Checklist

Fixes flutter#172006.

`flutter build web --wasm` already produces both a Wasm and a JS build
of the app so the runtime web loader can fall back to JS at runtime on
browsers that don't support WasmGC. `flutter run --wasm` only produces
the Wasm build, so attempting to run on a non-WasmGC browser (e.g.
iOS Safari) fails with:

  FlutterLoader could not find a build compatible with configuration
  and environment.

Bring the run path in line with build by returning a list of compiler
configurations (Wasm + JS when --wasm is set, JS-only otherwise) from
ResidentWebRunner._compilerConfigs and passing it through to
WebBuilder.buildWeb at both call sites.
Per @stuartmorgan-g's feedback, this PR makes a behavioral change
(returning a list of compiler configs instead of a single one when
--wasm is passed to `flutter run`) and was missing the corresponding
tests.

Adds:
* @VisibleForTesting accessor `debugCompilerConfigs` on
  ResidentWebRunner that exposes the otherwise-private getter.
* Two regression tests:
  - `--wasm produces both Wasm and JS compiler configs for runtime
    fallback` — asserts that with `webUseWasm: true` the runner emits
    exactly two configs, one per CompileTarget.
  - `no --wasm produces only a JS compiler config` — asserts the
    default path still emits a single JsCompilerConfig.
@github-actions github-actions Bot added the tool Affects the "flutter" command-line tool. See also t: labels. label May 8, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request modifies ResidentWebRunner to support multiple compiler configurations, specifically adding a JavaScript fallback when WebAssembly is enabled. It replaces the single _compilerConfig property with a list-based _compilerConfigs getter and introduces a @VisibleForTesting property for verification. Regression tests have been added to ensure the correct configurations are generated based on the Wasm flag. Feedback suggests refactoring the _compilerConfigs getter to eliminate duplication by extracting the shared JsCompilerConfig initialization into a local variable.

Comment thread packages/flutter_tools/lib/src/isolated/resident_web_runner.dart Outdated
@bkonyi bkonyi added the team-web Owned by Web platform team label May 25, 2026
Extract the JsCompilerConfig.run constructor into a local variable so
the JS-fallback (Wasm + JS) and JS-only branches share a single
source. Pure refactor — no behaviour change.
@github-actions github-actions Bot removed the team-web Owned by Web platform team label May 30, 2026
@bkonyi
bkonyi requested review from bkonyi and mdebbar June 1, 2026 15:59
@bkonyi bkonyi added the team-web Owned by Web platform team label Jun 1, 2026
bkonyi
bkonyi previously approved these changes Jun 1, 2026

@bkonyi bkonyi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, this LGTM! I do want a member of the Flutter Web team to sign off as well.

@mdebbar mdebbar added the CICD Run CI/CD label Jun 1, 2026
@github-actions github-actions Bot removed the team-web Owned by Web platform team label Jun 1, 2026
@mdebbar mdebbar added the autosubmit Merge PR when tree becomes green via auto submit App label Jun 1, 2026
mdebbar
mdebbar previously approved these changes Jun 1, 2026

@mdebbar mdebbar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment thread packages/flutter_tools/lib/src/isolated/resident_web_runner.dart Outdated
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jun 1, 2026
@auto-submit

auto-submit Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/186253, because - The status or check suite Linux analyzer_benchmark has failed. Please fix the issues identified (or deflake) before re-applying this label.

* lib/src/isolated/resident_web_runner.dart:
  - Omit obvious JsCompilerConfig type annotation on the dedup'd local
    introduced by the previous refactor (omit_obvious_local_variable_types).

* test/general.shard/resident_web_runner_test.dart:
  - Reorder web/compiler_config.dart import so the web/ block stays
    alphabetical (directives_ordering).
  - Replace const BuildInfo(BuildMode.debug, null, ...) with BuildInfo.dummy
    in the new --wasm regression test — the linter confirmed they're
    equivalent objects (use_named_constants).
  - Drop two obvious ResidentWebRunner type annotations on locals where
    the right-hand side already includes 'as ResidentWebRunner'
    (omit_obvious_local_variable_types).

Verified locally with 'flutter analyze' on both files: no issues found.
@shivanshu877
shivanshu877 dismissed stale reviews from mdebbar and bkonyi via c85bb22 June 2, 2026 06:20
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 2, 2026
@bkonyi

bkonyi commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Hi @shivanshu877!

If you can please address the comment left by myself and @mdebbar, we can move forward with merging this PR. Thanks!

Addresses review feedback from @mdebbar and @bkonyi: replace the
if/else with two return statements by a single list literal that uses
a collection-if for the Wasm config. Behaviour is unchanged.
@shivanshu877

Copy link
Copy Markdown
Contributor Author

Addressed in 02d1bf0 — folded _compilerConfigs into the collection-if expression suggested by @mdebbar. Thanks!

@bkonyi bkonyi added the CICD Run CI/CD label Jun 11, 2026
bkonyi
bkonyi previously approved these changes Jun 11, 2026

@bkonyi bkonyi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

@mdebbar can you reapprove and add the autosubmit label? :)

@bkonyi
bkonyi requested a review from nshahan June 16, 2026 21:09
@bkonyi

bkonyi commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@nshahan can I get you to rubber stamp this PR?

@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 16, 2026
kevmoo
kevmoo previously approved these changes Jun 16, 2026

@nshahan nshahan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To me it seems like this change complicates our support matrix and makes it less clear to a developer what version of the app they are running and what tooling should be supported.

What mode is this running? IIUC flutter run defaults to the debug mode. There are actually two JavaScript compilers under the hood. DDC is used for --debug and also runs developer tooling infra to support hot reload, hot restart, and debugging. --profile and --release use dart2js which doesn't support the extra developer tooling.

Running that with --wasm seems to force the use of dart2wasm potentially in any mode.

What JavaScript compiler should we use? This seems to use dart2js and skip the development tooling in any mode but I'm not really sure. The test doesn't tell me what actually happens if we run with this flag.

…nshahan

@nshahan raised a real concern on flutter#186253: it wasn't clear from the
code or tests which JavaScript compiler runs in which mode when the
new '--wasm' JS fallback is picked, or whether the fallback silently
loses DDC-based dev tooling.

Both concerns are documentation, not behavior — the code already picks
JsCompilerConfig (dart2js) unambiguously. Make that explicit:

  * Docstring on ResidentWebRunner._compilerConfigs now spells out the
    full mode / --wasm matrix in a table, calls out which branch of
    _runOnce actually consults this getter (isDebug && !webUseWasm
    routes through WebDevFS + DDC and does not), and explains that
    hot reload was already unavailable with --wasm before this getter
    was added (see reloadIsRestart) — the new JS fallback only widens
    browser compatibility, it does not remove tooling.

  * Assertions in the two --wasm regression tests now check the
    concrete runtime types (WasmCompilerConfig / JsCompilerConfig) and
    their relative order — not just the CompileTarget enum count — so
    the test itself documents 'JS side is dart2js, never DDC'. Test
    names also updated to reflect the fallback-preference ordering.
@shivanshu877
shivanshu877 dismissed stale reviews from kevmoo and bkonyi via 7e7f2ea July 7, 2026 17:09
@shivanshu877

Copy link
Copy Markdown
Contributor Author

Thanks for pushing back on this @nshahan — the ambiguity you're pointing at is real, and I think it's a documentation / test-clarity gap rather than a behavior change. Pushed 7e7f2ea289f addressing both:

Where DDC actually lives in flutter run. _compilerConfigs is only consulted when flutter run goes through WebBuilder.buildWeb — i.e. every mode when --wasm is set, plus profile/release when it isn't. The DDC path is a separate branch (isDebug && !webUseWasm in _runOnce) that routes through WebDevFS, and it never reads this getter. So the change here has no effect on the debug-without---wasm DDC path.

Full mode / --wasm matrix (now spelled out in the docstring):

mode --wasm off --wasm on
debug DDC via WebDevFS (this getter not called) dart2wasm + dart2js
profile dart2js dart2wasm + dart2js
release dart2js dart2wasm + dart2js

The JS side of the fallback is always dart2js (JsCompilerConfig), never DDC. That was already true before this PR for profile/release; the PR just adds the same dart2js build alongside the dart2wasm build so browsers without WasmGC support have a working fallback. reloadIsRestart was already true whenever --wasm was set — hot reload was already unavailable with --wasm; the fallback only widens browser compatibility.

Tests strengthened. The two --wasm-vs-no---wasm regression tests now assert the concrete runtime types (WasmCompilerConfig / JsCompilerConfig) and their loader-preference order — so the test itself documents "JS side is dart2js, never DDC" and "Wasm-first, JS-fallback second". Test names updated to say so.

The commit message and PR body still describe the change accurately: no behavior change beyond the JS fallback, no new dev-tooling regression. If you'd like the docstring to also reference an issue tracker item that discusses adding a DDC fallback for the --wasm + debug case specifically, happy to open one — but that felt like scope beyond fixing #172006.

@nshahan

nshahan commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@shivanshu877
Thanks for the update but there is still a concern that this is adding compilation with dart2js with a combination of flags (the debug mode) where it was not available previously. I'm liking the overall intention here but we want to clarify some of the flags combinations so the tool is more clear about what it is producing.

@kevmoo
Can you help with some direction on making sure we have a clear picture of what we are supporting with --wasm in debug, profile, and release modes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[web] flutter run with --wasm flag does not fallback to JS as it does with flutter build

5 participants