Skip to content

Add CDN loading with separate npm and CDN builds - #918

Merged
kiftio merged 3 commits into
mainfrom
dk/cdn-module-loader
Oct 9, 2026
Merged

kiftio merged 3 commits into
mainfrom
dk/cdn-module-loader

Conversation

@kiftio

@kiftio kiftio commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What changes are you making?

Add on-demand CDN loading to Checkout Kit Web, using the component structure and npm subpath introduced in #940. One package and version produce separate outputs: build:npm writes dist/, and build:cdn writes dist-cdn/. Packing builds only npm output; CDN assets stay out of the npm package.

<script type="module">
  import {loadComponents} from 'https://cdn.shopify.com/checkout-kit/v4/web-components.js';
  await loadComponents(['shopify-checkout']);
</script>

The loader imports only requested components. The URL is evergreen within a Checkout Kit major version, independent of the underlying protocol version; components share that library version. Hashed implementation chunks are deployment details. The loader also exports version and supportedComponents, validates requests before importing, shares concurrent loads, and retries transient import failures.

The npm registration entry remains:

import '@shopify/checkout-kit/shopify-checkout';

Both distributions use the component's register.ts, which registers through ShopifyCheckout.register() and leaves an existing <shopify-checkout> in place, so loading npm and CDN together does not throw. The npm root registers nothing (#940); the distribution tests cover the npm component entry and the npm root alongside the CDN loader, in both orders. No separate cdn-components/ source directory is needed.

Publishing: the release workflow builds and verifies both outputs, checks deploy permissions before npm publication, and uploads hashed CDN chunks before the loader. Stable non-prerelease releases published to npm latest update the major URL; other releases use the maintainer-only v<major>/unstable/ channel. Re-running the original release can redeploy or roll back its CDN assets. Deployment, caching, and rollback details are in platforms/web/CDN-PUBLISHING.md and platforms/web/RELEASING.md.

Known limitation: maintenance releases for older majors are not supported yet; this needs addressing before a new major ships.

Open question: the public filename remains web-components.js. This is a callable loader, unlike the Storefront side-effect bundle; the filename should be settled before first publication.

How to test

shadowenv exec -- dev web check
shadowenv exec -- dev web snapshot compare
shadowenv exec -- actionlint .github/workflows/web-publish.yml

Validation passed in the public checkout: 355 unit tests, 16 Chromium tests, 11 built-package tests, lint/typechecks, publint, the sample build, the packed-file snapshot comparison, and actionlint. Built-package tests cover consumer bundling and TypeScript resolution, isolated stable/unstable CDN layouts, and both npm/CDN loading orders.

After merge, a dry run from main can exercise deploy permissions without publishing. The first real prerelease deployment uses the unstable channel; the stable URL becomes available with 4.0.0.

@kiftio
kiftio requested a review from a team as a code owner October 6, 2026 13:13
@github-actions github-actions Bot added the #gsd:50662 Rebase Checkout Kit on UCP label Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Web — Coverage Report

Lines Statements Branches Functions
Coverage: 97%
95.58% (476/498) 86.59% (252/291) 97.43% (114/117)

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Bundle Size Budgets

Budget Size Limits Result
Web JavaScript (uncompressed) 35.58 KiB (0 B) 35 KiB soft / 50 KiB hard ➖ Above budget; no increase

Bundle and package size

Web bundle sizes cover shipped runtime JavaScript. Package sizes cover the full published archive, including any source maps, declarations, and documentation it contains.

Platform Measurement Compression Base Head Delta
Web JavaScript bundle Uncompressed 35.6 KiB 35.6 KiB 0 B
Web JavaScript bundle gzip 11.1 KiB 11.1 KiB 0 B
Web npm package (.tgz) gzip 99.1 KiB 99.4 KiB +296 B
Web package files (uncompressed)

These are uncompressed file sizes; they do not sum to the compressed package size above.

