Skip to content

Commit 4c784be

Browse files
committed
Formatting
1 parent 6f5a44e commit 4c784be

3 files changed

Lines changed: 144 additions & 56 deletions

File tree

packages/app/src/cli/models/extensions/specifications/type-generation.test.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,17 @@ interface CreateApplicationEmailIntentRequest {
5555
value?: CreateApplicationEmailIntentValue;
5656
}
5757
58-
type ShopifyGeneratedIntentResponse<Data = unknown> = {
58+
interface ShopifyGeneratedIntentResponse<Data = unknown> {
5959
ok(data?: Data): Promise<void>;
6060
}
6161
62-
type ShopifyGeneratedIntentsApi =
63-
| {
64-
request: CreateApplicationEmailIntentRequest;
65-
response?: ShopifyGeneratedIntentResponse<CreateApplicationEmailIntentOutput>;
66-
}
62+
interface ShopifyGeneratedIntentsApi<Request = unknown, ResponseData = unknown> {
63+
request: Request;
64+
response?: ShopifyGeneratedIntentResponse<ResponseData>;
65+
}
66+
67+
type ShopifyGeneratedIntentVariants =
68+
| ShopifyGeneratedIntentsApi<CreateApplicationEmailIntentRequest, CreateApplicationEmailIntentOutput>
6769
`)
6870
})
6971

@@ -109,7 +111,10 @@ type ShopifyGeneratedIntentsApi =
109111
expect(result).toContain('type CreateApplicationEmailIntentOutput = unknown')
110112
expect(result).toContain('interface EditShopifyProductIntentRequest')
111113
expect(result).toContain('type EditShopifyProductIntentValue = string')
112-
expect(result).toContain('response?: ShopifyGeneratedIntentResponse<EditShopifyProductIntentOutput>;')
114+
expect(result).toContain('response?: ShopifyGeneratedIntentResponse<ResponseData>;')
115+
expect(result).toContain(
116+
'ShopifyGeneratedIntentsApi<EditShopifyProductIntentRequest, EditShopifyProductIntentOutput>',
117+
)
113118
})
114119

115120
test('throws AbortError when intent action/type pairs are duplicated', async () => {

packages/app/src/cli/models/extensions/specifications/type-generation.ts

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -209,27 +209,62 @@ function buildShopifyUtilityTypes({includesTools, includesIntents}: ShopifyTypeO
209209
const utilityTypes: string[] = []
210210

211211
if (includesTools) {
212-
utilityTypes.push(`type WithGeneratedTools<T> = T extends {tools?: infer Tools}
213-
? Omit<T, 'tools'> & {tools: Omit<NonNullable<Tools>, 'register'> & ShopifyTools}
214-
: T & {tools: ShopifyTools}`)
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;`)
215227
}
216228

217229
if (includesIntents) {
218-
utilityTypes.push(`type MergeGeneratedIntentResponse<Intents> = ShopifyGeneratedIntentsApi extends infer Generated
219-
? Generated extends {response?: infer GeneratedResponse}
220-
? Omit<Generated, 'response'> & {
221-
response?: Intents extends {response?: infer BaseResponse}
222-
? Omit<NonNullable<BaseResponse>, 'ok'> & NonNullable<GeneratedResponse>
223-
: NonNullable<GeneratedResponse>
224-
}
225-
: Generated
226-
: never`)
230+
utilityTypes.push(`interface GeneratedIntentResponseConstraint<Response> {
231+
response?: Response;
232+
}
227233
228-
utilityTypes.push(`type WithGeneratedIntents<T> = T extends {intents?: infer Intents}
229-
? Omit<T, 'intents'> & {
230-
intents: Omit<NonNullable<Intents>, 'request' | 'response'> & MergeGeneratedIntentResponse<NonNullable<Intents>>
231-
}
232-
: T & {intents: ShopifyGeneratedIntentsApi}`)
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 GeneratedIntentsConstraint<Intents> {
243+
intents?: Intents;
244+
}
245+
246+
interface GeneratedIntentsOverride<Intents> {
247+
intents: Omit<NonNullable<Intents>, 'request' | 'response'> &
248+
MergeGeneratedIntentResponse<NonNullable<Intents>>;
249+
}
250+
251+
interface GeneratedIntentsFallback {
252+
intents: ShopifyGeneratedIntentVariants;
253+
}
254+
255+
type MergeGeneratedIntentResponse<Intents> =
256+
ShopifyGeneratedIntentVariants extends infer Generated
257+
? Generated extends GeneratedIntentResponseConstraint<infer GeneratedResponse>
258+
? Omit<Generated, 'response'> &
259+
(Intents extends GeneratedIntentResponseConstraint<infer BaseResponse>
260+
? GeneratedIntentResponseOverride<BaseResponse, GeneratedResponse>
261+
: GeneratedIntentResponseFallback<GeneratedResponse>)
262+
: Generated
263+
: never;`)
264+
265+
utilityTypes.push(`type WithGeneratedIntents<T> = T extends GeneratedIntentsConstraint<infer Intents>
266+
? Omit<T, 'intents'> & GeneratedIntentsOverride<Intents>
267+
: T & GeneratedIntentsFallback;`)
233268
}
234269

235270
return utilityTypes.join('\n\n')
@@ -274,7 +309,9 @@ export function createTypeDefinition({
274309
...(intentsTypeDefinition ? [intentsTypeDefinition] : []),
275310
...(shopifyUtilityTypes ? [shopifyUtilityTypes] : []),
276311
` const shopify: ${shopifyType};`,
277-
' const globalThis: { shopify: typeof shopify };',
312+
' const globalThis: {',
313+
' shopify: typeof shopify;',
314+
' };',
278315
'}',
279316
'',
280317
]
@@ -396,20 +433,20 @@ export async function createIntentsTypeDefinition(intents: IntentTypeDefinition[
396433

397434
const generatedIntents = types
398435
.map(({requestTypeName, outputTypeName}) => {
399-
return ` | {
400-
request: ${requestTypeName};
401-
response?: ShopifyGeneratedIntentResponse<${outputTypeName}>;
402-
}`
436+
return ` | ShopifyGeneratedIntentsApi<${requestTypeName}, ${outputTypeName}>`
403437
})
404438
.join('\n')
405439

406440
return `${types
407441
.map(
408442
({inputType, valueType, outputType, requestType}) => `${inputType}\n${valueType}\n${outputType}\n${requestType}`,
409443
)
410-
.join('\n\n')}\n\ntype ShopifyGeneratedIntentResponse<Data = unknown> = {
444+
.join('\n\n')}\n\ninterface ShopifyGeneratedIntentResponse<Data = unknown> {
411445
ok(data?: Data): Promise<void>;
412-
}\n\ntype ShopifyGeneratedIntentsApi =\n${generatedIntents}\n`
446+
}\n\ninterface ShopifyGeneratedIntentsApi<Request = unknown, ResponseData = unknown> {
447+
request: Request;
448+
response?: ShopifyGeneratedIntentResponse<ResponseData>;
449+
}\n\ntype ShopifyGeneratedIntentVariants =\n${generatedIntents}\n`
413450
}
414451

415452
/**

packages/app/src/cli/models/extensions/specifications/ui_extension.test.ts

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,11 +1284,15 @@ Please check the configuration in ${uiExtension.configurationPath}`),
12841284
new Set([
12851285
`//@ts-ignore\ndeclare module './src/index.jsx' {
12861286
const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;
1287-
const globalThis: { shopify: typeof shopify };
1287+
const globalThis: {
1288+
shopify: typeof shopify;
1289+
};
12881290
}\n`,
12891291
`//@ts-ignore\ndeclare module './src/condition/should-render.js' {
12901292
const shopify: import('@shopify/ui-extensions/admin.product-details.action.should-render').Api;
1291-
const globalThis: { shopify: typeof shopify };
1293+
const globalThis: {
1294+
shopify: typeof shopify;
1295+
};
12921296
}\n`,
12931297
]),
12941298
],
@@ -1345,11 +1349,15 @@ Please check the configuration in ${uiExtension.configurationPath}`),
13451349
new Set([
13461350
`//@ts-ignore\ndeclare module './src/index.jsx' {
13471351
const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;
1348-
const globalThis: { shopify: typeof shopify };
1352+
const globalThis: {
1353+
shopify: typeof shopify;
1354+
};
13491355
}\n`,
13501356
`//@ts-ignore\ndeclare module './src/another-target-module.jsx' {
13511357
const shopify: import('@shopify/ui-extensions/admin.orders-details.block.render').Api;
1352-
const globalThis: { shopify: typeof shopify };
1358+
const globalThis: {
1359+
shopify: typeof shopify;
1360+
};
13531361
}\n`,
13541362
]),
13551363
],
@@ -1358,7 +1366,9 @@ Please check the configuration in ${uiExtension.configurationPath}`),
13581366
new Set([
13591367
`//@ts-ignore\ndeclare module './should-render.js' {
13601368
const shopify: import('@shopify/ui-extensions/admin.product-details.action.should-render').Api;
1361-
const globalThis: { shopify: typeof shopify };
1369+
const globalThis: {
1370+
shopify: typeof shopify;
1371+
};
13621372
}\n`,
13631373
]),
13641374
],
@@ -1473,10 +1483,14 @@ Please check the configuration in ${uiExtension.configurationPath}`),
14731483

14741484
// Then - should include types for imported modules when single target
14751485
expect(Array.from(typeDefinitionsByFile.get(shopifyDtsPath) ?? [])).toContain(
1476-
`//@ts-ignore\ndeclare module './src/utils/helper.js' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1486+
`//@ts-ignore\ndeclare module './src/utils/helper.js' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1487+
shopify: typeof shopify;
1488+
};\n}\n`,
14771489
)
14781490
expect(Array.from(typeDefinitionsByFile.get(shopifyDtsPath) ?? [])).toContain(
1479-
`//@ts-ignore\ndeclare module './src/components/Button.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1491+
`//@ts-ignore\ndeclare module './src/components/Button.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1492+
shopify: typeof shopify;
1493+
};\n}\n`,
14801494
)
14811495
})
14821496
})
@@ -1535,7 +1549,9 @@ Please check the configuration in ${uiExtension.configurationPath}`),
15351549

