Skip to content

Update extension templates to Bundler module resolution - #424

Open
akhayoon wants to merge 4 commits into
mainfrom
codex/fix-pos-template-module-resolution
Open

Update extension templates to Bundler module resolution#424
akhayoon wants to merge 4 commits into
mainfrom
codex/fix-pos-template-module-resolution

Conversation

@akhayoon

@akhayoon akhayoon commented Aug 28, 2026

Copy link
Copy Markdown

What

Update every generated extension template still using legacy Node module resolution to emit ES2015 modules and use TypeScript's Bundler module resolution.

Why

TypeScript 6 rejects the deprecated node resolution mode with TS5107. Bundler correctly understands modern package imports and exports, including @shopify/ui-extensions/preact.

ES2015 is intentional. Shopify CLI bundles UI extensions for an ES2015 target. Using ESNext would allow import.meta through TypeScript even though the CLI bundler replaces it with an empty object, which can produce a deployable runtime failure.

Resolves shop/issues-retail#33804.

Changes

  • Add "module": "ES2015" to the 18 templates moving from Node resolution.
  • Change "moduleResolution": "node" to "moduleResolution": "Bundler".
  • Keep discount-details-function-settings-block on its existing ESNext plus Bundler configuration so valid dynamic imports remain supported there.
  • Intentionally adopt TypeScript 5 or newer as the baseline for the 18 migrated configurations because Bundler was introduced in TypeScript 5.

These configs use noEmit, so their primary effect is TypeScript checking and editor resolution. Existing generated projects are unchanged.

Review-driven investigation

This started with the three POS templates affected by shop/issues-retail#33804. Reviewer feedback prompted a repository-wide audit, which found 18 templates still using legacy Node resolution and expanded the fix to cover all of them consistently.

The implementation was then refined through two compatibility checks:

  • ESNext plus Bundler fixed TypeScript resolution, but allowed import.meta even though Shopify CLI cannot preserve it safely for the ES2015 runtime target. The 18 migrated templates therefore use ES2015, which rejects that unsupported construct during type-checking.
  • Applying ES2015 to the existing discount settings template rejected its valid dynamic imports with TS1323. That unrelated change was removed, leaving the discount template on its established ESNext plus Bundler configuration.

The TypeScript 5 baseline was also checked against current Shopify extension consumers rather than assumed:

The exact final head is 64c651898afd5d278714661c85db0f91531f57b3. It contains only the 18 intended migrations; the discount settings template is not part of the final diff.

Testing

  • TypeScript 6.0.2 reproduces TS5107 with the previous Node configuration.
  • ES2015 plus Bundler resolves @shopify/ui-extensions/preact under TypeScript 5.0.4, 5.9.3, and 6.0.2.
  • All 19 Bundler configs render, parse, retain noEmit: true, and validate under those three TypeScript versions: 18 use ES2015, and the existing discount settings template retains ESNext.
  • All alternate Liquid flavor branches remain unchanged.
  • An import.meta.url regression test for the 18 migrated templates is rejected by TypeScript with TS1343. Under ESNext, the same test passed TypeScript, bundled successfully with a warning, and crashed at runtime with TypeError: Invalid URL.
  • A dynamic import compiles successfully with the restored discount settings configuration; changing that existing template to ES2015 reproduces TS1323 under TypeScript 5.0.4, 5.9.3, and 6.0.2.
  • Six real entry points across POS, Checkout, Admin, Customer Account, and App Data produce byte-identical bundles before and after the configuration change.
  • No affected template currently uses import.meta or dynamic imports.
  • No legacy moduleResolution: "node" settings remain.
  • git diff --check passes.

Risk

Low for production. Existing projects are not modified, representative runtime bundles are byte-identical, and the configuration now rejects a module feature that Shopify CLI's ES2015 bundle target cannot preserve safely.

TypeScript 4.9 and older cannot read moduleResolution: "Bundler" and fail with TS6046. TypeScript 5 or newer is an intentional baseline for newly generated extensions using these 18 migrated configurations. This is an accepted development-tooling compatibility boundary rather than a runtime change, and it matches the TypeScript baseline in Shopify's current typed extension examples and JavaScript app templates.

Checklist

  • I have 🎩'd these changes
  • I have squashed my commits into chunks of work with meaningful commit messages

Co-authored by AI.

Generated Preact POS extensions currently use Node module resolution, which TypeScript deprecates and which does not understand modern package exports consistently. Configure all shipped POS templates to emit ESNext modules and use Bundler resolution, matching the repository's existing extension-template precedent.\n\nResolves shop/issues-retail#33804.

Copy link
Copy Markdown

It looks like there are lots more templates with this issue than just ours from https://github.com/search?q=repo%3AShopify%2Fextensions-templates+"moduleResolution"%3A+"node"&type=code. Should we make this a wider update? And if so, use that to align on these being the correct config options (that is what the AI recommended, but I haven't dug into if its right)

@akhayoon akhayoon changed the title Fix POS extension template module resolution Update extension templates to Bundler module resolution Sep 3, 2026
@akhayoon

akhayoon commented Sep 3, 2026

Copy link
Copy Markdown
Author

Great callout @henryStelle. I added for the rest.

@henryStelle henryStelle left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I looked up these tsconfig options and these look like the correct changes. It would probably be good to get an 👍 from at least one of the other extension teams.

@akhayoon

akhayoon commented Sep 8, 2026

Copy link
Copy Markdown
Author

Exact-head terminal verification

Read-only local reproduction against 64c651898afd5d278714661c85db0f91531f57b3.

  • Final scope: 18 intended migrations, discount template absent.
  • Shopify CLI: 20/20 build commands passed; all compared artifacts are byte-identical.
  • TypeScript: migrated configs accepted by 5.0.4, 5.9.3, and 6.0.3; the import.meta witness is rejected with TS1343.

These are local reproduction results, not hosted CI results.

Exact final head, changed-file scope, and live PR state

Shopify CLI build commands and byte-for-byte artifact comparison

TypeScript configuration matrix and import.meta rejection

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.

3 participants