diff --git a/packages/app/src/cli/models/extensions/extension-instance.test.ts b/packages/app/src/cli/models/extensions/extension-instance.test.ts index 5a808958df9..dbafca2026a 100644 --- a/packages/app/src/cli/models/extensions/extension-instance.test.ts +++ b/packages/app/src/cli/models/extensions/extension-instance.test.ts @@ -72,10 +72,10 @@ describe('keepBuiltSourcemapsLocally', async () => { await extensionInstance.keepBuiltSourcemapsLocally(bundleDirectory) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToMove.js'))).toBe(false) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToMove.js.map'))).toBe(true) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToIgnore.js'))).toBe(false) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToIgnore.js.map'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'uid1', 'scriptToMove.js'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'uid1', 'scriptToMove.js.map'))).toBe(true) + expect(fileExistsSync(joinPath(outputPath, 'otherUID', 'scriptToIgnore.js'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'otherUID', 'scriptToIgnore.js.map'))).toBe(false) }) }) }) @@ -103,10 +103,10 @@ describe('keepBuiltSourcemapsLocally', async () => { await extensionInstance.keepBuiltSourcemapsLocally(bundleInputPath) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToMove.js'))).toBe(false) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToMove.js.map'))).toBe(false) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToIgnore.js'))).toBe(false) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToIgnore.js.map'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'wrongUID', 'scriptToMove.js'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'wrongUID', 'scriptToMove.js.map'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'otherUID', 'scriptToIgnore.js'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'otherUID', 'scriptToIgnore.js.map'))).toBe(false) }) }) }) @@ -133,10 +133,10 @@ describe('keepBuiltSourcemapsLocally', async () => { await extensionInstance.keepBuiltSourcemapsLocally(bundleDirectory) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToMove.js'))).toBe(false) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToMove.js.map'))).toBe(false) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToIgnore.js'))).toBe(false) - expect(fileExistsSync(joinPath(outputPath, 'dist', 'scriptToIgnore.js.map'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'uid1', 'scriptToMove.js'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'uid1', 'scriptToMove.js.map'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'otherUID', 'scriptToIgnore.js'))).toBe(false) + expect(fileExistsSync(joinPath(outputPath, 'otherUID', 'scriptToIgnore.js.map'))).toBe(false) }) }) }) diff --git a/packages/app/src/cli/models/extensions/extension-instance.ts b/packages/app/src/cli/models/extensions/extension-instance.ts index 074d27c5dd0..a39bd741ae8 100644 --- a/packages/app/src/cli/models/extensions/extension-instance.ts +++ b/packages/app/src/cli/models/extensions/extension-instance.ts @@ -27,7 +27,7 @@ import {ok} from '@shopify/cli-kit/node/result' import {constantize, slugify} from '@shopify/cli-kit/common/string' import {hashString, nonRandomUUID} from '@shopify/cli-kit/node/crypto' import {partnersFqdn} from '@shopify/cli-kit/node/context/fqdn' -import {joinPath, basename, normalizePath, resolvePath} from '@shopify/cli-kit/node/path' +import {joinPath, normalizePath, resolvePath, relativePath, basename} from '@shopify/cli-kit/node/path' import {fileExists, touchFile, moveFile, writeFile, glob, copyFile, globSync} from '@shopify/cli-kit/node/fs' import {getPathValue} from '@shopify/cli-kit/common/object' import {outputDebug} from '@shopify/cli-kit/node/output' @@ -137,14 +137,11 @@ export class ExtensionInstance) => string getBundleExtensionStdinContent?: (config: TConfiguration) => {main: string; assets?: Asset[]} deployConfig?: ( config: TConfiguration, diff --git a/packages/app/src/cli/models/extensions/specifications/checkout_post_purchase.ts b/packages/app/src/cli/models/extensions/specifications/checkout_post_purchase.ts index 616adf80b73..cbae1290736 100644 --- a/packages/app/src/cli/models/extensions/specifications/checkout_post_purchase.ts +++ b/packages/app/src/cli/models/extensions/specifications/checkout_post_purchase.ts @@ -1,9 +1,11 @@ +import {ExtensionInstance} from '../extension-instance.js' import {BaseSchema, MetafieldSchema} from '../schemas.js' import {createExtensionSpecification} from '../specification.js' import {zod} from '@shopify/cli-kit/node/schema' const dependency = '@shopify/post-purchase-ui-extensions' +type CheckoutPostPurchaseConfigType = zod.infer const CheckoutPostPurchaseSchema = BaseSchema.extend({ metafields: zod.array(MetafieldSchema).optional(), }) @@ -15,6 +17,8 @@ const checkoutPostPurchaseSpec = createExtensionSpecification({ schema: CheckoutPostPurchaseSchema, appModuleFeatures: (_) => ['ui_preview', 'cart_url', 'esbuild', 'single_js_entry_path'], buildConfig: {mode: 'ui'}, + getOutputRelativePath: (extension: ExtensionInstance) => + `dist/${extension.handle}.js`, deployConfig: async (config, _) => { return {metafields: config.metafields ?? []} }, diff --git a/packages/app/src/cli/models/extensions/specifications/checkout_ui_extension.ts b/packages/app/src/cli/models/extensions/specifications/checkout_ui_extension.ts index f08dfd97c40..ab37ad6a44f 100644 --- a/packages/app/src/cli/models/extensions/specifications/checkout_ui_extension.ts +++ b/packages/app/src/cli/models/extensions/specifications/checkout_ui_extension.ts @@ -1,10 +1,12 @@ import {createExtensionSpecification} from '../specification.js' import {BaseSchema, MetafieldSchema} from '../schemas.js' import {loadLocalesConfig} from '../../../utilities/extensions/locales-configuration.js' +import {ExtensionInstance} from '../extension-instance.js' import {zod} from '@shopify/cli-kit/node/schema' const dependency = '@shopify/checkout-ui-extensions' +type CheckoutConfigType = zod.infer const CheckoutSchema = BaseSchema.extend({ name: zod.string(), extension_points: zod.array(zod.string()).optional(), @@ -22,6 +24,7 @@ const checkoutSpec = createExtensionSpecification({ schema: CheckoutSchema, appModuleFeatures: (_) => ['ui_preview', 'cart_url', 'esbuild', 'single_js_entry_path', 'generates_source_maps'], buildConfig: {mode: 'ui'}, + getOutputRelativePath: (extension: ExtensionInstance) => `dist/${extension.handle}.js`, deployConfig: async (config, directory) => { return { extension_points: config.extension_points, diff --git a/packages/app/src/cli/models/extensions/specifications/function.ts b/packages/app/src/cli/models/extensions/specifications/function.ts index ce6d2ec5a5d..c1f80ee796e 100644 --- a/packages/app/src/cli/models/extensions/specifications/function.ts +++ b/packages/app/src/cli/models/extensions/specifications/function.ts @@ -1,6 +1,7 @@ import {createExtensionSpecification} from '../specification.js' import {BaseSchema} from '../schemas.js' import {loadLocalesConfig} from '../../../utilities/extensions/locales-configuration.js' +import {ExtensionInstance} from '../extension-instance.js' import {zod} from '@shopify/cli-kit/node/schema' import {joinPath} from '@shopify/cli-kit/node/path' import {fileExists, readFile} from '@shopify/cli-kit/node/fs' @@ -88,6 +89,8 @@ const functionSpec = createExtensionSpecification({ schema: FunctionExtensionSchema, appModuleFeatures: (_) => ['function'], buildConfig: {mode: 'function'}, + getOutputRelativePath: (extension: ExtensionInstance) => + extension.configuration.build?.path ?? joinPath('dist', 'index.wasm'), deployConfig: async (config, directory, apiKey) => { let inputQuery: string | undefined const moduleId = randomUUID() diff --git a/packages/app/src/cli/models/extensions/specifications/pos_ui_extension.ts b/packages/app/src/cli/models/extensions/specifications/pos_ui_extension.ts index 33962306a24..2d9dc37b4ee 100644 --- a/packages/app/src/cli/models/extensions/specifications/pos_ui_extension.ts +++ b/packages/app/src/cli/models/extensions/specifications/pos_ui_extension.ts @@ -1,17 +1,22 @@ import {getDependencyVersion} from '../../app/app.js' import {createExtensionSpecification} from '../specification.js' import {BaseSchema} from '../schemas.js' +import {ExtensionInstance} from '../extension-instance.js' import {BugError} from '@shopify/cli-kit/node/error' import {zod} from '@shopify/cli-kit/node/schema' const dependency = '@shopify/retail-ui-extensions' +type PosUIConfigType = zod.infer +const PosUISchema = BaseSchema.extend({name: zod.string()}) + const posUISpec = createExtensionSpecification({ identifier: 'pos_ui_extension', dependency, - schema: BaseSchema.extend({name: zod.string()}), + schema: PosUISchema, appModuleFeatures: (_) => ['ui_preview', 'esbuild', 'single_js_entry_path'], buildConfig: {mode: 'ui'}, + getOutputRelativePath: (extension: ExtensionInstance) => `dist/${extension.handle}.js`, deployConfig: async (config, directory) => { const result = await getDependencyVersion(dependency, directory) if (result === 'not_found') throw new BugError(`Dependency ${dependency} not found`) diff --git a/packages/app/src/cli/models/extensions/specifications/product_subscription.ts b/packages/app/src/cli/models/extensions/specifications/product_subscription.ts index ba807e409f6..2497a236130 100644 --- a/packages/app/src/cli/models/extensions/specifications/product_subscription.ts +++ b/packages/app/src/cli/models/extensions/specifications/product_subscription.ts @@ -1,10 +1,14 @@ import {getDependencyVersion} from '../../app/app.js' import {createExtensionSpecification} from '../specification.js' import {BaseSchema} from '../schemas.js' +import {ExtensionInstance} from '../extension-instance.js' import {BugError} from '@shopify/cli-kit/node/error' +import {zod} from '@shopify/cli-kit/node/schema' const dependency = '@shopify/admin-ui-extensions' +type ProductSubscriptionConfigType = zod.infer + const productSubscriptionSpec = createExtensionSpecification({ identifier: 'product_subscription', additionalIdentifiers: ['subscription_management'], @@ -13,6 +17,7 @@ const productSubscriptionSpec = createExtensionSpecification({ schema: BaseSchema, appModuleFeatures: (_) => ['ui_preview', 'esbuild', 'single_js_entry_path'], buildConfig: {mode: 'ui'}, + getOutputRelativePath: (extension: ExtensionInstance) => `dist/${extension.handle}.js`, deployConfig: async (_, directory) => { const result = await getDependencyVersion(dependency, directory) if (result === 'not_found') throw new BugError(`Dependency ${dependency} not found`) diff --git a/packages/app/src/cli/models/extensions/specifications/tax_calculation.ts b/packages/app/src/cli/models/extensions/specifications/tax_calculation.ts index 1e97e577bb6..e7d09509939 100644 --- a/packages/app/src/cli/models/extensions/specifications/tax_calculation.ts +++ b/packages/app/src/cli/models/extensions/specifications/tax_calculation.ts @@ -1,11 +1,13 @@ import {createExtensionSpecification} from '../specification.js' import {BaseSchema, MetafieldSchema} from '../schemas.js' +import {ExtensionInstance} from '../extension-instance.js' import {zod} from '@shopify/cli-kit/node/schema' const CartLinePropertySchema = zod.object({ key: zod.string(), }) +type TaxCalculationsConfigType = zod.infer const TaxCalculationsSchema = BaseSchema.extend({ production_api_base_url: zod.string(), benchmark_api_base_url: zod.string().optional(), @@ -29,6 +31,7 @@ const spec = createExtensionSpecification({ schema: TaxCalculationsSchema, appModuleFeatures: (_) => [], buildConfig: {mode: 'tax_calculation'}, + getOutputRelativePath: (extension: ExtensionInstance) => `dist/${extension.handle}.js`, deployConfig: async (config, _) => { return { production_api_base_url: config.production_api_base_url, diff --git a/packages/app/src/cli/models/extensions/specifications/ui_extension.ts b/packages/app/src/cli/models/extensions/specifications/ui_extension.ts index f3e04cbeab9..8f8dc83d02a 100644 --- a/packages/app/src/cli/models/extensions/specifications/ui_extension.ts +++ b/packages/app/src/cli/models/extensions/specifications/ui_extension.ts @@ -36,6 +36,7 @@ export interface BuildManifest { const missingExtensionPointsMessage = 'No extension targets defined, add a `targeting` field to your configuration' +type UIExtensionConfigType = zod.infer export const UIExtensionSchema = BaseSchema.extend({ name: zod.string(), type: zod.literal('ui_extension'), @@ -102,6 +103,7 @@ const uiExtensionSpec = createExtensionSpecification({ dependency, schema: UIExtensionSchema, buildConfig: {mode: 'ui'}, + getOutputRelativePath: (extension: ExtensionInstance) => `dist/${extension.handle}.js`, appModuleFeatures: (config) => { const basic: ExtensionFeature[] = ['ui_preview', 'esbuild', 'generates_source_maps'] const needsCart = diff --git a/packages/app/src/cli/models/extensions/specifications/web_pixel_extension.ts b/packages/app/src/cli/models/extensions/specifications/web_pixel_extension.ts index 298a18d876b..e512e66e5f4 100644 --- a/packages/app/src/cli/models/extensions/specifications/web_pixel_extension.ts +++ b/packages/app/src/cli/models/extensions/specifications/web_pixel_extension.ts @@ -1,5 +1,6 @@ import {createExtensionSpecification} from '../specification.js' import {BaseSchema} from '../schemas.js' +import {ExtensionInstance} from '../extension-instance.js' import {zod} from '@shopify/cli-kit/node/schema' import {AbortError} from '@shopify/cli-kit/node/error' import {fileSize} from '@shopify/cli-kit/node/fs' @@ -10,6 +11,7 @@ const BUNDLE_SIZE_LIMIT = BUNDLE_SIZE_LIMIT_KB * kilobytes const dependency = '@shopify/web-pixels-extension' +type WebPixelConfigType = zod.infer const WebPixelSchema = BaseSchema.extend({ runtime_context: zod.string(), version: zod.string().optional(), @@ -32,6 +34,7 @@ const webPixelSpec = createExtensionSpecification({ schema: WebPixelSchema, appModuleFeatures: (_) => ['esbuild', 'single_js_entry_path'], buildConfig: {mode: 'ui'}, + getOutputRelativePath: (extension: ExtensionInstance) => `dist/${extension.handle}.js`, deployConfig: async (config, _) => { return { runtime_context: config.runtime_context, diff --git a/packages/app/src/cli/services/build/extension.ts b/packages/app/src/cli/services/build/extension.ts index 21072a23f66..d1000080051 100644 --- a/packages/app/src/cli/services/build/extension.ts +++ b/packages/app/src/cli/services/build/extension.ts @@ -150,8 +150,7 @@ export async function buildFunctionExtension( try { const bundlePath = extension.outputPath - const relativeBuildPath = - (extension as ExtensionInstance).configuration.build?.path ?? joinPath('dist', 'index.wasm') + const relativeBuildPath = extension.specification.getOutputRelativePath?.(extension) ?? '' extension.outputPath = joinPath(extension.directory, relativeBuildPath) diff --git a/packages/app/src/cli/services/dev/extension/server/middlewares.ts b/packages/app/src/cli/services/dev/extension/server/middlewares.ts index 7bf8116bc11..9cec5a369c4 100644 --- a/packages/app/src/cli/services/dev/extension/server/middlewares.ts +++ b/packages/app/src/cli/services/dev/extension/server/middlewares.ts @@ -5,7 +5,7 @@ import {getHTML} from '../templates.js' import {getWebSocketUrl} from '../../extension.js' import {fileExists, isDirectory, readFile, findPathUp} from '@shopify/cli-kit/node/fs' import {IncomingMessage, ServerResponse, sendRedirect, send} from 'h3' -import {joinPath, extname, moduleDirectory} from '@shopify/cli-kit/node/path' +import {joinPath, dirname, extname, moduleDirectory} from '@shopify/cli-kit/node/path' import {outputDebug} from '@shopify/cli-kit/node/output' export function corsMiddleware(_request: IncomingMessage, response: ServerResponse, next: (err?: Error) => unknown) { @@ -90,7 +90,7 @@ export function getExtensionAssetMiddleware({devOptions, getExtensions}: GetExte const bundlePath = devOptions.appWatcher.buildOutputPath const extensionOutputPath = extension.getOutputPathForDirectory(bundlePath) - const buildDirectory = extensionOutputPath.replace(extension.outputFileName, '') + const buildDirectory = dirname(extensionOutputPath) return fileServerMiddleware(request, response, next, { filePath: joinPath(buildDirectory, assetPath),