15361550
// Then - should generate union type for shared module
15371551
expect(Array.from(types ?? [])).toContain(
1538-
`//@ts-ignore\ndeclare module './shared/utils.js' {\n const shopify:\n | import('@shopify/ui-extensions/admin.product-details.action.render').Api\n | import('@shopify/ui-extensions/admin.orders-details.block.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1552+
`//@ts-ignore\ndeclare module './shared/utils.js' {\n const shopify:\n | import('@shopify/ui-extensions/admin.product-details.action.render').Api\n | import('@shopify/ui-extensions/admin.orders-details.block.render').Api;\n const globalThis: {
1553+
shopify: typeof shopify;
1554+
};\n}\n`,
15391555
)
15401556
})
15411557
})
@@ -1596,7 +1612,9 @@ Please check the configuration in ${uiExtension.configurationPath}`),
15961612
// Then - should generate union types for shared files
15971613
// when targets are from different surfaces (admin vs checkout)
15981614
expect(types).toContain(
1599-
`//@ts-ignore\ndeclare module './src/components/Shared.jsx' {\n const shopify:\n | import('@shopify/ui-extensions/admin.product-details.action.render').Api\n | import('@shopify/ui-extensions/purchase.checkout.block.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1615+
`//@ts-ignore\ndeclare module './src/components/Shared.jsx' {\n const shopify:\n | import('@shopify/ui-extensions/admin.product-details.action.render').Api\n | import('@shopify/ui-extensions/purchase.checkout.block.render').Api;\n const globalThis: {
1616+
shopify: typeof shopify;
1617+
};\n}\n`,
16001618
)
16011619
})
16021620
})
@@ -1646,10 +1664,14 @@ Please check the configuration in ${uiExtension.configurationPath}`),
16461664

16471665
// Then - should resolve aliased imports and include types
16481666
expect(Array.from(typeDefinitionsByFile.get(shopifyDtsPath) ?? [])).toContain(
1649-
`//@ts-ignore\ndeclare module './src/utils/helper.js' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1667+
`//@ts-ignore\ndeclare module './src/utils/helper.js' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1668+
shopify: typeof shopify;
1669+
};\n}\n`,
16501670
)
16511671
expect(Array.from(typeDefinitionsByFile.get(shopifyDtsPath) ?? [])).toContain(
1652-
`//@ts-ignore\ndeclare module './src/components/Button.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1672+
`//@ts-ignore\ndeclare module './src/components/Button.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1673+
shopify: typeof shopify;
1674+
};\n}\n`,
16531675
)
16541676
})
16551677
})
@@ -1720,7 +1742,9 @@ Please check the configuration in ${uiExtension.configurationPath}`),
17201742

17211743
const extensionTypes = typeDefinitionsByFile.get(extensionShopifyDtsPath)
17221744
expect(Array.from(extensionTypes ?? [])).toContain(
1723-
`//@ts-ignore\ndeclare module './src/index.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1745+
`//@ts-ignore\ndeclare module './src/index.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1746+
shopify: typeof shopify;
1747+
};\n}\n`,
17241748
)
17251749

