Detect ShopifyGlobal re-export, emit intersection in shopify.d.ts#7387
Merged
Detect ShopifyGlobal re-export, emit intersection in shopify.d.ts#7387
Conversation
…Global The CLI-generated shopify.d.ts now types the `shopify` binding as `Api & ShopifyGlobal` for UI extension targets whose .d.ts re-exports a type named `ShopifyGlobal`. Detection is AST-based via the typescript compiler API (already a dependency), matching on the public export name `ShopifyGlobal` so the CLI does not need to know about specific surfaces or targets. Targets that do not re-export `ShopifyGlobal` emit byte-identical output to main. Existing consumers who access the target API via `shopify.*` are unaffected. Net effect: host-level APIs like `shopify.addEventListener` now type-check automatically for opt-in targets (e.g. POS background extensions) without any CLI release coordination when new targets opt in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Updates CLI-generated UI extension type declarations so the shopify binding can include host-level APIs when a target opts in by re-exporting ShopifyGlobal. This aligns the generated shopify.d.ts with POS background extension runtime behavior while keeping existing targets byte-identical.
Changes:
- Detects
ShopifyGlobalre-exports in a resolved target file using the TypeScript compiler API. - Emits
Api & ShopifyGlobalfor opt-in targets; keepsApionly for all others. - Adds unit tests covering both the opt-in intersection case and the regression “no export → unchanged output” case.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/app/src/cli/models/extensions/specifications/type-generation.ts | Adds ShopifyGlobal re-export detection and conditionally emits an intersection type for shopify. |
| packages/app/src/cli/models/extensions/specifications/ui_extension.test.ts | Extends the node_modules mock helper and adds tests for both intersection and non-intersection outputs. |
| .changeset/shopify-global-type-detection.md | Documents the patch change in generated shopify.d.ts behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adds a third test case that uses a fabricated target name belonging to no real surface. Documents in the test body that the detector has no allowlist and no hard-coded target — any surface can opt in by shipping the same export shape from its target `.d.ts`. Softens the POS-specific phrasing in the first test's comment; POS is the first adopter, not a special case.
Api & ShopifyGlobal intersection in shopify.d.ts when target re-exports ShopifyGlobal
isaacroldan
approved these changes
Apr 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes https://github.com/shop/issues-retail/issues/28334
Companion ui-extension PR
Summary
When a
@shopify/ui-extensionstarget.d.tsre-exports aShopifyGlobaltype, the CLI-generatedshopify.d.tsnow types theshopifybinding asApi & ShopifyGlobalinstead of justApi. Targets that do not re-exportShopifyGlobalare unchanged.This unblocks POS background extensions, where the runtime
shopifyobject exposes both the target's data surface (shopify.cart,shopify.products, etc.) and host-level event APIs (shopify.addEventListener). Existing consumers are unaffected — the.Apihalf of the intersection preserves their current typing.Screen Recording 2026-04-23 at 3.47.17 PM.mov (uploaded via Graphite)
Detection
typescriptcompiler API (already a dependency of this file).ShopifyGlobal(matchesexport type {ShopifyGlobal} from '../globals',export {ShopifyGlobal}, andexport {Foo as ShopifyGlobal}).Scope — who's affected
ShopifyGlobalinglobals.tstoday?.d.tsafter companion PR?Api→Api & ShopifyGlobalOther surfaces: nothing breaks; no action required. The regression test added in this PR asserts that targets without a
ShopifyGlobalre-export emit the exact same output as before.Why POS is the first adopter: POS background extensions are the first pattern that writes code accessing both the target
Api(via e.g.shopify.cart.current) and host-level APIs (viashopify.addEventListener) through the sameshopifyidentifier in an entry file that also imports from@shopify/ui-extensions/<target>. That combination exposes a module-shadowing issue in the CLI-generatedshopify.d.tsthat other surfaces haven't hit yet. Any surface that later needs the same pattern can opt in with a one-line change to their branch ofbuildTargetDts.tsin ui-extensions — no CLI release required.Backwards compatibility
ShopifyGlobalre-export: output byte-identical to main.@shopify/ui-extensionsversions (noShopifyGlobalexport on any target): graceful fallback to plain.Apifor every target.shopify.<target-api-field>usage: unchanged — intersection, not replacementHow to test
Before / after in generated
shopify.d.ts— this is the only behavior change:declare module './src/BackgroundExtension.ts' { - const shopify: import('@shopify/ui-extensions/pos.app.ready.data').Api; + const shopify: import('@shopify/ui-extensions/pos.app.ready.data').Api & + import('@shopify/ui-extensions/pos.app.ready.data').ShopifyGlobal; const globalThis: { shopify: typeof shopify }; }For a live regeneration: link a POS extension to the local ui-extensions companion branch (pnpm
overrides.@shopify/ui-extensions: link:...), runpnpm shopify app build --path <app>from inside the CLI repo, and inspectshopify.d.ts. Full CLI setup per vault BKMy.Test plan
ShopifyGlobal.Apiemitted when target does not re-exportShopifyGlobal(regression guard).ui_extension.test.tssuite passes (45/45).shopify.addEventListenerandshopify.cart.current.valueboth type-check against the same generatedshopify.d.ts.Rollback
Revert this PR. No migration required — targets without the companion ui-extensions change are byte-identical to main, and targets with it fall back to the pre-PR output shape on the prior CLI version.t use a certa