Skip to content

Generate types for shared folders#6957

Open
justinhenricks wants to merge 4 commits intomainfrom
generate-for-shared-folders
Open

Generate types for shared folders#6957
justinhenricks wants to merge 4 commits intomainfrom
generate-for-shared-folders

Conversation

@justinhenricks
Copy link
Copy Markdown

@justinhenricks justinhenricks commented Mar 9, 2026

WHY are these changes introduced?

UI extensions can import files from shared folders outside their own extension directory (e.g. a top-level shared/
folder in the app workspace). Previously, type generation was scoped per-extension - so shared files either didn't
get types at all, or got incomplete ones because each extension only knew about its own targets.

WHAT is this pull request doing?

Changes the internal data structure for type generation from storing pre-rendered strings (Map<string, Set<string>>) to storing structured definitions (Map<string, Map<string, SharedTypeDefinition>>). Rendering is
deferred to a new renderTypeDefinitions step that runs after all extensions have contributed their types.

This means when two extensions both import shared/utils.ts with different targets, the targets get merged into a
single union type declaration instead of one overwriting the other.

Other changes:

  • findNearestTsConfigDir now walks up to appDirectory instead of extensionDirectory, so it can find tsconfigs for shared folders
  • Extracted addTypeDefinition (handles target merging + API version tracking), assertTargetsResolvable, and
    renderTypeDefinitions as shared helpers
  • Cleaned up exports that are no longer needed outside the module

How to test your changes?

  • Clone https://github.com/shopify-playground/robin-cli-shared-types
  • This app has two shared folders that extensions import from. One inside extension/ and one outside. One of them is imported by both extneions, the other one only by one. I did this to test a more complex case
  • Delete any shopify.d.ts files that might exist rm **/shopify.d.ts
  • While in the cli folder
  • run pnpm shopify app build --path=/path/to/your-app (you might need to run this with --reset once, I'm not entirely sure. Make sure you have this setup as an app for you so you can run build)
  • Go your app
  • There should now be a shopify.d.ts in the root with this content
import '@shopify/ui-extensions';

//@ts-ignore
declare module './extensions/shared/math.js' {
  const shopify: import('@shopify/ui-extensions/purchase.checkout.block.render').Api;
  const globalThis: { shopify: typeof shopify };
}

//@ts-ignore
declare module './shared/math.js' {
  const shopify:
    | import('@shopify/ui-extensions/customer-account.order-status.block.render').Api
    | import('@shopify/ui-extensions/purchase.checkout.block.render').Api;
  const globalThis: { shopify: typeof shopify };
}

Measuring impact

  • n/a - this doesn't need measurement, e.g. a linting rule or a bug-fix

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes

@justinhenricks justinhenricks requested a review from a team as a code owner March 9, 2026 16:00
@github-actions

This comment has been minimized.

@binks-code-reviewer
Copy link
Copy Markdown

binks-code-reviewer bot commented Mar 9, 2026

🤖 Code Review · #projects-dev-ai for questions
React with 👍/👎 or reply — all feedback helps improve the agent.

Complete - No issues

📋 History

✅ 1 findings → ✅ No issues

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 9, 2026

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 82.42% 15175/18411
🟡 Branches 74.99% 7476/9969
🟢 Functions 81.44% 3814/4683
🟢 Lines 82.8% 14345/17324

Test suite run success

4009 tests passing in 1535 suites.

Report generated by 🧪jest coverage report action from 777268a

@robin-drexler robin-drexler force-pushed the generate-for-shared-folders branch from b207899 to d22ff3b Compare March 11, 2026 20:06
@robin-drexler
Copy link
Copy Markdown
Member

/snapit

@github-actions
Copy link
Copy Markdown
Contributor

🫰✨ Thanks @robin-drexler! Your snapshot has been published to npm.

Test the snapshot by installing your package globally:

npm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20260311200812

Caution

After installing, validate the version by running shopify version in your terminal.
If the versions don't match, you might have multiple global instances installed.
Use which shopify to find out which one you are running and uninstall it.

@robin-drexler robin-drexler force-pushed the generate-for-shared-folders branch from d22ff3b to 56adcb7 Compare March 27, 2026 19:07
@robin-drexler robin-drexler requested review from JoviDeCroock, andrewmcgov, kumar303 and vividviolet and removed request for andrewmcgov March 27, 2026 20:22
if (existingTypeDefinition) {
targets.forEach((target) => existingTypeDefinition.targets.add(target))
existingTypeDefinition.toolsTypeDefinition ??= toolsTypeDefinition
if (isApiVersionNewer(apiVersion, existingTypeDefinition.apiVersion)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is this for? We should just fail early because it's not expected for extensions on different api versions to share files

Copy link
Copy Markdown
Member

@robin-drexler robin-drexler Mar 30, 2026

Choose a reason for hiding this comment

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

@vividviolet what do you mean by "fail early" here btw? Actually throw or just silently do nothing?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wouldn't it be fine if the versions are different as long as the target exists? (Which is currently checked)

Otherwise, you'd force partners to always use the same version for all extensions, right? As it'd otherwise always abort(?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The types are generated per extension though. This code should only hit if they have shared imports. We shouldn't encourage sharing code between different major versions. When the types are not compatible they'd have to do a custom type guard to make it work. Even when the types are compatible it just seems risky to allow this. The toml's api version also sets the default GraphQL version which might not be compatible either.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@vividviolet how do you suggest should the CLI behave if we encounter different versions?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@vividviolet The more I think about this, the less I love breaking the build in that situation. It would even break if you use shared code and don't even rely on global APIs at all.

Unless I'm misunderstanding your suggestion.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ok fair. I will settle for a warning then and clearly tell them we're using the newer apiVersion since there are multiple

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@vividviolet

we're using the newer apiVersion

are we really using a newer version, tho?

In the end, we create a shopify.d.ts file with something like:

  import '@shopify/ui-extensions';

  //@ts-ignore
  declare module './shared/utils.ts' {
    const shopify: (import('@shopify/ui-extensions/admin.orders-details.block.render').Api
      | import('@shopify/ui-extensions/admin.product-details.action.render').Api);
    const globalThis: { shopify: typeof shopify };
  }

What '@shopify/ui-extensions` resolves to is entirely up to where the closest tsconfig is and the node module resolution process, no?

So if shared-package has a package.json with '@shopify/ui-extensions in it (and a tsconfig), that version will be used.
If not, it'll go up to the root and use whatever version is in there.

Which versions the extensions declare for '@shopify/ui-extensions is almost irrelevant 😬

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wait, what is isApiVersionNewer checking then? Is just checking eligibility for type generation?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think that's only used for the error message generated here: https://github.com/Shopify/cli/blob/generate-for-shared-folders/packages/app/src/cli/models/extensions/specifications/type-generation.ts#L247-L250

@JoviDeCroock can you maybe chime in on this thread too? Just wanna make sure I'm getting this all right.

typeDefinitionsByFile.set(typeFilePath, currentTypeDefinitions)
}

export async function renderTypeDefinitions(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: can we rename this to something else? we're not rendering but returning content right?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

getTypeDefinitions?

@kumar303 kumar303 removed their request for review March 30, 2026 11:02
@robin-drexler robin-drexler force-pushed the generate-for-shared-folders branch from 56adcb7 to 8621f6e Compare March 30, 2026 13:09
Copy link
Copy Markdown
Member

This stack of pull requests is managed by Graphite. Learn more about stacking.

@robin-drexler robin-drexler force-pushed the generate-for-shared-folders branch from 8621f6e to 777268a Compare March 30, 2026 13:11
@robin-drexler robin-drexler requested a review from a team as a code owner March 30, 2026 13:11
@robin-drexler robin-drexler force-pushed the generate-for-shared-folders branch from 0028359 to 08fbe50 Compare March 31, 2026 00:07
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.

4 participants