@@ -8,6 +8,7 @@ import {zod} from '@shopify/cli-kit/node/schema'
88import { createRequire } from 'module'
99
1010const require = createRequire ( import . meta. url )
11+ const generatedTypesHelperImportPath = '@shopify/ui-extensions/admin'
1112
1213export function parseApiVersion ( apiVersion : string ) : { year : number ; month : number } | null {
1314 const [ year , month ] = apiVersion . split ( '-' )
@@ -197,99 +198,17 @@ function buildShopifyType(targets: string[], {includesTools, includesIntents}: S
197198 return baseShopifyType
198199 }
199200
200- const wrappers = [
201- ...( includesIntents ? [ 'WithGeneratedIntents' ] : [ ] ) ,
202- ...( includesTools ? [ 'WithGeneratedTools' ] : [ ] ) ,
203- ]
204-
205- return wrappers . reduce ( ( shopifyType , wrapper ) => `${ wrapper } <${ shopifyType } >` , baseShopifyType )
206- }
207-
208- function buildShopifyUtilityTypes ( { includesTools, includesIntents} : ShopifyTypeOptions ) : string {
209- const utilityTypes : string [ ] = [ ]
210-
211- if ( includesTools ) {
212- utilityTypes . push ( `interface GeneratedToolsConstraint<Tools> {
213- tools?: Tools;
214- }
215-
216- interface GeneratedToolsOverride<Tools> {
217- tools: Omit<NonNullable<Tools>, 'register'> & ShopifyTools;
218- }
219-
220- interface GeneratedToolsFallback {
221- tools: ShopifyTools;
222- }
223-
224- type WithGeneratedTools<T> = T extends GeneratedToolsConstraint<infer Tools>
225- ? Omit<T, 'tools'> & GeneratedToolsOverride<Tools>
226- : T & GeneratedToolsFallback;` )
227- }
201+ let shopifyType = baseShopifyType
228202
229203 if ( includesIntents ) {
230- utilityTypes . push ( `interface GeneratedIntentResponseConstraint<Response> {
231- response?: Response;
232- }
233-
234- interface GeneratedIntentResponseOverride<BaseResponse, GeneratedResponse> {
235- response?: Omit<NonNullable<BaseResponse>, 'ok'> & NonNullable<GeneratedResponse>;
236- }
237-
238- interface GeneratedIntentResponseFallback<GeneratedResponse> {
239- response?: NonNullable<GeneratedResponse>;
240- }
241-
242- interface GeneratedIntentRequestConstraint<Request> {
243- request: Request;
244- }
245-
246- type ReplaceSubscribableValue<Base, Value> = Base extends {value: unknown; subscribe: (callback: (value: infer _) => void) => () => void}
247- ? Omit<Base, 'value' | 'subscribe'> & {
248- readonly value: Value;
249- subscribe: (callback: (value: Value) => void) => () => void;
250- }
251- : {
252- readonly value: Value;
253- subscribe: (callback: (value: Value) => void) => () => void;
254- };
255-
256- interface GeneratedIntentsConstraint<Intents> {
257- intents?: Intents;
258- }
259-
260- interface GeneratedIntentsOverride<Intents> {
261- intents: Omit<NonNullable<Intents>, 'request' | 'response'> &
262- MergeGeneratedIntentResponse<NonNullable<Intents>>;
263- }
264-
265- interface GeneratedIntentsFallback {
266- intents: ShopifyGeneratedIntentVariants;
267- }
204+ shopifyType = `import('${ generatedTypesHelperImportPath } ').WithGeneratedIntents<${ shopifyType } , ShopifyGeneratedIntentVariants>`
205+ }
268206
269- type MergeGeneratedIntentResponse<Intents> =
270- ShopifyGeneratedIntentVariants extends infer Generated
271- ? Generated extends GeneratedIntentRequestConstraint<infer GeneratedRequest>
272- ? Omit<Generated, 'request' | 'response'> & {
273- request: Intents extends GeneratedIntentRequestConstraint<infer BaseRequest>
274- ? ReplaceSubscribableValue<BaseRequest, GeneratedRequest | null>
275- : {
276- readonly value: GeneratedRequest | null;
277- subscribe: (callback: (value: GeneratedRequest | null) => void) => () => void;
278- };
279- } & (Generated extends GeneratedIntentResponseConstraint<infer GeneratedResponse>
280- ? Intents extends GeneratedIntentResponseConstraint<infer BaseResponse>
281- ? GeneratedIntentResponseOverride<BaseResponse, GeneratedResponse>
282- : GeneratedIntentResponseFallback<GeneratedResponse>
283- : unknown)
284- : Generated
285- : never;` )
286-
287- utilityTypes . push ( `type WithGeneratedIntents<T> = T extends GeneratedIntentsConstraint<infer Intents>
288- ? Omit<T, 'intents'> & GeneratedIntentsOverride<Intents>
289- : T & GeneratedIntentsFallback;` )
207+ if ( includesTools ) {
208+ shopifyType = `import('${ generatedTypesHelperImportPath } ').WithGeneratedTools<${ shopifyType } , ShopifyTools>`
290209 }
291210
292- return utilityTypes . join ( '\n\n' )
211+ return shopifyType
293212}
294213
295214export function createTypeDefinition ( {
@@ -301,6 +220,9 @@ export function createTypeDefinition({
301220 intentsTypeDefinition,
302221} : CreateTypeDefinitionOptions ) : string | null {
303222 try {
223+ const includesTools = Boolean ( toolsTypeDefinition )
224+ const includesIntents = Boolean ( intentsTypeDefinition )
225+
304226 // Validate that all targets can be resolved
305227 for ( const target of targets ) {
306228 try {
@@ -315,21 +237,27 @@ export function createTypeDefinition({
315237 }
316238 }
317239
318- const relativePath = relativizePath ( fullPath , dirname ( typeFilePath ) )
319- const includesTools = Boolean ( toolsTypeDefinition )
320- const includesIntents = Boolean ( intentsTypeDefinition )
240+ if ( includesTools || includesIntents ) {
241+ try {
242+ require . resolve ( generatedTypesHelperImportPath , { paths : [ fullPath , typeFilePath ] } )
243+ } catch ( _ ) {
244+ const { year, month} = parseApiVersion ( apiVersion ) ?? { year : 2025 , month : 10 }
245+ throw new AbortError (
246+ `Type reference for ${ generatedTypesHelperImportPath } could not be found. You might be using the wrong @shopify/ui-extensions version.` ,
247+ `Fix the error by ensuring you have the correct version of @shopify/ui-extensions, for example ~${ year } .${ month } .0, in your dependencies.` ,
248+ )
249+ }
250+ }
321251
252+ const relativePath = relativizePath ( fullPath , dirname ( typeFilePath ) )
322253 const shopifyType = buildShopifyType ( targets , { includesTools, includesIntents} )
323254 if ( ! shopifyType ) return null
324255
325- const shopifyUtilityTypes = buildShopifyUtilityTypes ( { includesTools, includesIntents} )
326-
327256 const lines = [
328257 '//@ts-ignore' ,
329258 `declare module './${ relativePath } ' {` ,
330259 ...( toolsTypeDefinition ? [ toolsTypeDefinition ] : [ ] ) ,
331260 ...( intentsTypeDefinition ? [ intentsTypeDefinition ] : [ ] ) ,
332- ...( shopifyUtilityTypes ? [ shopifyUtilityTypes ] : [ ] ) ,
333261 ` const shopify: ${ shopifyType } ;` ,
334262 ' const globalThis: { shopify: typeof shopify };' ,
335263 '}' ,
@@ -453,20 +381,15 @@ export async function createIntentsTypeDefinition(intents: IntentTypeDefinition[
453381
454382 const generatedIntents = types
455383 . map ( ( { requestTypeName, outputTypeName} ) => {
456- return ` | ShopifyGeneratedIntentsApi <${ requestTypeName } , ${ outputTypeName } >`
384+ return ` | import(' ${ generatedTypesHelperImportPath } ').ShopifyGeneratedIntentVariant <${ requestTypeName } , ${ outputTypeName } >`
457385 } )
458386 . join ( '\n' )
459387
460388 return `${ types
461389 . map (
462390 ( { inputType, valueType, outputType, requestType} ) => `${ inputType } \n${ valueType } \n${ outputType } \n${ requestType } ` ,
463391 )
464- . join ( '\n\n' ) } \n\ninterface ShopifyGeneratedIntentResponse<Data = unknown> {
465- ok(data?: Data): Promise<void>;
466- }\n\ninterface ShopifyGeneratedIntentsApi<Request = unknown, ResponseData = unknown> {
467- request: Request;
468- response?: ShopifyGeneratedIntentResponse<ResponseData>;
469- }\n\ntype ShopifyGeneratedIntentVariants =\n${ generatedIntents } \n`
392+ . join ( '\n\n' ) } \n\ntype ShopifyGeneratedIntentVariants =\n${ generatedIntents } \n`
470393}
471394
472395/**
@@ -514,7 +437,7 @@ export async function createToolsTypeDefinition(tools: ToolDefinition[]): Promis
514437 . split ( '\n' )
515438 . map ( ( line ) => ` * ${ line } ` )
516439 . join ( '\n' )
517- return ` /**\n${ formattedDescription } \n */\n register(name: '${ name } ', handler: (input: ${ inputTypeName } ) => ${ outputTypeName } | Promise<${ outputTypeName } >);`
440+ return ` /**\n${ formattedDescription } \n */\n register(name: '${ name } ', handler: (input: ${ inputTypeName } ) => ${ outputTypeName } | Promise<${ outputTypeName } >): () => void ;`
518441 } )
519442 . join ( '\n' )
520443
0 commit comments