File Base Head Delta
dist/chunks/shopify-checkout.js.map 264.3 KiB 264.3 KiB 0 B
dist/custom-elements.json 54.7 KiB 54.7 KiB 0 B
dist/index.d.ts 49.5 KiB 49.5 KiB 0 B
dist/chunks/shopify-checkout.js 35.2 KiB 35.2 KiB 0 B
README.md 24.5 KiB 25.1 KiB +645 B
package.json 3.9 KiB 4.1 KiB +204 B
LICENSE 1.1 KiB 1.1 KiB 0 B
dist/shopify-checkout.js.map 850 B 850 B 0 B
dist/index.js 255 B 255 B 0 B
dist/shopify-checkout.js 156 B 156 B 0 B
dist/shopify-checkout.d.ts 33 B 33 B 0 B
How sizes are measured

Measured from the PR base SHA and PR head SHA. Web bundle rows sum shipped .js, .mjs, and .cjs files under dist/, excluding source maps and declarations. The gzip bundle size sums files compressed individually with gzip -n -9. npm package sizes are gzip-compressed .tgz archives; Android AAR sizes are ZIP archives. Package sizes are not final app binary sizes.

@kiftio
kiftio force-pushed the dk/cdn-module-loader branch 2 times, most recently from 3a429e4 to 26f4b7b Compare October 7, 2026 08:51
@kiftio

kiftio commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

/accept-size web Adds a second distribution entrypoint (CDN loader web-components.js, 969 B) and an npm re-export shim (276 B); the shared component chunk grew 104 B for the registration guard and chunk
boundary. No consumer downloads both entrypoints: npm consumers see +380 B, CDN consumers had nothing before.

@kiftio
kiftio force-pushed the dk/cdn-module-loader branch from 3f10675 to 7933df8 Compare October 8, 2026 09:24
@kiftio
kiftio changed the base branch from main to dk/web-component-structure October 8, 2026 09:24
@kiftio
kiftio added this pull request to stack #941 October 8, 2026 09:24
@kiftio kiftio changed the title Add Checkout Kit CDN module loader Add CDN loading with separate npm and CDN builds Oct 8, 2026
@kiftio
kiftio force-pushed the dk/cdn-module-loader branch from 7933df8 to a4dcd0f Compare October 8, 2026 11:12
@kiftio
kiftio removed this pull request from stack #941 October 8, 2026 11:12
@kiftio
kiftio added this pull request to stack #945 October 8, 2026 11:12
@bitrise

bitrise Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Bitrise builds

E2E · iOS CI

Checkout Kit E2E results

No native E2E runs were selected for this change.

@kiftio
kiftio force-pushed the dk/cdn-module-loader branch from a4dcd0f to 49161fc Compare October 8, 2026 11:17
@kiftio
kiftio force-pushed the dk/cdn-module-loader branch 3 times, most recently from 256faaa to c845519 Compare October 8, 2026 14:23
@kiftio
kiftio removed this pull request from stack #945 October 8, 2026 14:29
@kiftio
kiftio added this pull request to stack #959 October 8, 2026 14:29
@kiftio
kiftio force-pushed the dk/cdn-module-loader branch from c845519 to ca87750 Compare October 8, 2026 15:02
@kiftio
kiftio force-pushed the dk/cdn-module-loader branch from ca87750 to 9840674 Compare October 8, 2026 15:05
@kiftio
kiftio force-pushed the dk/cdn-module-loader branch 2 times, most recently from 0e63e4a to 86273a5 Compare October 8, 2026 19:25
Base automatically changed from dk/web-component-structure to main October 9, 2026 09:04
@kiftio
kiftio force-pushed the dk/cdn-module-loader branch from 86273a5 to 11c0d2c Compare October 9, 2026 09:04
Comment thread platforms/web/scripts/cdn-release-policy.mjs
Comment thread platforms/web/src/cdn-loader.test.ts Outdated
Comment thread platforms/web/src/cdn-loader.ts
Comment thread platforms/web/src/cdn-loader.ts Outdated
Comment thread platforms/web/CDN-PUBLISHING.md Outdated
Comment thread platforms/web/CDN-PUBLISHING.md Outdated
Comment thread platforms/web/README.md Outdated
@kiftio
kiftio force-pushed the dk/cdn-module-loader branch 2 times, most recently from 3fbfec5 to 5a5f78e Compare October 9, 2026 09:56
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Coverage Report