17261750
expect(Array.from(extensionTypes ?? [])).not.toContain(expect.stringContaining('helpers/utils.ts'))
@@ -1794,10 +1818,14 @@ Please check the configuration in ${uiExtension.configurationPath}`),
17941818

17951819
// Then - should include type definition for both the main file and the root-level shared file
17961820
expect(Array.from(types ?? [])).toContain(
1797-
`//@ts-ignore\ndeclare module './src/extension.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1821+
`//@ts-ignore\ndeclare module './src/extension.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1822+
shopify: typeof shopify;
1823+
};\n}\n`,
17981824
)
17991825
expect(Array.from(types ?? [])).toContain(
1800-
`//@ts-ignore\ndeclare module './shared_file.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1826+
`//@ts-ignore\ndeclare module './shared_file.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1827+
shopify: typeof shopify;
1828+
};\n}\n`,
18011829
)
18021830
})
18031831
})
@@ -1890,16 +1918,24 @@ Please check the configuration in ${uiExtension.configurationPath}`),
18901918
// Then - should include type definitions for all files:
18911919
// main file, component, and both root-level shared files
18921920
expect(types).toContain(
1893-
`//@ts-ignore\ndeclare module './src/extension.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1921+
`//@ts-ignore\ndeclare module './src/extension.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1922+
shopify: typeof shopify;
1923+
};\n}\n`,
18941924
)
18951925
expect(types).toContain(
1896-
`//@ts-ignore\ndeclare module './src/components/Component.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1926+
`//@ts-ignore\ndeclare module './src/components/Component.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1927+
shopify: typeof shopify;
1928+
};\n}\n`,
18971929
)
18981930
expect(types).toContain(
1899-
`//@ts-ignore\ndeclare module './shared_file.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1931+
`//@ts-ignore\ndeclare module './shared_file.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1932+
shopify: typeof shopify;
1933+
};\n}\n`,
19001934
)
19011935
expect(types).toContain(
1902-
`//@ts-ignore\ndeclare module './utils.js' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
1936+
`//@ts-ignore\ndeclare module './utils.js' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
1937+
shopify: typeof shopify;
1938+
};\n}\n`,
19031939
)
19041940
})
19051941
})
@@ -1994,17 +2030,23 @@ Please check the configuration in ${uiExtension.configurationPath}`),
19942030
// Then - should include type definitions for all files in the chain:
19952031
// 1. Main extension file
19962032
expect(types).toContain(
1997-
`//@ts-ignore\ndeclare module './src/extension.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
2033+
`//@ts-ignore\ndeclare module './src/extension.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
2034+
shopify: typeof shopify;
2035+
};\n}\n`,
19982036
)
19992037

20002038
// 2. Component file that imports from root
20012039
expect(types).toContain(
2002-
`//@ts-ignore\ndeclare module './src/components/Button.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
2040+
`//@ts-ignore\ndeclare module './src/components/Button.jsx' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
2041+
shopify: typeof shopify;
2042+
};\n}\n`,
20032043
)
20042044

20052045
// 3. Root-level shared file (imported by component, not directly by extension)
20062046
expect(types).toContain(
2007-
`//@ts-ignore\ndeclare module './shared_utils.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: { shopify: typeof shopify };\n}\n`,
2047+
`//@ts-ignore\ndeclare module './shared_utils.ts' {\n const shopify: import('@shopify/ui-extensions/admin.product-details.action.render').Api;\n const globalThis: {
2048+
shopify: typeof shopify;
2049+
};\n}\n`,
20082050
)
20092051

20102052
// Verify we have exactly 3 type definitions (no duplicates)
@@ -2063,6 +2105,7 @@ Please check the configuration in ${uiExtension.configurationPath}`),
20632105
expect(typeDefinition).toContain('interface SearchProductsInput')
20642106
expect(typeDefinition).toContain('interface SearchProductsOutput')
20652107
expect(typeDefinition).toContain("name: 'search_products'")
2108+
expect(typeDefinition).toContain('interface GeneratedToolsConstraint<Tools>')
20662109
expect(typeDefinition).toContain("tools: Omit<NonNullable<Tools>, 'register'> & ShopifyTools")
20672110
})
20682111
})
@@ -2341,10 +2384,13 @@ Please check the configuration in ${uiExtension.configurationPath}`),
23412384
expect(typeDefinition).toContain('interface CreateApplicationEmailIntentRequest')
23422385
expect(typeDefinition).toContain(`action: 'create';`)
23432386
expect(typeDefinition).toContain(`type: 'application/email';`)
2344-
expect(typeDefinition).toContain(
2345-
'response?: ShopifyGeneratedIntentResponse<CreateApplicationEmailIntentOutput>;',
2346-
)
2347-
expect(typeDefinition).toContain('type ShopifyGeneratedIntentsApi =')
2387+
expect(typeDefinition).toContain('interface ShopifyGeneratedIntentResponse<Data = unknown>')
2388+
expect(typeDefinition).toContain('interface ShopifyGeneratedIntentsApi<')
2389+
expect(typeDefinition).toContain('Request = unknown,')
2390+
expect(typeDefinition).toContain('ResponseData = unknown,')
2391+
expect(typeDefinition).toContain('type ShopifyGeneratedIntentVariants = ShopifyGeneratedIntentsApi<')
2392+
expect(typeDefinition).toContain('CreateApplicationEmailIntentRequest,')
2393+
expect(typeDefinition).toContain('CreateApplicationEmailIntentOutput')
23482394
expect(typeDefinition).toContain('WithGeneratedIntents<')
23492395
})
23502396
})

0 commit comments

Comments
 (0)