Skip to content

Don't bundle CanvasKit and Pyodide in CDN-mode web builds; fix --no-cdn loading Pyodide from a CDN (0.86.7) - #6760

Open
FeodorFitsner wants to merge 3 commits into
mainfrom
fix/web-skip-bundling-cdn-assets
Open

Don't bundle CanvasKit and Pyodide in CDN-mode web builds; fix --no-cdn loading Pyodide from a CDN (0.86.7)#6760
FeodorFitsner wants to merge 3 commits into
mainfrom
fix/web-skip-bundling-cdn-assets

Conversation

@FeodorFitsner

@FeodorFitsner FeodorFitsner commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Two related web-build defects, one bug fix and one packaging change. Split into separate commits — the first stands alone.

1. --no-cdn still loaded Pyodide from jsdelivr

The web template chose between the bundled runtime and the CDN with:

{% if cookiecutter.no_cdn == "True" %}

but flet build passes no_cdn through cookiecutter's extra_context as a Python bool, and Jinja compares it as-is — True == "True" is False. The CDN branch was taken in both modes, so --no-cdn builds downloaded, cached and shipped ~15 MB of Pyodide that the browser then ignored.

Only pyodideUrl was affected. flet.noCdn itself is derived separately, via "{{ cookiecutter.no_cdn }}".toLowerCase() == "true", which renders correctly — so CanvasKit and the fallback fonts loaded locally as expected while Pyodide alone went to the CDN. That asymmetry made it look like a Pyodide quirk rather than a template bug.

The template's other no_cdn test (assets/FontManifest.json) already used plain truthiness. All conditions now do, and no == "True" comparisons remain under templates/.

Also fixes the bundled URL being origin-absolute: with --base-url myapp the app requested /pyodide/pyodide.mjs while the file sat at /myapp/pyodide/pyodide.mjs. It now renders relative to the configured base URL, as canvasKitBaseUrl does. Without --base-url the rendered URL is unchanged.

2. CDN-mode builds bundled ~52 MB nothing fetches

In CDN mode (the default) Flutter loads CanvasKit from gstatic and Flet points pyodideUrl at jsdelivr — yet flutter build web always emits canvaskit/ (~37 MB), and ensure_pyodide() ran unconditionally in both flet build web and flet publish, copying a further ~15 MB. Neither is ever requested.

Both are now dropped in CDN mode. A minimal web build goes from 71 MB to 19 MB. flet build also clears a pyodide/ left in the reused Flutter project by an earlier --no-cdn build, so switching modes doesn't silently keep shipping it. --no-cdn (or [tool.flet.web] cdn = false) still bundles everything, unchanged.

Decoupling where assets load from, from what got bundled

flutter_bootstrap.js applied canvasKitBaseUrl and fontFallbackBaseUrl only inside if (flet.noCdn). A host serving its own copy of the runtime — a CDN-restricted network, an air-gapped deployment, a platform mirroring the runtime on its own origin — had to also set noCdn for the assignment to take effect; setting the URL alone failed silently and the app booted off gstatic anyway.

Both are now applied whenever set, default to null in CDN mode, and are pinned by flet build/patch_index.py only when bundling. FletJS.canvasKitBaseUrl becomes String? to match — it has no readers in the Dart tree, so that is an annotation fix only.

Verification

End to end on a minimal app, both modes:

CDN mode (default) --no-cdn
Build size 19 MB (was 71 MB) 71 MB
canvaskit/ / pyodide/ absent 37 MB / 15 MB present
pyodideUrl jsdelivr /pyodide/pyodide.mjs (was jsdelivr)
canvasKitBaseUrl null → gstatic /canvaskit/
Boots in headless Chrome yes yes

The CDN-mode build's network log shows chromium/canvaskit.{js,wasm} from gstatic and the full Pyodide runtime from jsdelivr, with zero requests to a local canvaskit/ or pyodide/ path — confirming the dropped directories were unreferenced.

The bug in §1 is reproduced against main: rendering the template with no_cdn=True emits the jsdelivr URL there and the local path on this branch.

Not covered: Chrome only. Safari and Firefox select different CanvasKit variants (Chrome fetched the chromium/ one) and were not exercised.

Summary by Sourcery

Adjust web build and runtime configuration to correctly respect --no-cdn, avoid bundling unused CanvasKit/Pyodide assets in CDN mode, and decouple CDN usage from how runtime asset URLs are configured.

