From 87adda48045db31d54aa0c209dc784d1aec11803 Mon Sep 17 00:00:00 2001 From: James Meng Date: Mon, 7 Oct 2024 10:16:31 -0700 Subject: [PATCH 1/9] remove legacy flag --- packages/app/src/cli/commands/app/dev.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/app/src/cli/commands/app/dev.ts b/packages/app/src/cli/commands/app/dev.ts index 3f099aa318..eaa06954b8 100644 --- a/packages/app/src/cli/commands/app/dev.ts +++ b/packages/app/src/cli/commands/app/dev.ts @@ -133,11 +133,6 @@ If you're using the PHP or Ruby app template, then you need to complete the foll 'Key used to authenticate GraphiQL requests. Should be specified if exposing GraphiQL on a publicly accessible URL. By default, no key is required.', env: 'SHOPIFY_FLAG_GRAPHIQL_KEY', }), - legacy: Flags.boolean({ - hidden: true, - description: 'Use the legacy Ruby implementation for managing theme app extensions.', - env: 'SHOPIFY_FLAG_LEGACY', - }), } public static analyticsStopCommand(): string | undefined { @@ -185,7 +180,6 @@ If you're using the PHP or Ruby app template, then you need to complete the foll notify: flags.notify, graphiqlPort: flags['graphiql-port'], graphiqlKey: flags['graphiql-key'], - devPreview: !flags.legacy, } const result = await dev(devOptions) From 929b5f881d8af45bac5fb853899c048a1d178d6c Mon Sep 17 00:00:00 2001 From: James Meng Date: Mon, 7 Oct 2024 10:19:26 -0700 Subject: [PATCH 2/9] Update imports to remove legacy TAE setup --- packages/app/src/cli/services/dev.ts | 1 - .../dev/processes/setup-dev-processes.ts | 33 +++++-------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/packages/app/src/cli/services/dev.ts b/packages/app/src/cli/services/dev.ts index 5bfee4cc55..9b5bae45eb 100644 --- a/packages/app/src/cli/services/dev.ts +++ b/packages/app/src/cli/services/dev.ts @@ -64,7 +64,6 @@ export interface DevOptions { notify?: string graphiqlPort?: number graphiqlKey?: string - devPreview?: boolean } export async function dev(commandOptions: DevOptions) { diff --git a/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts b/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts index cc9bcd7008..e3e4ab1b5e 100644 --- a/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts +++ b/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts @@ -1,9 +1,5 @@ import {BaseProcess, DevProcessFunction} from './types.js' -import {PreviewThemeAppExtensionsProcess, setupPreviewThemeAppExtensionsProcess} from './theme-app-extension.js' -import { - PreviewThemeAppExtensionsProcess as PreviewThemeAppExtensionsNextProcess, - setupPreviewThemeAppExtensionsProcess as setupPreviewThemeAppExtensionsProcessNext, -} from './theme-app-extension-next.js' +import {PreviewThemeAppExtensionsProcess, setupPreviewThemeAppExtensionsProcess} from './theme-app-extension-next.js' import {PreviewableExtensionProcess, setupPreviewableExtensionsProcess} from './previewable-extension.js' import {DraftableExtensionProcess, setupDraftableExtensionsProcess} from './draftable-extension.js' import {SendWebhookProcess, setupSendUninstallWebhookProcess} from './uninstall-webhook.js' @@ -31,7 +27,6 @@ interface ProxyServerProcess extends BaseProcess<{port: number; rules: {[key: st type DevProcessDefinition = | SendWebhookProcess | PreviewThemeAppExtensionsProcess - | PreviewThemeAppExtensionsNextProcess | WebProcess | ProxyServerProcess | PreviewableExtensionProcess @@ -141,24 +136,14 @@ export async function setupDevProcesses({ developerPlatformClient, proxyUrl: network.proxyUrl, }), - commandOptions.devPreview - ? await setupPreviewThemeAppExtensionsProcessNext({ - remoteApp, - localApp, - storeFqdn, - developerPlatformClient, - theme: commandOptions.theme, - themeExtensionPort: commandOptions.themeExtensionPort, - }) - : await setupPreviewThemeAppExtensionsProcess({ - allExtensions: localApp.allExtensions, - storeFqdn, - apiKey, - developerPlatformClient, - theme: commandOptions.theme, - themeExtensionPort: commandOptions.themeExtensionPort, - notify: commandOptions.notify, - }), + await setupPreviewThemeAppExtensionsProcess({ + remoteApp, + localApp, + storeFqdn, + developerPlatformClient, + theme: commandOptions.theme, + themeExtensionPort: commandOptions.themeExtensionPort, + }), setupSendUninstallWebhookProcess({ webs: localApp.webs, backendPort: network.backendPort, From 303cf625b829e9040d5c2916fe7bde202dfc0ae1 Mon Sep 17 00:00:00 2001 From: James Meng Date: Mon, 7 Oct 2024 10:23:42 -0700 Subject: [PATCH 3/9] Update docs --- packages/cli/oclif.manifest.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index cdb21e78ad..095864d815 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -412,14 +412,6 @@ "name": "graphiql-port", "type": "option" }, - "legacy": { - "allowNo": false, - "description": "Use the legacy Ruby implementation for managing theme app extensions.", - "env": "SHOPIFY_FLAG_LEGACY", - "hidden": true, - "name": "legacy", - "type": "boolean" - }, "no-color": { "allowNo": false, "description": "Disable color output.", From c88388c994ca1d993eee9a18f9e2b898382bfdc7 Mon Sep 17 00:00:00 2001 From: James Meng Date: Mon, 7 Oct 2024 10:49:36 -0700 Subject: [PATCH 4/9] Remove import from tests --- .../cli/services/dev/processes/setup-dev-processes.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts b/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts index 632daedd18..6055c474d7 100644 --- a/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts +++ b/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts @@ -5,7 +5,6 @@ import {WebProcess, launchWebProcess} from './web.js' import {PreviewableExtensionProcess, launchPreviewableExtensionProcess} from './previewable-extension.js' import {launchGraphiQLServer} from './graphiql.js' import {pushUpdatesForDraftableExtensions} from './draftable-extension.js' -import {runThemeAppExtensionsServer} from './theme-app-extension.js' import {pushUpdatesForDevSession} from './dev-session.js' import { testAppAccessConfigExtension, @@ -26,6 +25,7 @@ import {describe, test, expect, beforeEach, vi} from 'vitest' import {ensureAuthenticatedAdmin, ensureAuthenticatedStorefront} from '@shopify/cli-kit/node/session' import {Config} from '@oclif/core' import {getEnvironmentVariables} from '@shopify/cli-kit/node/environment' +import {initializeDevelopmentExtensionServer} from '@shopify/theme' vi.mock('../../context/identifiers.js') vi.mock('@shopify/cli-kit/node/session.js') @@ -197,7 +197,7 @@ describe('setup-dev-processes', () => { expect(res.processes[4]).toMatchObject({ type: 'theme-app-extensions', prefix: 'theme-extensions', - function: runThemeAppExtensionsServer, + function: initializeDevelopmentExtensionServer, options: { adminSession: { storeFqdn: 'store.myshopify.io', From bb5a4316fd45f6c0ee66749ec26737117643145c Mon Sep 17 00:00:00 2001 From: James Meng Date: Mon, 7 Oct 2024 10:50:30 -0700 Subject: [PATCH 5/9] Remove legacy theme-app-extension file --- .../dev/processes/theme-app-extension.ts | 118 ------------------ 1 file changed, 118 deletions(-) delete mode 100644 packages/app/src/cli/services/dev/processes/theme-app-extension.ts diff --git a/packages/app/src/cli/services/dev/processes/theme-app-extension.ts b/packages/app/src/cli/services/dev/processes/theme-app-extension.ts deleted file mode 100644 index 49140cbc42..0000000000 --- a/packages/app/src/cli/services/dev/processes/theme-app-extension.ts +++ /dev/null @@ -1,118 +0,0 @@ -import {BaseProcess, DevProcessFunction} from './types.js' -import {ExtensionInstance} from '../../../models/extensions/extension-instance.js' -import {HostThemeManager} from '../../../utilities/extensions/theme/host-theme-manager.js' -import {themeExtensionArgs} from '../theme-extension-args.js' -import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js' -import {execCLI2} from '@shopify/cli-kit/node/ruby' -import {useEmbeddedThemeCLI} from '@shopify/cli-kit/node/context/local' -import {outputDebug} from '@shopify/cli-kit/node/output' -import {AdminSession, ensureAuthenticatedAdmin, ensureAuthenticatedStorefront} from '@shopify/cli-kit/node/session' - -// Tokens may be invalidated after as little as 4 minutes, better to be safe and refresh every 3 minutes -const PARTNERS_TOKEN_REFRESH_TIMEOUT_IN_MS = 3 * 60 * 1000 - -interface PreviewThemeAppExtensionsOptions { - adminSession: AdminSession - themeExtensionServerArgs: string[] - storefrontToken: string - developerPlatformClient: DeveloperPlatformClient -} - -export interface PreviewThemeAppExtensionsProcess extends BaseProcess { - type: 'theme-app-extensions' -} - -export const runThemeAppExtensionsServer: DevProcessFunction = async ( - {stdout, stderr, abortSignal}, - {adminSession, themeExtensionServerArgs: args, storefrontToken, developerPlatformClient}, -) => { - const refreshSequence = (attempt = 0) => { - outputDebug(`Refreshing Developer Platform token (attempt ${attempt})...`, stdout) - refreshToken(developerPlatformClient) - .then(() => { - outputDebug('Refreshed Developer Platform token successfully', stdout) - }) - .catch((error) => { - outputDebug(`Failed to refresh Developer Platform token: ${error}`, stderr) - if (attempt < 3) { - // Retry after 30 seconds. Sometimes we see random ECONNREFUSED errors - // so let's let the network sort itself out and retry. - setTimeout(() => refreshSequence(attempt + 1), 30 * 1000) - } else { - throw error - } - }) - } - setInterval(refreshSequence, PARTNERS_TOKEN_REFRESH_TIMEOUT_IN_MS) - - await refreshToken(developerPlatformClient) - await execCLI2(['extension', 'serve', ...args], { - store: adminSession.storeFqdn, - adminToken: adminSession.token, - storefrontToken, - stdout, - stderr, - signal: abortSignal, - }) -} - -export async function setupPreviewThemeAppExtensionsProcess({ - allExtensions, - apiKey, - storeFqdn, - theme, - themeExtensionPort, - notify, - developerPlatformClient, -}: Pick & { - allExtensions: ExtensionInstance[] - apiKey: string - storeFqdn: string - theme?: string - notify?: string - themeExtensionPort?: number -}): Promise { - const themeExtensions = allExtensions.filter((ext) => ext.isThemeExtension) - if (themeExtensions.length === 0) { - return - } - - const adminSession = await ensureAuthenticatedAdmin(storeFqdn) - const extension = themeExtensions[0]! - let optionsToOverwrite = {} - if (!theme) { - const theme = await new HostThemeManager(adminSession).findOrCreate() - optionsToOverwrite = { - theme: theme.id.toString(), - generateTmpTheme: true, - } - } - const [storefrontToken, args] = await Promise.all([ - ensureAuthenticatedStorefront(), - themeExtensionArgs(extension, apiKey, developerPlatformClient, { - theme, - themeExtensionPort, - notify, - ...optionsToOverwrite, - }), - ]) - - return { - type: 'theme-app-extensions', - prefix: 'theme-extensions', - function: runThemeAppExtensionsServer, - options: { - adminSession, - themeExtensionServerArgs: args, - storefrontToken, - developerPlatformClient, - }, - } -} - -async function refreshToken(developerPlatformClient: DeveloperPlatformClient) { - const newToken = await developerPlatformClient.refreshToken() - if (useEmbeddedThemeCLI()) { - await execCLI2(['theme', 'token', '--partners', newToken]) - } -} From 40749eb7fbfbba42e53b077ba540274bb71d95fc Mon Sep 17 00:00:00 2001 From: James Meng Date: Mon, 7 Oct 2024 15:37:39 -0700 Subject: [PATCH 6/9] Update tests and follow DevProcess patterns in theme-app-extension-next --- .../dev/processes/setup-dev-processes.test.ts | 32 +++++++++++---- .../dev/processes/setup-dev-processes.ts | 1 - .../theme-app-extension-next.test.ts | 7 ---- .../dev/processes/theme-app-extension-next.ts | 41 ++++++++++++------- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts b/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts index 6055c474d7..95a1111506 100644 --- a/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts +++ b/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts @@ -6,6 +6,7 @@ import {PreviewableExtensionProcess, launchPreviewableExtensionProcess} from './ import {launchGraphiQLServer} from './graphiql.js' import {pushUpdatesForDraftableExtensions} from './draftable-extension.js' import {pushUpdatesForDevSession} from './dev-session.js' +import {runThemeAppExtensionsServer} from './theme-app-extension-next.js' import { testAppAccessConfigExtension, testAppConfigExtensions, @@ -25,13 +26,15 @@ import {describe, test, expect, beforeEach, vi} from 'vitest' import {ensureAuthenticatedAdmin, ensureAuthenticatedStorefront} from '@shopify/cli-kit/node/session' import {Config} from '@oclif/core' import {getEnvironmentVariables} from '@shopify/cli-kit/node/environment' -import {initializeDevelopmentExtensionServer} from '@shopify/theme' +import {isStorefrontPasswordProtected} from '@shopify/theme' +import {fetchTheme} from '@shopify/cli-kit/node/themes/api' vi.mock('../../context/identifiers.js') vi.mock('@shopify/cli-kit/node/session.js') vi.mock('../fetch.js') vi.mock('@shopify/cli-kit/node/environment') - +vi.mock('@shopify/theme') +vi.mock('@shopify/cli-kit/node/themes/api') beforeEach(() => { // mocked for draft extensions vi.mocked(ensureDeploymentIdsPresence).mockResolvedValue({ @@ -48,6 +51,14 @@ beforeEach(() => { }) vi.mocked(ensureAuthenticatedStorefront).mockResolvedValue('storefront-token') vi.mocked(getEnvironmentVariables).mockReturnValue({}) + vi.mocked(isStorefrontPasswordProtected).mockResolvedValue(false) + vi.mocked(fetchTheme).mockResolvedValue({ + id: 1, + name: 'Theme', + createdAtRuntime: false, + role: 'theme', + processing: false, + }) }) describe('setup-dev-processes', () => { @@ -197,18 +208,21 @@ describe('setup-dev-processes', () => { expect(res.processes[4]).toMatchObject({ type: 'theme-app-extensions', prefix: 'theme-extensions', - function: initializeDevelopmentExtensionServer, + function: runThemeAppExtensionsServer, options: { + theme: { + id: 1, + name: 'Theme', + createdAtRuntime: false, + role: 'theme', + processing: false, + }, adminSession: { storeFqdn: 'store.myshopify.io', token: 'admin-token', }, - themeExtensionServerArgs: - './my-extension --api-key api-key --extension-id extension-id --extension-title theme-extension-name --extension-type THEME_APP_EXTENSION --theme 1'.split( - ' ', - ), - storefrontToken: 'storefront-token', - developerPlatformClient, + themeExtensionDirectory: './my-extension', + themeExtensionPort: 9293, }, }) expect(res.processes[5]).toMatchObject({ diff --git a/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts b/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts index e3e4ab1b5e..60a04e9c3e 100644 --- a/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts +++ b/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts @@ -140,7 +140,6 @@ export async function setupDevProcesses({ remoteApp, localApp, storeFqdn, - developerPlatformClient, theme: commandOptions.theme, themeExtensionPort: commandOptions.themeExtensionPort, }), diff --git a/packages/app/src/cli/services/dev/processes/theme-app-extension-next.test.ts b/packages/app/src/cli/services/dev/processes/theme-app-extension-next.test.ts index a3f31ddd82..45b9db7174 100644 --- a/packages/app/src/cli/services/dev/processes/theme-app-extension-next.test.ts +++ b/packages/app/src/cli/services/dev/processes/theme-app-extension-next.test.ts @@ -1,5 +1,4 @@ import {setupPreviewThemeAppExtensionsProcess, findOrCreateHostTheme} from './theme-app-extension-next.js' -import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js' import {HostThemeManager} from '../../../utilities/extensions/theme/host-theme-manager.js' import {testApp, testOrganizationApp, testThemeExtensions} from '../../../models/app/app.test-data.js' import {AdminSession, ensureAuthenticatedAdmin} from '@shopify/cli-kit/node/session' @@ -22,7 +21,6 @@ vi.mock('@shopify/cli-kit/node/ui', async (realImport) => { describe('setupPreviewThemeAppExtensionsProcess', () => { const mockAdminSession = {storeFqdn: 'test.myshopify.com'} as any as AdminSession - const mockDeveloperPlatformClient = {} as DeveloperPlatformClient beforeEach(() => { vi.mocked(ensureAuthenticatedAdmin).mockResolvedValue(mockAdminSession) @@ -33,14 +31,12 @@ describe('setupPreviewThemeAppExtensionsProcess', () => { const localApp = testApp() const remoteApp = testOrganizationApp() const storeFqdn = 'test.myshopify.com' - const developerPlatformClient = mockDeveloperPlatformClient // When const result = await setupPreviewThemeAppExtensionsProcess({ localApp, remoteApp, storeFqdn, - developerPlatformClient, }) // Then @@ -54,7 +50,6 @@ describe('setupPreviewThemeAppExtensionsProcess', () => { const storeFqdn = 'test.myshopify.com' const theme = '123' - const developerPlatformClient = mockDeveloperPlatformClient const remoteApp = testOrganizationApp() const localApp = testApp({allExtensions: [await testThemeExtensions()]}) @@ -64,12 +59,10 @@ describe('setupPreviewThemeAppExtensionsProcess', () => { remoteApp, storeFqdn, theme, - developerPlatformClient, }) // Then expect(result).toBeDefined() - expect(result?.options.developerPlatformClient).toBe(developerPlatformClient) expect(renderInfo).toBeCalledWith({ headline: 'The theme app extension development server is ready.', nextSteps: [ diff --git a/packages/app/src/cli/services/dev/processes/theme-app-extension-next.ts b/packages/app/src/cli/services/dev/processes/theme-app-extension-next.ts index ae8de30954..5fb364051d 100644 --- a/packages/app/src/cli/services/dev/processes/theme-app-extension-next.ts +++ b/packages/app/src/cli/services/dev/processes/theme-app-extension-next.ts @@ -1,5 +1,4 @@ -import {BaseProcess} from './types.js' -import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js' +import {BaseProcess, DevProcessFunction} from './types.js' import {HostThemeManager} from '../../../utilities/extensions/theme/host-theme-manager.js' import {AppInterface} from '../../../models/app/app.js' import {OrganizationApp} from '../../../models/organization.js' @@ -13,7 +12,11 @@ import {initializeDevelopmentExtensionServer, ensureValidPassword, isStorefrontP import {partnersFqdn} from '@shopify/cli-kit/node/context/fqdn' interface ThemeAppExtensionServerOptions { - developerPlatformClient: DeveloperPlatformClient + theme: Theme + adminSession: AdminSession + storefrontPassword?: string + themeExtensionDirectory: string + themeExtensionPort: number } interface HostThemeSetupOptions { @@ -22,7 +25,6 @@ interface HostThemeSetupOptions { storeFqdn: string theme?: string themeExtensionPort?: number - developerPlatformClient: DeveloperPlatformClient } export interface PreviewThemeAppExtensionsProcess extends BaseProcess { @@ -91,22 +93,31 @@ export async function setupPreviewThemeAppExtensionsProcess( return { type: 'theme-app-extensions', prefix: 'theme-extensions', - function: async () => { - const server = await initializeDevelopmentExtensionServer(theme, { - adminSession, - storefrontPassword, - themeExtensionDirectory, - themeExtensionPort, - }) - - await server.start() - }, + function: runThemeAppExtensionsServer, options: { - developerPlatformClient: options.developerPlatformClient, + theme, + adminSession, + storefrontPassword, + themeExtensionDirectory, + themeExtensionPort, }, } } +export const runThemeAppExtensionsServer: DevProcessFunction = async ( + _, + {theme, adminSession, storefrontPassword, themeExtensionDirectory, themeExtensionPort}, +) => { + const server = await initializeDevelopmentExtensionServer(theme, { + adminSession, + storefrontPassword, + themeExtensionDirectory, + themeExtensionPort, + }) + + await server.start() +} + export async function findOrCreateHostTheme(adminSession: AdminSession, theme?: string): Promise { let hostTheme: Theme | undefined if (theme) { From d7671f995819cca155bd52cf739e84a025c444f1 Mon Sep 17 00:00:00 2001 From: James Meng Date: Mon, 7 Oct 2024 10:51:31 -0700 Subject: [PATCH 7/9] Rename theme-app-extension-next to theme-app-extension --- .../src/cli/services/dev/processes/setup-dev-processes.test.ts | 2 +- .../app/src/cli/services/dev/processes/setup-dev-processes.ts | 2 +- ...e-app-extension-next.test.ts => theme-app-extension.test.ts} | 2 +- .../{theme-app-extension-next.ts => theme-app-extension.ts} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename packages/app/src/cli/services/dev/processes/{theme-app-extension-next.test.ts => theme-app-extension.test.ts} (99%) rename packages/app/src/cli/services/dev/processes/{theme-app-extension-next.ts => theme-app-extension.ts} (100%) diff --git a/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts b/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts index 95a1111506..63cfdb8bcb 100644 --- a/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts +++ b/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts @@ -6,7 +6,7 @@ import {PreviewableExtensionProcess, launchPreviewableExtensionProcess} from './ import {launchGraphiQLServer} from './graphiql.js' import {pushUpdatesForDraftableExtensions} from './draftable-extension.js' import {pushUpdatesForDevSession} from './dev-session.js' -import {runThemeAppExtensionsServer} from './theme-app-extension-next.js' +import {runThemeAppExtensionsServer} from './theme-app-extension.js' import { testAppAccessConfigExtension, testAppConfigExtensions, diff --git a/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts b/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts index 60a04e9c3e..3aa29c5864 100644 --- a/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts +++ b/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts @@ -1,5 +1,5 @@ import {BaseProcess, DevProcessFunction} from './types.js' -import {PreviewThemeAppExtensionsProcess, setupPreviewThemeAppExtensionsProcess} from './theme-app-extension-next.js' +import {PreviewThemeAppExtensionsProcess, setupPreviewThemeAppExtensionsProcess} from './theme-app-extension.js' import {PreviewableExtensionProcess, setupPreviewableExtensionsProcess} from './previewable-extension.js' import {DraftableExtensionProcess, setupDraftableExtensionsProcess} from './draftable-extension.js' import {SendWebhookProcess, setupSendUninstallWebhookProcess} from './uninstall-webhook.js' diff --git a/packages/app/src/cli/services/dev/processes/theme-app-extension-next.test.ts b/packages/app/src/cli/services/dev/processes/theme-app-extension.test.ts similarity index 99% rename from packages/app/src/cli/services/dev/processes/theme-app-extension-next.test.ts rename to packages/app/src/cli/services/dev/processes/theme-app-extension.test.ts index 45b9db7174..f289bfea4b 100644 --- a/packages/app/src/cli/services/dev/processes/theme-app-extension-next.test.ts +++ b/packages/app/src/cli/services/dev/processes/theme-app-extension.test.ts @@ -1,4 +1,4 @@ -import {setupPreviewThemeAppExtensionsProcess, findOrCreateHostTheme} from './theme-app-extension-next.js' +import {setupPreviewThemeAppExtensionsProcess, findOrCreateHostTheme} from './theme-app-extension.js' import {HostThemeManager} from '../../../utilities/extensions/theme/host-theme-manager.js' import {testApp, testOrganizationApp, testThemeExtensions} from '../../../models/app/app.test-data.js' import {AdminSession, ensureAuthenticatedAdmin} from '@shopify/cli-kit/node/session' diff --git a/packages/app/src/cli/services/dev/processes/theme-app-extension-next.ts b/packages/app/src/cli/services/dev/processes/theme-app-extension.ts similarity index 100% rename from packages/app/src/cli/services/dev/processes/theme-app-extension-next.ts rename to packages/app/src/cli/services/dev/processes/theme-app-extension.ts From c40310fd1579108d2b30cb010b4a7a44c4367b4a Mon Sep 17 00:00:00 2001 From: Nick Wesselman <27013789+nickwesselman@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:25:18 -0400 Subject: [PATCH 8/9] Fix AsyncLocalStorage with file watching --- .../src/cli/services/dev/extension/bundler.ts | 66 ++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/packages/app/src/cli/services/dev/extension/bundler.ts b/packages/app/src/cli/services/dev/extension/bundler.ts index c5af4d3451..618af19b65 100644 --- a/packages/app/src/cli/services/dev/extension/bundler.ts +++ b/packages/app/src/cli/services/dev/extension/bundler.ts @@ -13,6 +13,7 @@ import {FSWatcher} from 'chokidar' import micromatch from 'micromatch' import {deepCompare} from '@shopify/cli-kit/common/object' import {Writable} from 'stream' +import {AsyncResource} from 'async_hooks' export interface FileWatcherOptions { devOptions: ExtensionDevOptions @@ -170,37 +171,42 @@ Redeploy Paths: let buildController: AbortController | null const allPaths = [...buildPaths, ...configurationPaths] - const functionRebuildAndRedeployWatcher = chokidar.watch(allPaths, {ignored: '**/*.test.*'}).on('change', (path) => { - outputDebug(`Extension file at path ${path} changed`, stdout) - if (buildController) { - // terminate any existing builds - buildController.abort() - } - buildController = new AbortController() - const buildSignal = buildController.signal - const shouldBuild = micromatch.isMatch(path, buildPaths) - - reloadAndbuildIfNecessary(extension, shouldBuild, { - app, - stdout, - stderr, - useTasks: false, - signal: buildSignal, - environment: 'development', - appURL: url, - }) - .then(({newConfig, previousConfig}) => { - if (shouldBuild) { - if (buildSignal.aborted) return - return onChange() - } - - if (deepCompare(newConfig, previousConfig)) return - return onChange() + const functionRebuildAndRedeployWatcher = chokidar.watch(allPaths, {ignored: '**/*.test.*'}).on( + 'change', + // We need to bind the execution context to ensure the event handler can access the correct AsyncLocalStorage + // See also: https://nodejs.org/api/async_context.html#integrating-asyncresource-with-eventemitter + AsyncResource.bind((path) => { + outputDebug(`Extension file at path ${path} changed`, stdout) + if (buildController) { + // terminate any existing builds + buildController.abort() + } + buildController = new AbortController() + const buildSignal = buildController.signal + const shouldBuild = micromatch.isMatch(path, buildPaths) + + reloadAndbuildIfNecessary(extension, shouldBuild, { + app, + stdout, + stderr, + useTasks: false, + signal: buildSignal, + environment: 'development', + appURL: url, }) - .catch((error: Error) => onReloadAndBuildError(error)) - }) - listenForAbortOnWatcher(functionRebuildAndRedeployWatcher) + .then(({newConfig, previousConfig}) => { + if (shouldBuild) { + if (buildSignal.aborted) return + return onChange() + } + + if (deepCompare(newConfig, previousConfig)) return + return onChange() + }) + .catch((error: Error) => onReloadAndBuildError(error)) + listenForAbortOnWatcher(functionRebuildAndRedeployWatcher) + }), + ) } async function reloadAndbuildIfNecessary(extension: ExtensionInstance, build: boolean, options: ExtensionBuildOptions) { From eeafbdb253afaa1f728c77e2499a588d75bc9db8 Mon Sep 17 00:00:00 2001 From: Nick Wesselman <27013789+nickwesselman@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:35:25 -0400 Subject: [PATCH 9/9] fix listener abort --- packages/app/src/cli/services/dev/extension/bundler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/cli/services/dev/extension/bundler.ts b/packages/app/src/cli/services/dev/extension/bundler.ts index 618af19b65..89e0561914 100644 --- a/packages/app/src/cli/services/dev/extension/bundler.ts +++ b/packages/app/src/cli/services/dev/extension/bundler.ts @@ -204,9 +204,9 @@ Redeploy Paths: return onChange() }) .catch((error: Error) => onReloadAndBuildError(error)) - listenForAbortOnWatcher(functionRebuildAndRedeployWatcher) }), ) + listenForAbortOnWatcher(functionRebuildAndRedeployWatcher) } async function reloadAndbuildIfNecessary(extension: ExtensionInstance, build: boolean, options: ExtensionBuildOptions) {