Skip to content

Organize Web components and register through component entries - #940

Open
kiftio wants to merge 3 commits into
dk/web-tag-name-typesfrom
dk/web-component-structure
Open

kiftio wants to merge 3 commits into
dk/web-tag-name-typesfrom
dk/web-component-structure

Conversation

@kiftio

@kiftio kiftio commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What changes are you making?

Organize Checkout Kit Web by component, register components through their own entry points, and make the package root side-effect free. This follows the common web component package pattern (Shoelace/Web Awesome, Material Web, Spectrum): component imports register, and the root exports classes, events, and types.

Consumer impact (breaking): import '@shopify/checkout-kit' no longer registers <shopify-checkout>. Register it in one of two ways:

// Registers <shopify-checkout>
import '@shopify/checkout-kit/shopify-checkout';

// Or: import from the root (registers nothing), then register yourself,
// optionally with a custom tag name
import {ShopifyCheckout} from '@shopify/checkout-kit';
ShopifyCheckout.register();                // <shopify-checkout>
ShopifyCheckout.register('acme-checkout'); // returns a subclass for <acme-checkout>

All root exports remain available. Without either registration, <shopify-checkout> elements stay unregistered and checkout won't open, so this needs a release note. The package is on 4.0.0-alpha, and the README calls it out.

Reviewing: the last commit, "Make the root import side-effect free and add ShopifyCheckout.register()", contains the registration change on top of the previously approved version.

  • Move checkout implementation, styles, events, types, and tests into src/components/shopify-checkout/. shopify-checkout.ts contains the implementation; register.ts handles registration. Use direct file imports without component barrel files.
  • Keep shared models and helpers outside component directories. Additional components can follow this structure when added.
  • Build both npm entry points with a shared implementation chunk. Only the component entry has side effects; the shared chunk (now chunks/shopify-checkout.js) and the root are pure, so sideEffects lists only the component entry. This also answers whether every chunk has side effects: none do now.
  • Add ShopifyCheckout.register(name?, registry?):
    • the default name registers the class itself; each custom name gets its own subclass, since a registry accepts a constructor only once;
    • registering a name this class already owns returns that class, so it's safe to call more than once;
    • a name owned by another element throws.
  • The component entry registers through register() and leaves an existing <shopify-checkout> in place, so loading Checkout Kit twice (for example a bundle plus the CDN loader) doesn't fail.
  • Keep the custom elements manifest's tag-to-class definition: the analyzer can't follow register(), so a manifest plugin recreates it from the class's @tagname.
  • Update documentation, the sample, browser fixtures, declarations, and the packed-file snapshot for the explicit import.
  • Ship the tag-name typing with the component entry: dist/shopify-checkout.d.ts loads the root declarations, so import '@shopify/checkout-kit/shopify-checkout' alone types document.createElement('shopify-checkout') as ShopifyCheckout (building on Ship custom element tag-name typing in the published declarations #958).
  • Exclude every type-only .types.ts module from the dependency-cruiser reachability rule, not only checkout.types.ts.

This builds on #958 (published tag-name typing); #918 adds separate CDN output and on-demand loading.

How to test

shadowenv exec -- dev web check
shadowenv exec -- dev web snapshot compare

Validated with 337 unit tests, 16 Chromium tests, 9 built-package tests, lint/typechecks, publint, the sample build, and the packed-file snapshot comparison. The 22 changed-file filter tests also passed. Consumer bundling tests cover every import combination:

  • component import;
  • root alone (registers nothing);
  • root then register();
  • root then a custom tag name;
  • component import plus a custom tag name (both tags);
  • an existing <shopify-checkout> on the page.

They also verify that the component entry doesn't import the package root, and type-check strict TypeScript consumers without casts for both import styles. Unit tests cover register() directly.

@kiftio
kiftio added this pull request to stack #941 October 8, 2026 09:24
@github-actions github-actions Bot added the #gsd:50662 Rebase Checkout Kit on UCP label Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Bundle Size Budgets

Budget Size Limits Result
Web JavaScript (uncompressed) 35.54 KiB (+557 B) 35 KiB soft / 50 KiB hard ⚠️ Acceptance required

Repository writers, including the PR author, can accept current soft-budget breaches with a reason:

/accept-size web Explain why this increase is necessary.

Use one command per platform; several lines can share a comment. Post after this report is ready for the current head. Commands in edited comments are not accepted.

Acceptance applies to each currently exceeded metric up to its recorded size. Further growth or a newly exceeded metric needs fresh acceptance. Hard-budget increases require a reviewed change to .ci/bundle-size-budgets.json.

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.0 KiB 35.5 KiB +557 B
Web JavaScript bundle gzip 10.8 KiB 11.1 KiB +368 B
Web npm package (.tgz) gzip 93.1 KiB 95.7 KiB +2.6 KiB
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 — 257.0 KiB +257.0 KiB
dist/index.js.map 255.4 KiB — -255.4 KiB
dist/custom-elements.json 51.9 KiB 54.7 KiB +2.8 KiB
dist/index.d.ts 48.6 KiB 49.5 KiB +904 B
dist/chunks/shopify-checkout.js — 35.1 KiB +35.1 KiB
dist/index.js 35.0 KiB 307 B -34.7 KiB
README.md 21.7 KiB 24.5 KiB +2.8 KiB
package.json 3.6 KiB 3.9 KiB +353 B
LICENSE 1.1 KiB 1.1 KiB 0 B
dist/shopify-checkout.js.map — 832 B +832 B
dist/shopify-checkout.js — 156 B +156 B
dist/shopify-checkout.d.ts — 33 B +33 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 marked this pull request as ready for review October 8, 2026 10:23
@kiftio
kiftio requested a review from a team as a code owner October 8, 2026 10:23
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Web — Coverage Report

Lines Statements Branches Functions
Coverage: 98%
95.61% (436/456) 86.86% (238/274) 97.22% (105/108)

@kiftio

kiftio commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

/accept-size web Adds an explicit shopify-checkout npm entry point while preserving existing root imports and sharing the component implementation. Increase is pretty small (167 B)

@kiftio
kiftio force-pushed the dk/web-component-structure branch from 7619d5a to 004ea88 Compare October 8, 2026 11:12
@kiftio
kiftio removed this pull request from stack #941 October 8, 2026 11:12
@kiftio
kiftio changed the base branch from main to dk/e2e-report-empty-plan 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/web-component-structure branch 2 times, most recently from 012e7d9 to af406b3 Compare October 8, 2026 12:24
Base automatically changed from dk/e2e-report-empty-plan to main October 8, 2026 12:51
@kiftio
kiftio force-pushed the dk/web-component-structure branch from af406b3 to 1af0910 Compare October 8, 2026 12:51
"(^|/)node_modules/",
"^package\\.json$",
"^src/checkout\\.types\\.ts$",
"^src/components/shopify-checkout/checkout\\.types\\.ts$",

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.

Can we make this broadly target all .types.ts files?

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.

Done in 803048b: the reachability rule now excludes \.types\.ts$, with a comment that type-only modules are erased at compile time, so nothing reaches them at runtime.

Comment thread platforms/web/package.json Outdated
"./src/components/shopify-checkout/register.ts",
"./dist/index.js",
"./dist/shopify-checkout.js",
"./dist/chunks/*.js"

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.

Is it guaranteed that all chunks will contain side effects?

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.

Not guaranteed in general, but true today. The build emits one shared chunk, chunks/register.js, and that's where customElements.define runs (both entries import it). It has to stay flagged, or consumer bundlers can drop the import and the element never registers. The consumer bundling tests in scripts/npm-package.test.ts cover that for each import combination.

The glob errs in the safe direction: a pure chunk flagged by mistake only loses tree-shaking, while a missing flag silently breaks registration.

We're following up by making the root import side-effect free: component imports (@shopify/checkout-kit/shopify-checkout, the CDN loader) register, and the root exports the class plus ShopifyCheckout.register() for manual or custom-name registration. That moves the define into dist/shopify-checkout.js, so sideEffects can drop ./dist/chunks/*.js and the root entry.

@kiftio
kiftio force-pushed the dk/web-component-structure branch from 1af0910 to b2832d8 Compare October 8, 2026 14:23
@kiftio
kiftio removed this pull request from stack #945 October 8, 2026 14:29
@kiftio
kiftio changed the base branch from main to dk/web-tag-name-types 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/web-component-structure branch 2 times, most recently from 4266b84 to 803048b Compare October 8, 2026 15:05
@kiftio kiftio changed the title Organize Web components and add explicit checkout import Organize Web components and register through component entries Oct 8, 2026
kiftio added 3 commits October 8, 2026 20:25
- The component entry's declarations now load the root declarations, so
  `import "@shopify/checkout-kit/shopify-checkout"` alone types
  document.createElement("shopify-checkout") as ShopifyCheckout. The
  TypeScript consumer test no longer casts and covers both import styles.
- Exclude every `.types.ts` module from the reachability rule rather than
  only checkout.types.ts (review feedback).
Follow the common web component package pattern (Shoelace/Web Awesome,
Material Web, Spectrum): component imports register, and the package
root only exports classes, events, and types.

- `import "@shopify/checkout-kit"` no longer registers
  <shopify-checkout>. This is a breaking change for consumers relying on
  the root import to register; the README calls it out.
- Add `ShopifyCheckout.register(name?, registry?)`. The default name
  registers the class itself; each custom name gets its own subclass,
  since a registry accepts a constructor only once. Registering a name
  the class already owns returns that class; a name owned by another
  element throws.
- The component entry registers through `register()` and leaves an
  existing <shopify-checkout> in place, so loading Checkout Kit twice
  (bundle plus CDN loader) doesn't fail.
- `sideEffects` now lists only the component entry. The shared chunk is
  pure and is renamed from chunks/register.js to
  chunks/shopify-checkout.js.
- Keep the custom elements manifest's tag-to-class definition: the
  analyzer can't follow `register()`, so a manifest plugin recreates it
  from the class's `@tagname`.
- dependency-cruiser treats component entries as entry points.
- Package tests cover every import combination, including custom names
  and an existing <shopify-checkout> on the page.
@kiftio
kiftio force-pushed the dk/web-component-structure branch from f0ea1f1 to 3d2f45d Compare October 8, 2026 19:25

This branch has not been deployed

No deployments
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