Bug Fixes:

  • Ensure --no-cdn web builds use the bundled Pyodide runtime instead of loading it from jsdelivr.
  • Fix Pyodide URL generation in --no-cdn builds to honor the configured base URL for sub-path deployments.
  • Honor runtime asset base URLs (CanvasKit and font fallbacks) based on their presence rather than noCdn, so hosts serving their own copies are respected.

Enhancements:

  • Introduce a shared resolve_no_cdn helper to centralize CDN-mode resolution in the build pipeline.
  • Make canvasKitBaseUrl and fontFallbackBaseUrl default to null in CDN mode and apply them only when explicitly set, allowing flexible runtime hosting.
  • Relax the FletJS.canvasKitBaseUrl type to nullable to match its runtime usage.

Build:

  • Skip bundling Pyodide and remove any stale local copy from web builds when CDN mode is enabled.
  • Prune the canvaskit/ directory from CDN-mode web build output to reduce bundle size.
  • Ensure flet publish only bundles local CanvasKit/Pyodide assets in no-CDN mode and removes them from dist otherwise.

Documentation:

  • Document the 0.86.7 release changes in the Python and Dart package changelogs, including CDN and asset-loading behavior.

The web template picked between the bundled runtime and jsdelivr with
`{% if cookiecutter.no_cdn == "True" %}`, but `flet build` passes
`no_cdn` through cookiecutter's extra_context as a Python bool. Jinja
compares it as-is, and `True == "True"` is False, so the CDN branch was
taken in both modes: `--no-cdn` downloaded, cached and shipped ~15 MB of
Pyodide that the browser then ignored.

Only pyodideUrl was affected. `flet.noCdn` is derived separately, via
`"{{ cookiecutter.no_cdn }}".toLowerCase() == "true"`, which renders
correctly - so CanvasKit and the fallback fonts loaded locally as
expected while Pyodide alone went to the CDN, which made this look like
a Pyodide quirk rather than a template bug.

The template's other no_cdn test (assets/FontManifest.json) already used
plain truthiness. All conditions now do, and no `== "True"` comparisons
remain under templates/.

Also fixes the bundled URL being origin-absolute: with `--base-url
myapp` the app requested /pyodide/pyodide.mjs while the file sat at
/myapp/pyodide/pyodide.mjs. It now renders relative to the configured
base URL, as canvasKitBaseUrl does; without --base-url the rendered URL
is unchanged.
In CDN mode (the default) Flutter loads CanvasKit from gstatic and Flet
points pyodideUrl at jsdelivr, so neither local copy is ever requested -
yet `flutter build web` always emits canvaskit/ (~37 MB) and
ensure_pyodide() ran unconditionally in both `flet build web` and `flet
publish`, downloading and copying a further ~15 MB.

`flet build web` and `flet publish` now drop both in CDN mode. A minimal
web build goes from 71 MB to 19 MB. `flet build` also clears a pyodide/
left in the reused Flutter project by an earlier --no-cdn build, so
switching modes doesn't silently keep shipping it. --no-cdn (or
[tool.flet.web] cdn = false) still bundles everything, unchanged.

Also decouples where a runtime asset is fetched from from what the build
bundled. flutter_bootstrap.js applied canvasKitBaseUrl and
fontFallbackBaseUrl only inside `if (flet.noCdn)`, so a host serving its
own copy of the runtime had to also set noCdn for the assignment to take
effect - setting the URL alone failed silently and the app booted off
gstatic anyway. Both are now applied whenever set, default to null in
CDN mode, and are pinned by flet build / patch_index.py only when
bundling.

FletJS.canvasKitBaseUrl becomes String? to match. It has no readers in
the Dart tree, so this is an annotation fix only.

Verified end to end on a minimal app: CDN mode builds to 19 MB with no
canvaskit/ or pyodide/, boots in headless Chrome, and its network log
shows chromium/canvaskit.{js,wasm} from gstatic plus the full Pyodide
runtime from jsdelivr, with zero requests to a local canvaskit/ or
pyodide/ path. --no-cdn builds to 71 MB with both directories present
and local URLs, and boots.

@sourcery-ai sourcery-ai 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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying flet-website-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: eaa316f
Status: ✅  Deploy successful!
Preview URL: https://c040830e.flet-website-v2.pages.dev
Branch Preview URL: https://fix-web-skip-bundling-cdn-as.flet-website-v2.pages.dev

View logs

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.

1 participant