Status Platform / target Lines Branches Functions Report
✅ Web 98% 86.6% 97.44% Full report
⏭️ React Native — — — Not run for this change
⏭️ Embedded Checkout Protocol (TS) — — — Not run for this change

kiftio added 3 commits October 9, 2026 11:44
Publish Checkout Kit Web to Shopify's CDN alongside npm. A major-versioned
ES module loader at /checkout-kit/v<major>/web-components.js registers
components on demand via loadComponents(['shopify-checkout']); component
names are the custom element tags they register. A maintainer-only
/v<major>/unstable/ channel exists for exercising prereleases.

Builds: `build:npm` writes dist/ (root plus the shopify-checkout entry,
sharing one chunk) and `build:cdn` writes dist-cdn/ (loader plus a
self-contained hashed component chunk). Packing builds only npm output, so
CDN assets never enter the package. Both distributions register through
the component's register.ts, which leaves an existing <shopify-checkout>
in place, so loading npm and CDN together does not throw.

Loader: validates every name before fetching, shares in-flight loads,
remembers loaded components, and retries failed imports through a unique
cache-busting URL per sequence (browsers remember a failed import per URL
for the life of the page). A build plugin injects the hashed chunk URL.
Exports `version` and `supportedComponents`.

Release workflow:
- Content-hashed chunks upload before the loader that references them.
  The stable URL updates only for a non-prerelease version, not flagged as
  a GitHub prerelease, published to npm `latest`; everything else goes to
  the unstable channel for its major.
- A token-only pre-flight confirms the deploy identity holds the bucket
  permissions the upload needs, before anything irreversible happens and
  without leaving credentials on disk for package code. Full
  authentication happens only after npm publication; `npm publish` runs
  with --ignore-scripts so the verified dist/ is what ships.
- The deploy identity and bucket are npm-web environment secrets rather
  than committed values: identifiers, not credentials, but masked in logs
  and readable only by jobs declaring the environment. The workflow fails
  early, without printing values, if any is missing.
- Re-running a release (or a failed manual run) redeploys the CDN assets
  for an already-published version, which doubles as rollback. A fresh
  manual dispatch of an already-published version is refused, since main
  may no longer match the published tarball.
- The CDN route applies one cache policy to every /checkout-kit/ path, so
  the workflow sets no per-object Cache-Control and the docs describe the
  real ~30 minute propagation window and a bad-deploy runbook.

Known limitation: no supported path yet for patching an older major after
a new major ships; tracked separately.
… trim docs

- Parse the package version with semver instead of string splitting.
- Dedupe repeated component names in one loadComponents() call explicitly.
- Retry base delay 250 ms -> 100 ms.
- Drop the literal unstable URL and the "alternative not adopted" section
  from CDN-PUBLISHING.md; remove the hypothetical v5 example from README.
Silently resolving left it unclear whether nothing or everything had been
loaded. Nothing is ever loaded implicitly, so an empty list is a mistake
and now throws.
@kiftio
kiftio force-pushed the dk/cdn-module-loader branch from 5a5f78e to 098dbfd Compare October 9, 2026 10:44
@kiftio
kiftio merged commit 63cf08f into main Oct 9, 2026
25 checks passed
@kiftio
kiftio deleted the dk/cdn-module-loader branch October 9, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

#gsd:50662 Rebase Checkout Kit on UCP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants