diff --git a/packages/app/src/cli/models/app/loader.ts b/packages/app/src/cli/models/app/loader.ts index 9a447c83bbd..41ad884f471 100644 --- a/packages/app/src/cli/models/app/loader.ts +++ b/packages/app/src/cli/models/app/loader.ts @@ -336,20 +336,22 @@ export async function loadAppFromContext ({ - name: spec.externalName, - externalName: spec.externalName, - identifier: spec.identifier, - gated: false, - externalIdentifier: spec.externalIdentifier, - experience: spec.experience as 'extension' | 'configuration' | 'deprecated', - managementExperience: 'cli' as const, - registrationLimit: spec.registrationLimit, - uidStrategy: spec.uidStrategy, - surface: spec.surface, - })) - moduleRegistry.mergeRemoteSpecs(remoteSpecsForMerge) + const moduleRegistry = loadModuleRegistry?.() ?? new ModuleRegistry() + if (moduleRegistry.size > 0) { + const remoteSpecsForMerge = specifications.map((spec) => ({ + name: spec.externalName, + externalName: spec.externalName, + identifier: spec.identifier, + gated: false, + externalIdentifier: spec.externalIdentifier, + experience: spec.experience as 'extension' | 'configuration' | 'deprecated', + managementExperience: 'cli' as const, + registrationLimit: spec.registrationLimit, + uidStrategy: spec.uidStrategy, + surface: spec.surface, + })) + moduleRegistry.mergeRemoteSpecs(remoteSpecsForMerge) + } const loadedConfiguration: ConfigurationLoaderResult = { directory: project.directory, diff --git a/packages/app/src/cli/models/extensions/load-specifications.ts b/packages/app/src/cli/models/extensions/load-specifications.ts index 0a6cdd29f8d..342ce533dc6 100644 --- a/packages/app/src/cli/models/extensions/load-specifications.ts +++ b/packages/app/src/cli/models/extensions/load-specifications.ts @@ -27,8 +27,8 @@ import themeSpec from './specifications/theme.js' import uiExtensionSpec from './specifications/ui_extension.js' import webPixelSpec from './specifications/web_pixel_extension.js' import editorExtensionCollectionSpecification from './specifications/editor_extension_collection.js' -import channelSpecificationSpec from './specifications/channel.js' import adminSpecificationSpec from './specifications/admin.js' +import {channelDescriptor} from './specifications/channel_module.js' const SORTED_CONFIGURATION_SPEC_IDENTIFIERS = [ BrandingSpecIdentifier, @@ -59,7 +59,7 @@ export async function loadLocalExtensionsSpecifications(): Promise diff --git a/packages/app/src/cli/models/extensions/specifications/channel.ts b/packages/app/src/cli/models/extensions/specifications/channel.ts deleted file mode 100644 index fc3ca3b5a9e..00000000000 --- a/packages/app/src/cli/models/extensions/specifications/channel.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {createContractBasedModuleSpecification} from '../specification.js' -import {joinPath} from '@shopify/cli-kit/node/path' - -const SUBDIRECTORY_NAME = 'specifications' -const FILE_EXTENSIONS = ['json', 'toml', 'yaml', 'yml', 'svg'] - -const channelSpecificationSpec = createContractBasedModuleSpecification({ - identifier: 'channel_config', - uidStrategy: 'single', - experience: 'extension', - buildConfig: { - mode: 'copy_files', - filePatterns: FILE_EXTENSIONS.map((ext) => joinPath(SUBDIRECTORY_NAME, '**', `*.${ext}`)), - }, - clientSteps: [ - { - lifecycle: 'deploy', - steps: [ - { - id: 'copy-files', - name: 'Copy Files', - type: 'include_assets', - config: { - inclusions: [ - { - type: 'pattern', - baseDir: SUBDIRECTORY_NAME, - destination: SUBDIRECTORY_NAME, - include: FILE_EXTENSIONS.map((ext) => `**/*.${ext}`), - }, - ], - }, - }, - ], - }, - ], - appModuleFeatures: () => [], -}) - -export default channelSpecificationSpec diff --git a/packages/app/src/cli/models/extensions/specifications/channel_module.ts b/packages/app/src/cli/models/extensions/specifications/channel_module.ts new file mode 100644 index 00000000000..69cec8c2e35 --- /dev/null +++ b/packages/app/src/cli/models/extensions/specifications/channel_module.ts @@ -0,0 +1,80 @@ +import {blocks} from '../../../constants.js' +import {ClientSteps} from '../../../services/build/client-steps.js' +import {loadLocalesConfig} from '../../../utilities/extensions/locales-configuration.js' +import {ApplicationModule} from '../application-module.js' +import {ModuleDescriptor} from '../module-descriptor.js' +import {BaseConfigType, BaseSchema} from '../schemas.js' +import {configWithoutFirstClassFields} from '../specification.js' +import {joinPath} from '@shopify/cli-kit/node/path' +import {zod} from '@shopify/cli-kit/node/schema' +import type {ExtensionFeature, BuildConfig, ExtensionDeployConfigOptions} from '../application-module.js' + +const SUBDIRECTORY_NAME = 'specifications' +const FILE_EXTENSIONS = ['json', 'toml', 'yaml', 'yml', 'svg'] + +const ChannelModuleIdentifier = 'channel_config' + +class ChannelModule extends ApplicationModule { + appModuleFeatures(): ExtensionFeature[] { + return [] + } + + override get buildConfig(): BuildConfig { + return { + mode: 'copy_files', + filePatterns: FILE_EXTENSIONS.map((ext) => joinPath(SUBDIRECTORY_NAME, '**', `*.${ext}`)), + } + } + + override get clientSteps(): ClientSteps { + return [ + { + lifecycle: 'deploy', + steps: [ + { + id: 'copy-files', + name: 'Copy Files', + type: 'include_assets', + config: { + inclusions: [ + { + type: 'pattern', + baseDir: SUBDIRECTORY_NAME, + destination: SUBDIRECTORY_NAME, + include: FILE_EXTENSIONS.map((ext) => `**/*.${ext}`), + }, + ], + }, + }, + ], + }, + ] + } + + override async deployConfig(_options: ExtensionDeployConfigOptions): Promise | undefined> { + let parsedConfig = configWithoutFirstClassFields(this.configuration) + if (this.appModuleFeatures().includes('localization')) { + const localization = await loadLocalesConfig(this.directory, this.identifier) + parsedConfig = {...parsedConfig, localization} + } + return parsedConfig + } +} + +export const channelDescriptor: ModuleDescriptor = { + identifier: ChannelModuleIdentifier, + additionalIdentifiers: [], + externalIdentifier: `${ChannelModuleIdentifier}_external`, + externalName: 'Channel config', + partnersWebIdentifier: ChannelModuleIdentifier, + surface: 'test-surface', + registrationLimit: blocks.extensions.defaultRegistrationLimit, + experience: 'extension', + uidStrategy: 'single', + schema: zod.any({}) as unknown as typeof BaseSchema, + contributeToAppConfigurationSchema: (schema) => schema, + parseConfigurationObject: (configurationObject: object) => { + return {state: 'ok' as const, data: configurationObject as BaseConfigType, errors: undefined} + }, + createModule: (options) => new ChannelModule(options), +}