Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/app/src/cli/models/app/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export async function loadConfigurationFileContent(

try {
const configurationContent = await readFile(filepath)
// console.log('Initial undecoded config from TOML file', {configurationContent})
return decode(configurationContent)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
Expand Down Expand Up @@ -140,6 +141,7 @@ export function parseConfigurationObject<TSchema extends zod.ZodType>(
const fallbackOutput = {} as zod.TypeOf<TSchema>

const parseResult = schema.safeParse(configurationObject)
// console.log('Config after comparing with schema (includes remote contract)', {parseResult})
if (!parseResult.success) {
return abortOrReport(
outputContent`\n${outputToken.errorText('Validation errors')} in ${outputToken.path(
Expand Down Expand Up @@ -346,7 +348,7 @@ export async function loadOpaqueApp(options: {
const rawConfig = await loadConfigurationFileContent(configurationPath)
const parsed = TemplateConfigSchema.parse(rawConfig)
const packageManager = await getPackageManager(appDirectory)

// console.log('WE ARE NOT GETTING HERE', {rawConfig})
return {
state: 'loaded-template',
rawConfig,
Expand Down Expand Up @@ -856,6 +858,7 @@ export async function loadAppConfiguration(
options: AppConfigurationLoaderConstructorArgs,
): Promise<AppConfigurationInterface> {
const specifications = options.specifications ?? (await loadLocalExtensionsSpecifications())
console.log('SPECIFICATIONS', {specifications})
const state = await getAppConfigurationState(options.directory, options.userProvidedConfigName)
const result = await loadAppConfigurationFromState(state, specifications, options.remoteFlags ?? [])
await logMetadataFromAppLoadingProcess(result.configurationLoadResultMetadata)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {PosSpecIdentifier} from './specifications/app_config_point_of_sale.js'
import {PrivacyComplianceWebhooksSpecIdentifier} from './specifications/app_config_privacy_compliance_webhooks.js'
import {WebhooksSpecIdentifier} from './specifications/app_config_webhook.js'
import {WebhookSubscriptionSpecIdentifier} from './specifications/app_config_webhook_subscription.js'
import {HostedAppHomeSpecIdentifier} from './specifications/app_config_hosted_app_home.js'
import {
ExtensionBuildOptions,
buildFunctionExtension,
Expand Down Expand Up @@ -40,6 +41,7 @@ export const CONFIG_EXTENSION_IDS: string[] = [
AppHomeSpecIdentifier,
AppProxySpecIdentifier,
BrandingSpecIdentifier,
HostedAppHomeSpecIdentifier,
PosSpecIdentifier,
PrivacyComplianceWebhooksSpecIdentifier,
WebhookSubscriptionSpecIdentifier,
Expand Down Expand Up @@ -366,6 +368,9 @@ export class ExtensionInstance<TConfiguration extends BaseConfigType = BaseConfi
this.specification.buildConfig.filePatterns,
this.specification.buildConfig.ignoredFilePatterns,
)
case 'static_app':
await this.copyStaticAssets()
break
case 'none':
break
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 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 hostedAppHomeSpec, {HostedAppHomeSpecIdentifier} from './specifications/app_config_hosted_app_home.js'

const SORTED_CONFIGURATION_SPEC_IDENTIFIERS = [
BrandingSpecIdentifier,
Expand All @@ -36,6 +37,7 @@ const SORTED_CONFIGURATION_SPEC_IDENTIFIERS = [
AppProxySpecIdentifier,
PosSpecIdentifier,
AppHomeSpecIdentifier,
HostedAppHomeSpecIdentifier,
]

/**
Expand All @@ -58,6 +60,7 @@ function loadSpecifications() {
appPrivacyComplienceSpec,
appWebhooksSpec,
appWebhookSubscriptionSpec,
hostedAppHomeSpec,
]
const moduleSpecs = [
checkoutPostPurchaseSpec,
Expand Down
6 changes: 5 additions & 1 deletion packages/app/src/cli/models/extensions/specification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface Asset {
}

type BuildConfig =
| {mode: 'ui' | 'theme' | 'function' | 'tax_calculation' | 'none'}
| {mode: 'ui' | 'theme' | 'function' | 'tax_calculation' | 'none' | 'static_app'}
| {mode: 'copy_files'; filePatterns: string[]; ignoredFilePatterns?: string[]}
/**
* Extension specification with all the needed properties and methods to load an extension.
Expand Down Expand Up @@ -239,11 +239,13 @@ export function createExtensionSpecification<TConfiguration extends BaseConfigTy
export function createConfigExtensionSpecification<TConfiguration extends BaseConfigType = BaseConfigType>(spec: {
identifier: string
schema: ZodSchemaType<TConfiguration>
buildConfig?: BuildConfig
appModuleFeatures?: (config?: TConfiguration) => ExtensionFeature[]
transformConfig: TransformationConfig | CustomTransformationConfig
uidStrategy?: UidStrategy
getDevSessionUpdateMessages?: (config: TConfiguration) => Promise<string[]>
patchWithAppDevURLs?: (config: TConfiguration, urls: ApplicationURLs) => void
copyStaticAssets?: (config: TConfiguration, directory: string, outputPath: string) => Promise<void>
}): ExtensionSpecification<TConfiguration> {
const appModuleFeatures = spec.appModuleFeatures ?? (() => [])
return createExtensionSpecification({
Expand All @@ -256,8 +258,10 @@ export function createConfigExtensionSpecification<TConfiguration extends BaseCo
transformRemoteToLocal: resolveReverseAppConfigTransform(spec.schema, spec.transformConfig),
experience: 'configuration',
uidStrategy: spec.uidStrategy ?? 'single',
buildConfig: spec.buildConfig ?? {mode: 'none'},
getDevSessionUpdateMessages: spec.getDevSessionUpdateMessages,
patchWithAppDevURLs: spec.patchWithAppDevURLs,
copyStaticAssets: spec.copyStaticAssets,
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {BaseSchemaWithoutHandle} from '../schemas.js'
import {TransformationConfig, createConfigExtensionSpecification} from '../specification.js'
import {copyDirectoryContents} from '@shopify/cli-kit/node/fs'
import {dirname, joinPath} from '@shopify/cli-kit/node/path'
import {zod} from '@shopify/cli-kit/node/schema'

const HostedAppHomeSchema = BaseSchemaWithoutHandle.extend({
static_root: zod.string().optional(),
})

const HostedAppHomeTransformConfig: TransformationConfig = {
static_root: 'static_root',
}

export const HostedAppHomeSpecIdentifier = 'hosted_app'

const hostedAppHomeSpec = createConfigExtensionSpecification({
identifier: HostedAppHomeSpecIdentifier,
buildConfig: {mode: 'static_app'} as const,
schema: HostedAppHomeSchema,
transformConfig: HostedAppHomeTransformConfig,
copyStaticAssets: async (config, directory, outputPath) => {
if (!config.static_root) return
const sourceDir = joinPath(directory, config.static_root)
const outputDir = dirname(outputPath)

return copyDirectoryContents(sourceDir, outputDir).catch((error) => {
throw new Error(`Failed to copy static assets from ${sourceDir} to ${outputDir}: ${error.message}`)
})
},
})

export default hostedAppHomeSpec
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export class AppEventWatcher extends EventEmitter {
this.fileWatcher.onChange((events) => {
handleWatcherEvents(events, this.app, this.options)
.then(async (appEvent) => {
console.log({appEvent})
if (appEvent?.extensionEvents.length === 0) outputDebug('Change detected, but no extensions were affected')
if (!appEvent) return

Expand All @@ -169,6 +170,13 @@ export class AppEventWatcher extends EventEmitter {
await this.app.generateExtensionTypes()
}

if (appEvent.appWasReloaded) {
const appHomeExtension = this.app.realExtensions.find((ext) => ext.specification.identifier === 'app_home')
if (appHomeExtension) {
await appHomeExtension.copyStaticAssets(this.buildOutputPath)
}
}

// Find deleted extensions and delete their previous build output
await this.deleteExtensionsBuildOutput(appEvent)
this.emit('all', appEvent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export async function fetchSpecifications({
const extensionSpecifications: FlattenedRemoteSpecification[] = result
.filter((specification) => ['extension', 'configuration'].includes(specification.experience))
.map((spec) => {
if (spec.identifier === 'hosted_app') {
console.log('Hosted app home spec', spec)
}
const newSpec = spec as FlattenedRemoteSpecification
// WORKAROUND: The identifiers in the API are different for these extensions to the ones the CLI
// has been using so far. This is a workaround to keep the CLI working until the API is updated.
Expand Down Expand Up @@ -78,7 +81,6 @@ async function mergeLocalAndRemoteSpecs(

const merged = {...localSpec, ...remoteSpec, loadedRemoteSpecs: true} as RemoteAwareExtensionSpecification &
FlattenedRemoteSpecification

// If configuration is inside an app.toml -- i.e. single UID mode -- we must be able to parse a partial slice.
let handleInvalidAdditionalProperties: HandleInvalidAdditionalProperties
switch (merged.uidStrategy) {
Expand Down
Loading