From f98548c317187361287399726372413633ab398d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isaac=20Rold=C3=A1n?= Date: Tue, 7 Jul 2026 12:35:11 +0200 Subject: [PATCH] Remove legacy developer preview concepts Assisted-By: devx/782ef931-ac0a-4cc4-bff1-a2511c379664 --- .../cli/api/graphql/development_preview.ts | 36 - .../cli/api/graphql/find_app_preview_mode.ts | 19 - .../app/src/cli/models/app/app.test-data.ts | 24 - packages/app/src/cli/prompts/dev.test.ts | 36 +- packages/app/src/cli/prompts/dev.ts | 30 +- packages/app/src/cli/services/dev.ts | 17 - packages/app/src/cli/services/dev/ui.test.tsx | 234 +---- packages/app/src/cli/services/dev/ui.tsx | 30 +- .../services/dev/ui/components/Dev.test.tsx | 948 ------------------ .../cli/services/dev/ui/components/Dev.tsx | 275 ----- packages/app/src/cli/services/dev/urls.ts | 7 +- .../app/src/cli/services/extensions/common.ts | 27 - .../utilities/developer-platform-client.ts | 7 - .../app-management-client.ts | 15 - .../partners-client.ts | 20 - 15 files changed, 51 insertions(+), 1674 deletions(-) delete mode 100644 packages/app/src/cli/api/graphql/development_preview.ts delete mode 100644 packages/app/src/cli/api/graphql/find_app_preview_mode.ts delete mode 100644 packages/app/src/cli/services/dev/ui/components/Dev.test.tsx delete mode 100644 packages/app/src/cli/services/dev/ui/components/Dev.tsx diff --git a/packages/app/src/cli/api/graphql/development_preview.ts b/packages/app/src/cli/api/graphql/development_preview.ts deleted file mode 100644 index 66a0eb75d8e..00000000000 --- a/packages/app/src/cli/api/graphql/development_preview.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {gql} from 'graphql-request' - -export const DevelopmentStorePreviewUpdateQuery = gql` - mutation DevelopmentStorePreviewUpdate($input: DevelopmentStorePreviewUpdateInput!) { - developmentStorePreviewUpdate(input: $input) { - app { - id - developmentStorePreviewEnabled - } - userErrors { - message - field - } - } - } -` - -export interface DevelopmentStorePreviewUpdateInput { - input: { - apiKey: string - enabled: boolean - } -} - -export interface DevelopmentStorePreviewUpdateSchema { - developmentStorePreviewUpdate: { - app: { - id: string - developmentStorePreviewEnabled: boolean - } - userErrors: { - field: string[] - message: string - }[] - } -} diff --git a/packages/app/src/cli/api/graphql/find_app_preview_mode.ts b/packages/app/src/cli/api/graphql/find_app_preview_mode.ts deleted file mode 100644 index 421b0b70ffb..00000000000 --- a/packages/app/src/cli/api/graphql/find_app_preview_mode.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {gql} from 'graphql-request' - -export const FindAppPreviewModeQuery = gql` - query FindAppPreviewMode($apiKey: String!) { - app(apiKey: $apiKey) { - developmentStorePreviewEnabled - } - } -` - -export interface FindAppPreviewModeSchema { - app: { - developmentStorePreviewEnabled: boolean - } -} - -export interface FindAppPreviewModeVariables { - apiKey: string -} diff --git a/packages/app/src/cli/models/app/app.test-data.ts b/packages/app/src/cli/models/app/app.test-data.ts index 7ef89aab3a1..ba2a317a1bc 100644 --- a/packages/app/src/cli/models/app/app.test-data.ts +++ b/packages/app/src/cli/models/app/app.test-data.ts @@ -43,11 +43,6 @@ import {AllAppExtensionRegistrationsQuerySchema} from '../../api/graphql/all_app import {AppDeploySchema, AppDeployVariables} from '../../api/graphql/app_deploy.js' import {ExtensionCreateSchema, ExtensionCreateVariables} from '../../api/graphql/extension_create.js' import {ConvertDevToTransferDisabledStoreVariables} from '../../api/graphql/convert_dev_to_transfer_disabled_store.js' -import { - DevelopmentStorePreviewUpdateInput, - DevelopmentStorePreviewUpdateSchema, -} from '../../api/graphql/development_preview.js' -import {FindAppPreviewModeSchema, FindAppPreviewModeVariables} from '../../api/graphql/find_app_preview_mode.js' import {SendSampleWebhookSchema, SendSampleWebhookVariables} from '../../services/webhook/request-sample.js' import {PublicApiVersionsSchema} from '../../services/webhook/request-api-versions.js' import {WebhookTopicsSchema, WebhookTopicsVariables} from '../../services/webhook/request-topics.js' @@ -1272,22 +1267,6 @@ const convertedToTransferDisabledStoreResponse = { }, } -const updateDeveloperPreviewResponse: DevelopmentStorePreviewUpdateSchema = { - developmentStorePreviewUpdate: { - app: { - id: 'app-id', - developmentStorePreviewEnabled: true, - }, - userErrors: [], - }, -} - -const appPreviewModeResponse: FindAppPreviewModeSchema = { - app: { - developmentStorePreviewEnabled: true, - }, -} - const organizationsResponse: Organization[] = [testOrganization()] const sendSampleWebhookResponse: SendSampleWebhookSchema = { @@ -1385,9 +1364,6 @@ export function testDeveloperPlatformClient(stubs: Partial Promise.resolve(generateSignedUploadUrlResponse), convertToTransferDisabledStore: (_input: ConvertDevToTransferDisabledStoreVariables) => Promise.resolve(convertedToTransferDisabledStoreResponse), - updateDeveloperPreview: (_input: DevelopmentStorePreviewUpdateInput) => - Promise.resolve(updateDeveloperPreviewResponse), - appPreviewMode: (_input: FindAppPreviewModeVariables) => Promise.resolve(appPreviewModeResponse), sendSampleWebhook: (_input: SendSampleWebhookVariables) => Promise.resolve(sendSampleWebhookResponse), apiVersions: () => Promise.resolve(apiVersionsResponse), topics: (_input: WebhookTopicsVariables) => Promise.resolve(topicsResponse), diff --git a/packages/app/src/cli/prompts/dev.test.ts b/packages/app/src/cli/prompts/dev.test.ts index 6d45b5e9dae..51eee69ee3b 100644 --- a/packages/app/src/cli/prompts/dev.test.ts +++ b/packages/app/src/cli/prompts/dev.test.ts @@ -273,44 +273,17 @@ describe('createAsNewAppPrompt', () => { }) describe('updateURLsPrompt', () => { - test('shows legacy prompt when dev sessions is disabled', async () => { + test('shows prompt without app proxy', async () => { // Given vi.mocked(renderConfirmationPrompt).mockResolvedValue(true) const currentAppUrl = 'http://current-url' - const currentRedirectUrls = ['http://current-redirect-url1', 'http://current-redirect-url2'] const newUrls = { applicationUrl: 'http://new-url', redirectUrlWhitelist: ['http://new-redirect-url'], } // When - const got = await updateURLsPrompt(false, currentAppUrl, currentRedirectUrls, newUrls) - - // Then - expect(got).toEqual(true) - expect(renderConfirmationPrompt).toHaveBeenCalledWith({ - message: "Have Shopify automatically update your app's URL in order to create a preview experience?", - infoTable: { - 'Current app URL': ['http://current-url'], - 'Current redirect URLs': ['http://current-redirect-url1', 'http://current-redirect-url2'], - }, - confirmationMessage: 'Yes, automatically update', - cancellationMessage: 'No, never', - }) - }) - - test('shows dev sessions prompt when enabled without app proxy', async () => { - // Given - vi.mocked(renderConfirmationPrompt).mockResolvedValue(true) - const currentAppUrl = 'http://current-url' - const currentRedirectUrls: string[] = [] - const newUrls = { - applicationUrl: 'http://new-url', - redirectUrlWhitelist: ['http://new-redirect-url'], - } - - // When - const got = await updateURLsPrompt(true, currentAppUrl, currentRedirectUrls, newUrls) + const got = await updateURLsPrompt(currentAppUrl, newUrls) // Then expect(got).toEqual(true) @@ -327,11 +300,10 @@ describe('updateURLsPrompt', () => { }) }) - test('shows dev sessions prompt when enabled with app proxy', async () => { + test('shows prompt with app proxy', async () => { // Given vi.mocked(renderConfirmationPrompt).mockResolvedValue(true) const currentAppUrl = 'http://current-url' - const currentRedirectUrls: string[] = [] const newUrls: ApplicationURLs = { applicationUrl: 'http://new-url', redirectUrlWhitelist: ['http://new-redirect-url'], @@ -343,7 +315,7 @@ describe('updateURLsPrompt', () => { } // When - const got = await updateURLsPrompt(true, currentAppUrl, currentRedirectUrls, newUrls) + const got = await updateURLsPrompt(currentAppUrl, newUrls) // Then expect(got).toEqual(true) diff --git a/packages/app/src/cli/prompts/dev.ts b/packages/app/src/cli/prompts/dev.ts index dcdbb7ec2c6..e8c249fda76 100644 --- a/packages/app/src/cli/prompts/dev.ts +++ b/packages/app/src/cli/prompts/dev.ts @@ -154,39 +154,15 @@ export async function createAsNewAppPrompt(): Promise { }) } -export function updateURLsPrompt( - usingDevSessions: boolean, - currentAppUrl: string, - currentRedirectUrls: string[], - newURLs: ApplicationURLs, -): Promise { - if (usingDevSessions) { - return updateURLsPromptWithDevSessions(currentAppUrl, newURLs) - } - return legacyUpdateURLsPrompt(currentAppUrl, currentRedirectUrls) -} - -function legacyUpdateURLsPrompt(currentAppUrl: string, currentRedirectUrls: string[]): Promise { - return renderConfirmationPrompt({ - message: "Have Shopify automatically update your app's URL in order to create a preview experience?", - confirmationMessage: 'Yes, automatically update', - cancellationMessage: 'No, never', - infoTable: { - 'Current app URL': [currentAppUrl], - 'Current redirect URLs': currentRedirectUrls, - }, - }) -} - -function updateURLsPromptWithDevSessions(currentAppUrl: string, urls: ApplicationURLs): Promise { +export function updateURLsPrompt(currentAppUrl: string, newURLs: ApplicationURLs): Promise { const affectedConfigs = ['application_url', 'redirect_urls'] - if (urls.appProxy?.proxyUrl) { + if (newURLs.appProxy?.proxyUrl) { affectedConfigs.push('app_proxy') } const infoTable: {[key: string]: string[]} = { 'Currently released app URL': [currentAppUrl], - '=> Dev URL': [urls.applicationUrl], + '=> Dev URL': [newURLs.applicationUrl], 'Affected configurations': affectedConfigs, } diff --git a/packages/app/src/cli/services/dev.ts b/packages/app/src/cli/services/dev.ts index a73ceb5fcc4..0dfcb3b09e1 100644 --- a/packages/app/src/cli/services/dev.ts +++ b/packages/app/src/cli/services/dev.ts @@ -12,7 +12,6 @@ import {installAppDependencies} from './dependencies.js' import {DevConfig, DevProcesses, setupDevProcesses} from './dev/processes/setup-dev-processes.js' import {frontAndBackendConfig} from './dev/processes/utils.js' import {renderDev} from './dev/ui.js' -import {DeveloperPreviewController} from './dev/ui/components/Dev.js' import {DevProcessFunction} from './dev/processes/types.js' import {getCachedAppInfo, setCachedAppInfo} from './local-storage.js' import {fetchAppRemoteConfiguration} from './app/select-app.js' @@ -334,25 +333,18 @@ async function launchDevProcesses({ return outputProcess }) - const apiKey = config.remoteApp.apiKey const developerPlatformClient = config.developerPlatformClient const app = { - canEnablePreviewMode: false, - developmentStorePreviewEnabled: config.remoteApp.developmentStorePreviewEnabled, - apiKey, id: config.remoteApp.id, developerPlatformClient, - extensions: config.localApp.allExtensions, } return renderDev({ processes: processesForTaskRunner, previewUrl, graphiqlUrl, - graphiqlPort: config.graphiqlPort, app, abortController, - developerPreview: developerPreviewController(), shopFqdn: config.storeFqdn, devSessionStatusManager, appURL: config.localApp.devApplicationURLs?.applicationUrl, @@ -363,15 +355,6 @@ async function launchDevProcesses({ }) } -function developerPreviewController(): DeveloperPreviewController { - return { - fetchMode: () => Promise.resolve(false), - enable: () => Promise.resolve(false), - disable: () => Promise.resolve(), - update: () => Promise.resolve(false), - } -} - async function logMetadataForDev(options: { devOptions: DevOptions tunnelUrl: string diff --git a/packages/app/src/cli/services/dev/ui.test.tsx b/packages/app/src/cli/services/dev/ui.test.tsx index 425b1679e99..ad6e27c29e8 100644 --- a/packages/app/src/cli/services/dev/ui.test.tsx +++ b/packages/app/src/cli/services/dev/ui.test.tsx @@ -8,16 +8,8 @@ import {AbortController} from '@shopify/cli-kit/node/abort' import {terminalSupportsPrompting} from '@shopify/cli-kit/node/system' vi.mock('@shopify/cli-kit/node/system') -vi.mock('../context.js') vi.mock('./ui/components/DevSessionUI.js') -const developerPreview = { - fetchMode: vi.fn(async () => true), - enable: vi.fn(async () => true), - disable: vi.fn(async () => {}), - update: vi.fn(async (_state: boolean) => true), -} - const developerPlatformClient = testDeveloperPlatformClient() const devSessionStatusManager = new DevSessionStatusManager() @@ -33,32 +25,15 @@ describe('ui', () => { prefix: 'prefix', action: vi.fn(async (_stdout, _stderr, _signal) => {}), } - - const processes = [concurrentProcess] - const previewUrl = 'https://lala.cloudflare.io/' - const graphiqlUrl = 'https://lala.cloudflare.io/graphiql' - const shopFqdn = 'mystore.shopify.io' - const graphiqlPort = 1234 - const app = { - canEnablePreviewMode: true, - developmentStorePreviewEnabled: false, - apiKey: '123', - id: '123', - developerPlatformClient, - extensions: [], - } - const abortController = new AbortController() await renderDev({ - processes, - previewUrl, - graphiqlUrl, - graphiqlPort, - app, + processes: [concurrentProcess], + previewUrl: 'https://lala.cloudflare.io/', + graphiqlUrl: 'https://lala.cloudflare.io/graphiql', + app: {id: '123', developerPlatformClient}, abortController, - developerPreview, - shopFqdn, + shopFqdn: 'mystore.shopify.io', devSessionStatusManager, }) @@ -78,23 +53,12 @@ describe('ui', () => { action: vi.fn(async (_stdout, _stderr, _signal) => {}), } - const abortController = new AbortController() - await renderDev({ processes: [concurrentProcess], previewUrl: 'https://lala.cloudflare.io/', graphiqlUrl: 'https://lala.cloudflare.io/graphiql', - graphiqlPort: 1234, - app: { - canEnablePreviewMode: true, - developmentStorePreviewEnabled: false, - apiKey: '123', - id: '123', - developerPlatformClient, - extensions: [], - }, - abortController, - developerPreview, + app: {id: '123', developerPlatformClient}, + abortController: new AbortController(), shopFqdn: 'mystore.shopify.io', devSessionStatusManager, }) @@ -106,164 +70,28 @@ describe('ui', () => { write.mockRestore() }) - test("enable dev preview when terminal doesn't support TTY and the app supports it", async () => { - vi.mocked(terminalSupportsPrompting).mockReturnValue(false) - const concurrentProcess = { - prefix: 'prefix', - action: vi.fn(async (_stdout, _stderr, _signal) => {}), - } - - const processes = [concurrentProcess] - const previewUrl = 'https://lala.cloudflare.io/' - const graphiqlUrl = 'https://lala.cloudflare.io/graphiql' - const shopFqdn = 'mystore.shopify.io' - const graphiqlPort = 1234 - const app = { - canEnablePreviewMode: true, - developmentStorePreviewEnabled: false, - apiKey: '123', - id: '123', - developerPlatformClient, - extensions: [], - } - - const abortController = new AbortController() - - await renderDev({ - processes, - previewUrl, - graphiqlUrl, - graphiqlPort, - app, - abortController, - developerPreview, - shopFqdn, - devSessionStatusManager, - }) - abortController.abort() - - expect(developerPreview.enable).toHaveBeenCalled() - expect(developerPreview.disable).toHaveBeenCalled() - }) - - test("don't enable dev preview when terminal doesn't support TTY and the app doesn't supports it", async () => { - vi.mocked(terminalSupportsPrompting).mockReturnValue(false) - const concurrentProcess = { - prefix: 'prefix', - action: vi.fn(async (_stdout, _stderr, _signal) => {}), - } - - const processes = [concurrentProcess] - const previewUrl = 'https://lala.cloudflare.io/' - const graphiqlUrl = 'https://lala.cloudflare.io/graphiql' - const shopFqdn = 'mystore.shopify.io' - const graphiqlPort = 1234 - const app = { - canEnablePreviewMode: false, - developmentStorePreviewEnabled: false, - apiKey: '123', - id: '123', - developerPlatformClient, - extensions: [], - } - - const abortController = new AbortController() - - await renderDev({ - processes, - previewUrl, - graphiqlUrl, - graphiqlPort, - app, - abortController, - developerPreview, - shopFqdn, - devSessionStatusManager, - }) - abortController.abort() - - expect(developerPreview.enable).not.toHaveBeenCalled() - expect(developerPreview.disable).not.toHaveBeenCalled() - }) - - test('renders dev session UI when terminal supports TTY', async () => { - vi.mocked(terminalSupportsPrompting).mockReturnValue(true) - const concurrentProcess = { - prefix: 'prefix', - action: vi.fn(async (_stdout, _stderr, _signal) => {}), - } - - const processes = [concurrentProcess] - const previewUrl = 'https://lala.cloudflare.io/' - const graphiqlUrl = 'https://lala.cloudflare.io/graphiql' - const shopFqdn = 'mystore.shopify.io' - const graphiqlPort = 1234 - const app = { - canEnablePreviewMode: true, - developmentStorePreviewEnabled: false, - apiKey: '123', - id: '123', - developerPlatformClient, - extensions: [], - } - - const abortController = new AbortController() - - // eslint-disable-next-line @typescript-eslint/no-floating-promises - renderDev({ - processes, - previewUrl, - graphiqlUrl, - graphiqlPort, - app, - abortController, - developerPreview, - shopFqdn, - devSessionStatusManager, - }) - - await new Promise((resolve) => setTimeout(resolve, 10)) - - expect(vi.mocked(DevSessionUI)).toHaveBeenCalled() - expect(concurrentProcess.action).not.toHaveBeenCalled() - }) - test('renders DevSessionUI when terminal supports TTY', async () => { vi.mocked(terminalSupportsPrompting).mockReturnValue(true) const concurrentProcess = { prefix: 'prefix', action: vi.fn(async (_stdout, _stderr, _signal) => {}), } - - const processes = [concurrentProcess] - const previewUrl = 'https://lala.cloudflare.io/' - const graphiqlUrl = 'https://lala.cloudflare.io/graphiql' - const shopFqdn = 'mystore.shopify.io' - const graphiqlPort = 1234 - const app = { - canEnablePreviewMode: true, - developmentStorePreviewEnabled: false, - apiKey: '123', - id: '123', - developerPlatformClient: { - ...developerPlatformClient, - devSessionDelete: vi.fn(), - }, - extensions: [], - } - const abortController = new AbortController() // eslint-disable-next-line @typescript-eslint/no-floating-promises renderDev({ - processes, - previewUrl, - graphiqlUrl, - graphiqlPort, - app, + processes: [concurrentProcess], + previewUrl: 'https://lala.cloudflare.io/', + graphiqlUrl: 'https://lala.cloudflare.io/graphiql', + app: { + id: '123', + developerPlatformClient: { + ...developerPlatformClient, + devSessionDelete: vi.fn(), + }, + }, abortController, - developerPreview, - shopFqdn, + shopFqdn: 'mystore.shopify.io', devSessionStatusManager, }) @@ -271,7 +99,7 @@ describe('ui', () => { expect(vi.mocked(DevSessionUI)).toHaveBeenCalledWith( expect.objectContaining({ - processes, + processes: [concurrentProcess], abortController, devSessionStatusManager, onAbort: expect.any(Function), @@ -279,46 +107,38 @@ describe('ui', () => { // React 19 no longer passes legacy context as second argument undefined, ) + expect(concurrentProcess.action).not.toHaveBeenCalled() }) test('calls devSessionDelete when DevSessionUI aborts', async () => { vi.mocked(terminalSupportsPrompting).mockReturnValue(true) - const processes = [ - { - prefix: 'prefix', - action: vi.fn(async (_stdout, _stderr, _signal) => {}), - }, - ] const app = { - canEnablePreviewMode: false, - developmentStorePreviewEnabled: false, - apiKey: '123', id: '123', developerPlatformClient: { ...developerPlatformClient, devSessionDelete: vi.fn(), }, - extensions: [], } const shopFqdn = 'mystore.shopify.io' - const abortController = new AbortController() // eslint-disable-next-line @typescript-eslint/no-floating-promises renderDev({ - processes, + processes: [ + { + prefix: 'prefix', + action: vi.fn(async (_stdout, _stderr, _signal) => {}), + }, + ], previewUrl: '', graphiqlUrl: '', - graphiqlPort: 1234, app, - abortController, - developerPreview, + abortController: new AbortController(), shopFqdn, devSessionStatusManager, }) await new Promise((resolve) => setTimeout(resolve, 10)) - // Get the onAbort callback that was passed to DevSessionUI const onAbort = vi.mocked(DevSessionUI).mock.calls[0]?.[0]?.onAbort await onAbort?.() diff --git a/packages/app/src/cli/services/dev/ui.tsx b/packages/app/src/cli/services/dev/ui.tsx index aa4a29bf732..9eb23042433 100644 --- a/packages/app/src/cli/services/dev/ui.tsx +++ b/packages/app/src/cli/services/dev/ui.tsx @@ -1,17 +1,30 @@ -import {type DevProps} from './ui/components/Dev.js' import {DevSessionUI} from './ui/components/DevSessionUI.js' import {DevSessionStatusManager} from './processes/dev-session/dev-session-status-manager.js' +import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js' +import {OutputProcess} from '@shopify/cli-kit/node/output' +import {AbortController} from '@shopify/cli-kit/node/abort' import React from 'react' import {render} from '@shopify/cli-kit/node/ui' import {terminalSupportsPrompting} from '@shopify/cli-kit/node/system' +interface DevProps { + processes: OutputProcess[] + previewUrl: string + graphiqlUrl?: string + abortController: AbortController + shopFqdn: string + app: { + id: string + developerPlatformClient: DeveloperPlatformClient + } +} + export async function renderDev({ processes, previewUrl, app, abortController, graphiqlUrl, - developerPreview, shopFqdn, devSessionStatusManager, appURL, @@ -53,10 +66,7 @@ export async function renderDev({ processes, previewUrl, graphiqlUrl, - app, abortController, - developerPreview, - shopFqdn, }) } @@ -64,16 +74,8 @@ async function renderDevNonInteractive({ processes, previewUrl, graphiqlUrl, - app: {canEnablePreviewMode}, abortController, - developerPreview, -}: Omit) { - if (canEnablePreviewMode) { - await developerPreview.enable() - abortController?.signal.addEventListener('abort', async () => { - await developerPreview.disable() - }) - } +}: Pick) { process.stdout.write(`\nPreview URL: ${previewUrl}\n`) if (graphiqlUrl) { process.stdout.write(`GraphiQL URL (Admin API): ${graphiqlUrl}\n`) diff --git a/packages/app/src/cli/services/dev/ui/components/Dev.test.tsx b/packages/app/src/cli/services/dev/ui/components/Dev.test.tsx deleted file mode 100644 index 2808a12dbba..00000000000 --- a/packages/app/src/cli/services/dev/ui/components/Dev.test.tsx +++ /dev/null @@ -1,948 +0,0 @@ -import {calculatePrefixColumnSize, Dev} from './Dev.js' -import {testDeveloperPlatformClient, testUIExtension} from '../../../../models/app/app.test-data.js' -import { - getLastFrameAfterUnmount, - render, - sendInputAndWait, - sendInputAndWaitForContent, - Stdin, - waitForContent, - waitForInputsToBeReady, -} from '@shopify/cli-kit/node/testing/ui' -import {AbortController, AbortSignal} from '@shopify/cli-kit/node/abort' -import React from 'react' -import {describe, expect, test, vi} from 'vitest' -import {unstyled} from '@shopify/cli-kit/node/output' -import {openURL, sleep} from '@shopify/cli-kit/node/system' -import {Writable} from 'stream' - -vi.mock('@shopify/cli-kit/node/system', async () => { - const actual: any = await vi.importActual('@shopify/cli-kit/node/system') - return { - ...actual, - openURL: vi.fn(), - } -}) - -vi.mock('../../../context.js') -vi.mock('../../fetch.js') -vi.mock('../../processes/dev-session.js') -vi.mock('@shopify/cli-kit/node/hooks/postrun', async () => { - const actual: any = await vi.importActual('@shopify/cli-kit/node/hooks/postrun') - return { - ...actual, - waitForPostRunHookAndExit: vi.fn(), - } -}) - -const developerPlatformClient = testDeveloperPlatformClient() - -const testApp = { - canEnablePreviewMode: true, - developmentStorePreviewEnabled: false, - apiKey: '123', - id: '123', - developerPlatformClient, - extensions: [], -} - -const developerPreview = { - fetchMode: vi.fn(async () => true), - enable: vi.fn(async () => true), - disable: vi.fn(async () => {}), - update: vi.fn(async (_state: boolean) => true), -} - -describe('Dev', () => { - test('renders a stream of concurrent outputs from sub-processes, shortcuts and a preview url', async () => { - // Given - let backendPromiseResolve: () => void - let frontendPromiseResolve: () => void - - const backendPromise = new Promise(function (resolve, _reject) { - backendPromiseResolve = resolve - }) - - const frontendPromise = new Promise(function (resolve, _reject) { - frontendPromiseResolve = resolve - }) - - const backendProcess = { - prefix: 'backend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - stdout.write('first backend message') - stdout.write('second backend message') - stdout.write('third backend message') - - backendPromiseResolve() - }, - } - - const frontendProcess = { - prefix: 'frontend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - await backendPromise - - stdout.write('first frontend message') - stdout.write('second frontend message') - stdout.write('third frontend message') - - frontendPromiseResolve() - }, - } - // When - - const renderInstance = render( - , - ) - - await frontendPromise - // Wait for React 19 to render the process output - await waitForContent(renderInstance, 'third frontend message') - - // Then - expect(unstyled(renderInstance.lastFrame()!.replace(/\d/g, '0'))).toMatchInlineSnapshot(` - "00:00:00 │ backend │ first backend message - 00:00:00 │ backend │ second backend message - 00:00:00 │ backend │ third backend message - 00:00:00 │ frontend │ first frontend message - 00:00:00 │ frontend │ second frontend message - 00:00:00 │ frontend │ third frontend message - - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - " - `) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test("doesn't render shortcuts if the stdin is not a TTY", async () => { - // Given - let backendPromiseResolve: () => void - let frontendPromiseResolve: () => void - - const backendPromise = new Promise(function (resolve, _reject) { - backendPromiseResolve = resolve - }) - - const frontendPromise = new Promise(function (resolve, _reject) { - frontendPromiseResolve = resolve - }) - - const backendProcess = { - prefix: 'backend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - stdout.write('first backend message') - stdout.write('second backend message') - stdout.write('third backend message') - - backendPromiseResolve() - }, - } - - const frontendProcess = { - prefix: 'frontend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - await backendPromise - - stdout.write('first frontend message') - stdout.write('second frontend message') - stdout.write('third frontend message') - - frontendPromiseResolve() - - // await promise that never resolves - await new Promise(() => {}) - }, - } - // When - - const renderInstance = render( - , - {stdin: new Stdin({isTTY: false})}, - ) - - await frontendPromise - // Wait for React 19 to render the process output - await waitForContent(renderInstance, 'third frontend message') - - // Then - expect(unstyled(renderInstance.lastFrame()!.replace(/\d/g, '0'))).toMatchInlineSnapshot(` - "00:00:00 │ backend │ first backend message - 00:00:00 │ backend │ second backend message - 00:00:00 │ backend │ third backend message - 00:00:00 │ frontend │ first frontend message - 00:00:00 │ frontend │ second frontend message - 00:00:00 │ frontend │ third frontend message - - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - " - `) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('opens the previewUrl when p is pressed', async () => { - // When - const renderInstance = render( - , - ) - - await waitForInputsToBeReady() - await sendInputAndWait(renderInstance, 10, 'p') - // Then - expect(vi.mocked(openURL)).toHaveBeenNthCalledWith(1, 'https://shopify.com') - - renderInstance.unmount() - }) - - test('quits when q is pressed', async () => { - // Given - const abortController = new AbortController() - const abort = vi.spyOn(abortController, 'abort') - - // When - const renderInstance = render( - , - ) - - const promise = renderInstance.waitUntilExit() - - await waitForInputsToBeReady() - renderInstance.stdin.write('q') - - await promise - // Then - expect(abort).toHaveBeenCalledOnce() - }) - - test('quits when ctrl+c is pressed', async () => { - // Given - const abortController = new AbortController() - const abort = vi.spyOn(abortController, 'abort') - - // When - const renderInstance = render( - , - ) - - const promise = renderInstance.waitUntilExit() - - await waitForInputsToBeReady() - await sendInputAndWait(renderInstance, 10, '\u0003') - - await promise - // Then - expect(abort).toHaveBeenCalledOnce() - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('abortController can be used to exit from outside and should preserve static output', async () => { - // Given - const abortController = new AbortController() - - const backendProcess = { - prefix: 'backend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - stdout.write('first backend message') - stdout.write('second backend message') - stdout.write('third backend message') - - // await promise that never resolves - await new Promise(() => {}) - }, - } - - // When - - const renderInstance = render( - , - ) - - const promise = renderInstance.waitUntilExit() - - // Wait for process output to render before aborting - await waitForContent(renderInstance, 'first backend message') - - abortController.abort() - - await promise - - expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - "00:00:00 │ backend │ first backend message - 00:00:00 │ backend │ second backend message - 00:00:00 │ backend │ third backend message - " - `) - expect(developerPreview.disable).toHaveBeenCalledOnce() - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('abortController can be used to exit with an error', async () => { - // Given - const abortController = new AbortController() - - const backendProcess = { - prefix: 'backend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - stdout.write('first backend message') - stdout.write('second backend message') - stdout.write('third backend message') - - // await promise that never resolves - await new Promise(() => {}) - }, - } - - // When - - const renderInstance = render( - , - ) - - const promise = renderInstance.waitUntilExit() - - // Wait for process output to render before aborting - await waitForContent(renderInstance, 'first backend message') - - abortController.abort('something went wrong') - - await promise - - expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - "00:00:00 │ backend │ first backend message - 00:00:00 │ backend │ second backend message - 00:00:00 │ backend │ third backend message - " - `) - expect(developerPreview.disable).toHaveBeenCalledOnce() - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('accepts inputs when the processes resolve', async () => { - // Given - const backendProcess = { - prefix: 'backend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - stdout.write('first backend message') - stdout.write('second backend message') - stdout.write('third backend message') - }, - } - - // When - const renderInstance = render( - , - ) - - await waitForContent(renderInstance, 'first backend message') - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - "00:00:00 │ backend │ first backend message - 00:00:00 │ backend │ second backend message - 00:00:00 │ backend │ third backend message - - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - " - `) - - await waitForInputsToBeReady() - await sendInputAndWait(renderInstance, 10, 'p') - expect(vi.mocked(openURL)).toHaveBeenNthCalledWith(1, 'https://shopify.com') - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('when a process throws an error it calls abort on the abortController', async () => { - // Given - const backendProcess = { - prefix: 'backend', - action: async (_stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - throw new Error('something went wrong') - }, - } - - const abortController = new AbortController() - const abort = vi.spyOn(abortController, 'abort') - - // When - const renderInstance = render( - , - ) - - await expect(renderInstance.waitUntilExit()).rejects.toThrow('something went wrong') - expect(abort).toHaveBeenNthCalledWith(1, new Error('something went wrong')) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('polls for preview mode', async () => { - // Given - vi.mocked(developerPreview.fetchMode).mockReset() - vi.mocked(developerPreview.enable).mockReset() - vi.mocked(developerPreview.fetchMode).mockResolvedValue(true) - - // When - const renderInstance = render( - , - ) - - // Then - // Wait for polling to call fetchMode multiple times (avoids timing-dependent flakiness) - await vi.waitFor( - () => { - expect(developerPreview.fetchMode.mock.calls.length).toBeGreaterThanOrEqual(2) - }, - {timeout: 2000, interval: 10}, - ) - - // enable should be called once at startup - expect(developerPreview.enable).toHaveBeenCalledTimes(1) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test("doesn't poll for preview mode when the app does not support it", async () => { - // Given - const backendProcess = { - prefix: 'backend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - stdout.write('first backend message') - stdout.write('second backend message') - stdout.write('third backend message') - }, - } - - // Use a locally-scoped mock to isolate from any leaked polling intervals - // from previous tests that might call the module-level developerPreview mock - const localDeveloperPreview = { - fetchMode: vi.fn(async () => true), - enable: vi.fn(async () => true), - disable: vi.fn(async () => {}), - update: vi.fn(async (_state: boolean) => true), - } - - // When - const renderInstance = render( - , - ) - - // Then - // Wait to ensure no polling occurs - await sleep(0.05) - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - "00:00:00 │ backend │ first backend message - 00:00:00 │ backend │ second backend message - 00:00:00 │ backend │ third backend message - - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - " - `) - expect(localDeveloperPreview.enable).not.toHaveBeenCalled() - expect(localDeveloperPreview.fetchMode).not.toHaveBeenCalled() - - // Verify 'd' input doesn't trigger update when app doesn't support preview - await waitForInputsToBeReady() - await sendInputAndWait(renderInstance, 10, 'd') - expect(localDeveloperPreview.update).not.toHaveBeenCalled() - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('shows an error message when polling for preview mode fails', async () => { - vi.mocked(developerPreview.fetchMode).mockReset() - vi.mocked(developerPreview.fetchMode).mockRejectedValue(new Error('fail')) - - // When - const renderInstance = render( - , - ) - - await waitForContent(renderInstance, 'Failed to fetch') - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d\d:\d\d:\d\d/g, '00:00:00')).toMatchInlineSnapshot(` - " - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:1234/graphiql - Failed to fetch the latest status of the development store preview, trying again in 5 seconds. - " - `) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('enables preview mode when pressing d', async () => { - // Given - vi.mocked(developerPreview.update).mockResolvedValueOnce(true) - - const renderInstance = render( - , - ) - - await waitForInputsToBeReady() - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - " - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - " - `) - - await waitForInputsToBeReady() - await sendInputAndWait(renderInstance, 10, 'd') - expect(developerPreview.update).toHaveBeenCalledOnce() - - await waitForContent(renderInstance, 'off') - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - " - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✖ off - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - " - `) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test("shows an error message if enabling preview mode by pressing d doesn't succeed", async () => { - // Given - vi.mocked(developerPreview.update).mockResolvedValueOnce(false) - - const backendProcess = { - prefix: 'backend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - stdout.write('first backend message') - stdout.write('second backend message') - stdout.write('third backend message') - }, - } - - const renderInstance = render( - , - ) - - await waitForInputsToBeReady() - await sendInputAndWaitForContent(renderInstance, 'Failed to turn off development store preview.', 'd') - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - "00:00:00 │ backend │ first backend message - 00:00:00 │ backend │ second backend message - 00:00:00 │ backend │ third backend message - - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - Failed to turn off development store preview. - " - `) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('shows an error message if enabling preview mode by pressing d throws an exception', async () => { - // Given - vi.mocked(developerPreview.update).mockRejectedValueOnce(new Error('something went wrong')) - - const backendProcess = { - prefix: 'backend', - action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => { - stdout.write('first backend message') - stdout.write('second backend message') - stdout.write('third backend message') - }, - } - - const renderInstance = render( - , - ) - - await waitForInputsToBeReady() - await sendInputAndWaitForContent(renderInstance, 'Failed to turn off development store preview.', 'd') - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - "00:00:00 │ backend │ first backend message - 00:00:00 │ backend │ second backend message - 00:00:00 │ backend │ third backend message - - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - Failed to turn off development store preview. - " - `) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('enables preview mode at startup', async () => { - // Given - const renderInstance = render( - , - ) - - await waitForInputsToBeReady() - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - " - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - " - `) - - await waitForInputsToBeReady() - - expect(developerPreview.enable).toHaveBeenCalledOnce() - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('shows an error message if enabling preview mode at startup fails', async () => { - // Given - vi.mocked(developerPreview.enable).mockRejectedValueOnce(new Error('something went wrong')) - - const renderInstance = render( - , - ) - - await waitForContent(renderInstance, 'Failed to turn on development store preview automatically.') - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - " - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✖ off - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - Failed to turn on development store preview automatically. - Try turning it on manually by pressing \`d\`. - " - `) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) - - test('shows an error if handling input throws an error', async () => { - vi.mocked(openURL).mockRejectedValueOnce(new Error('something went wrong')) - - const renderInstance = render( - , - ) - - await waitForInputsToBeReady() - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - " - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - " - `) - - await waitForInputsToBeReady() - await sendInputAndWaitForContent(renderInstance, 'Failed to handle your input.', 'p') - - expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(` - " - ──────────────────────────────────────────────────────────────────────────────────────────────────── - - › Press d │ toggle development store preview: ✔ on - › Press g │ open GraphiQL (Admin API) in your browser - › Press p │ preview in your browser - › Press q │ quit - - Preview URL: https://shopify.com - GraphiQL URL: http://localhost:0000/graphiql - Failed to handle your input. - " - `) - - // unmount so that polling is cleared after every test - renderInstance.unmount() - }) -}) - -describe('calculatePrefixColumnSize', () => { - test('returns max size of processes and extensions', async () => { - // Given - const processes = [ - {prefix: '1', action: async () => {}}, - {prefix: '12', action: async () => {}}, - {prefix: '123', action: async () => {}}, - ] - const extensions = [ - await testUIExtension({configuration: {name: 'Extension 1', handle: '1234', type: 'ui_extension'}}), - await testUIExtension({configuration: {name: 'Extension 2', handle: '12345', type: 'ui_extension'}}), - await testUIExtension({configuration: {name: 'Extension 3', handle: '123456', type: 'ui_extension'}}), - ] - - // When - const result = calculatePrefixColumnSize(processes, extensions) - - // Then - expect(result).toBe(6) - }) -}) diff --git a/packages/app/src/cli/services/dev/ui/components/Dev.tsx b/packages/app/src/cli/services/dev/ui/components/Dev.tsx deleted file mode 100644 index 32f7f163666..00000000000 --- a/packages/app/src/cli/services/dev/ui/components/Dev.tsx +++ /dev/null @@ -1,275 +0,0 @@ -import metadata from '../../../../metadata.js' -import {DeveloperPlatformClient} from '../../../../utilities/developer-platform-client.js' -import {ExtensionInstance} from '../../../../models/extensions/extension-instance.js' -import {OutputProcess} from '@shopify/cli-kit/node/output' -import {ConcurrentOutput, Link} from '@shopify/cli-kit/node/ui/components' -import {useAbortSignal} from '@shopify/cli-kit/node/ui/hooks' -import React, {FunctionComponent, useEffect, useMemo, useRef, useState} from 'react' -import {AbortController, AbortSignal} from '@shopify/cli-kit/node/abort' -import {Box, Text, useInput, useStdin} from '@shopify/cli-kit/node/ink' -import {handleCtrlC} from '@shopify/cli-kit/node/ui' -import {openURL} from '@shopify/cli-kit/node/system' -import figures from '@shopify/cli-kit/node/figures' -import {waitForPostRunHookAndExit} from '@shopify/cli-kit/node/hooks/postrun' -import {Writable} from 'stream' - -export interface DeveloperPreviewController { - fetchMode: () => Promise - enable: () => Promise - disable: () => Promise - update: (state: boolean) => Promise -} - -export interface DevProps { - processes: OutputProcess[] - abortController: AbortController - previewUrl: string - graphiqlUrl?: string - graphiqlPort: number - app: { - canEnablePreviewMode: boolean - developmentStorePreviewEnabled?: boolean - id: string - apiKey: string - developerPlatformClient: DeveloperPlatformClient - extensions: ExtensionInstance[] - } - pollingTime?: number - developerPreview: DeveloperPreviewController - isEditionWeek?: boolean - shopFqdn: string -} - -const calculatePrefixColumnSize = (processes: OutputProcess[], extensions: ExtensionInstance[]) => { - return Math.max( - ...processes.map((process) => process.prefix.length), - ...extensions.map((extension) => extension.handle.length), - ) -} - -const Dev: FunctionComponent = ({ - abortController, - processes, - previewUrl, - graphiqlUrl = '', - graphiqlPort, - app, - pollingTime = 5000, - developerPreview, - isEditionWeek, -}) => { - const {canEnablePreviewMode, developmentStorePreviewEnabled} = app - - const {isRawModeSupported: canUseShortcuts} = useStdin() - const pollingInterval = useRef() - const localhostGraphiqlUrl = `http://localhost:${graphiqlPort}/graphiql` - - const [isShuttingDownMessage, setIsShuttingDownMessage] = useState(undefined) - const [devPreviewEnabled, setDevPreviewEnabled] = useState(true) - const [error, setError] = useState(undefined) - - const {isAborted} = useAbortSignal(abortController.signal, async (err) => { - if (err) { - setIsShuttingDownMessage('Shutting down dev because of an error ...') - } else { - setIsShuttingDownMessage('Shutting down dev ...') - waitForPostRunHookAndExit() - } - clearInterval(pollingInterval.current) - await developerPreview.disable() - }) - - const errorHandledProcesses = useMemo(() => { - return processes.map((process) => { - return { - ...process, - action: async (stdout: Writable, stderr: Writable, signal: AbortSignal) => { - try { - return await process.action(stdout, stderr, signal) - // eslint-disable-next-line no-catch-all/no-catch-all - } catch (error) { - abortController.abort(error) - } - }, - } - }) - }, [processes, abortController]) - - useEffect(() => { - const pollDevPreviewMode = async () => { - try { - const enabled = await developerPreview.fetchMode() - setDevPreviewEnabled(enabled ?? false) - setError('') - // eslint-disable-next-line no-catch-all/no-catch-all - } catch (_) { - setError('Failed to fetch the latest status of the development store preview, trying again in 5 seconds.') - } - } - - const enablePreviewMode = async () => { - // Enable dev preview on app dev start - try { - await developerPreview.enable() - setError('') - // eslint-disable-next-line no-catch-all/no-catch-all - } catch (_) { - setError( - 'Failed to turn on development store preview automatically.\nTry turning it on manually by pressing `d`.', - ) - setDevPreviewEnabled(Boolean(developmentStorePreviewEnabled)) - } - } - - if (canEnablePreviewMode) { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - enablePreviewMode() - - const startPolling = () => { - return setInterval( - // eslint-disable-next-line @typescript-eslint/no-misused-promises - () => pollDevPreviewMode(), - pollingTime, - ) - } - - pollingInterval.current = startPolling() - } - - return () => { - clearInterval(pollingInterval.current) - } - }, [canEnablePreviewMode]) - - useInput( - (input, key) => { - handleCtrlC(input, key, () => abortController.abort()) - - const onInput = async () => { - try { - setError('') - - if (input === 'p' && previewUrl) { - await metadata.addPublicMetadata(() => ({ - cmd_dev_preview_url_opened: true, - })) - await openURL(previewUrl) - } else if (input === 'g' && graphiqlUrl) { - await metadata.addPublicMetadata(() => ({ - cmd_dev_graphiql_opened: true, - })) - await openURL(localhostGraphiqlUrl) - } else if (input === 'q') { - abortController.abort() - } else if (input === 'e' && isEditionWeek) { - await openURL('https://shopify.link/yQmk') - } else if (input === 'd' && canEnablePreviewMode) { - await metadata.addPublicMetadata(() => ({ - cmd_dev_dev_preview_toggle_used: true, - })) - const newDevPreviewEnabled = !devPreviewEnabled - setDevPreviewEnabled(newDevPreviewEnabled) - try { - const developerPreviewUpdateSucceded = await developerPreview.update(newDevPreviewEnabled) - if (!developerPreviewUpdateSucceded) { - throw new Error(`Failed to turn ${newDevPreviewEnabled ? 'on' : 'off'} development store preview.`) - } - // eslint-disable-next-line no-catch-all/no-catch-all - } catch (_) { - setDevPreviewEnabled(devPreviewEnabled) - setError(`Failed to turn ${newDevPreviewEnabled ? 'on' : 'off'} development store preview.`) - } - } - // eslint-disable-next-line no-catch-all/no-catch-all - } catch (_) { - setError('Failed to handle your input.') - } - } - - // eslint-disable-next-line @typescript-eslint/no-floating-promises - onInput() - }, - {isActive: Boolean(canUseShortcuts)}, - ) - - const now = new Date() - const season = now.getMonth() > 3 ? 'Summer' : 'Winter' - const year = now.getFullYear() - - return ( - <> - - {/* eslint-disable-next-line no-negated-condition */} - {!isAborted ? ( - - {canUseShortcuts ? ( - - {isEditionWeek ? ( - - {figures.pointerSmall} Press e {figures.lineVertical} check out {season} Edition - {` ${year}`}, live NOW with 100+ product announcements! - - ) : null} - {canEnablePreviewMode ? ( - - {figures.pointerSmall} Press d {figures.lineVertical} toggle development store - preview: {} - {devPreviewEnabled ? ✔ on : ✖ off} - - ) : null} - {graphiqlUrl ? ( - - {figures.pointerSmall} Press g {figures.lineVertical} open GraphiQL (Admin API) in - your browser - - ) : null} - - {figures.pointerSmall} Press p {figures.lineVertical} preview in your browser - - - {figures.pointerSmall} Press q {figures.lineVertical} quit - - - ) : null} - - - {isShuttingDownMessage ? ( - {isShuttingDownMessage} - ) : ( - <> - - Preview URL: - - {graphiqlUrl ? ( - - GraphiQL URL: - - ) : null} - - )} - - - {error ? {error} : null} - - ) : null} - - ) -} - -export {Dev, calculatePrefixColumnSize} diff --git a/packages/app/src/cli/services/dev/urls.ts b/packages/app/src/cli/services/dev/urls.ts index d4f356b5666..5065743a545 100644 --- a/packages/app/src/cli/services/dev/urls.ts +++ b/packages/app/src/cli/services/dev/urls.ts @@ -235,12 +235,7 @@ export async function shouldOrPromptUpdateURLs(options: ShouldOrPromptUpdateURLs let shouldUpdateURLs: boolean = options.cachedUpdateURLs === true if (options.cachedUpdateURLs === undefined) { - shouldUpdateURLs = await updateURLsPrompt( - true, - options.currentURLs.applicationUrl, - options.currentURLs.redirectUrlWhitelist, - options.newURLs, - ) + shouldUpdateURLs = await updateURLsPrompt(options.currentURLs.applicationUrl, options.newURLs) if (options.localApp) { const localConfiguration = options.localApp.configuration diff --git a/packages/app/src/cli/services/extensions/common.ts b/packages/app/src/cli/services/extensions/common.ts index b218e096010..79ad12eaeed 100644 --- a/packages/app/src/cli/services/extensions/common.ts +++ b/packages/app/src/cli/services/extensions/common.ts @@ -1,7 +1,6 @@ import {AppInterface} from '../../models/app/app.js' import {blocks, configurationFileNames} from '../../constants.js' import {ExtensionFlavor} from '../../models/app/template.js' -import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js' import {joinPath} from '@shopify/cli-kit/node/path' import {fileExists, mkdir, touchFile} from '@shopify/cli-kit/node/fs' import {AbortError} from '@shopify/cli-kit/node/error' @@ -31,29 +30,3 @@ export async function ensureExtensionDirectoryExists({name, app}: {name: string; await touchFile(joinPath(extensionDirectory, configurationFileNames.lockFile)) return extensionDirectory } - -export async function canEnablePreviewMode({ - localApp, - developerPlatformClient, - apiKey, - organizationId, -}: { - localApp: AppInterface - developerPlatformClient: DeveloperPlatformClient - apiKey: string - organizationId: string -}) { - const {dashboardManagedExtensionRegistrations} = ( - await developerPlatformClient.appExtensionRegistrations({ - id: apiKey, - apiKey, - organizationId, - }) - ).app - if (dashboardManagedExtensionRegistrations.length > 0) return true - const themeExtensions = localApp.allExtensions.filter((ext) => ext.isThemeExtension) - if (themeExtensions.length > 0) return true - if (localApp.allExtensions.length > 0) return true - - return false -} diff --git a/packages/app/src/cli/utilities/developer-platform-client.ts b/packages/app/src/cli/utilities/developer-platform-client.ts index a5af2a5f01b..f16b4942799 100644 --- a/packages/app/src/cli/utilities/developer-platform-client.ts +++ b/packages/app/src/cli/utilities/developer-platform-client.ts @@ -17,11 +17,6 @@ import { ConvertDevToTransferDisabledStoreVariables, } from '../api/graphql/convert_dev_to_transfer_disabled_store.js' import {AppVersionsQuerySchema} from '../api/graphql/get_versions_list.js' -import { - DevelopmentStorePreviewUpdateInput, - DevelopmentStorePreviewUpdateSchema, -} from '../api/graphql/development_preview.js' -import {FindAppPreviewModeSchema, FindAppPreviewModeVariables} from '../api/graphql/find_app_preview_mode.js' import {AppReleaseSchema} from '../api/graphql/app_release.js' import {AppVersionsDiffSchema} from '../api/graphql/app_versions_diff.js' import {SendSampleWebhookSchema, SendSampleWebhookVariables} from '../services/webhook/request-sample.js' @@ -270,8 +265,6 @@ export interface DeveloperPlatformClient { convertToTransferDisabledStore: ( input: ConvertDevToTransferDisabledStoreVariables, ) => Promise - updateDeveloperPreview: (input: DevelopmentStorePreviewUpdateInput) => Promise - appPreviewMode: (input: FindAppPreviewModeVariables) => Promise sendSampleWebhook: (input: SendSampleWebhookVariables, organizationId: string) => Promise apiVersions: (organizationId: string) => Promise topics: (input: WebhookTopicsVariables, organizationId: string) => Promise diff --git a/packages/app/src/cli/utilities/developer-platform-client/app-management-client.ts b/packages/app/src/cli/utilities/developer-platform-client/app-management-client.ts index a17b9bd857f..505a9ca1a3b 100644 --- a/packages/app/src/cli/utilities/developer-platform-client/app-management-client.ts +++ b/packages/app/src/cli/utilities/developer-platform-client/app-management-client.ts @@ -49,11 +49,6 @@ import { ConvertDevToTransferDisabledSchema, ConvertDevToTransferDisabledStoreVariables, } from '../../api/graphql/convert_dev_to_transfer_disabled_store.js' -import {FindAppPreviewModeSchema, FindAppPreviewModeVariables} from '../../api/graphql/find_app_preview_mode.js' -import { - DevelopmentStorePreviewUpdateInput, - DevelopmentStorePreviewUpdateSchema, -} from '../../api/graphql/development_preview.js' import {AppReleaseSchema} from '../../api/graphql/app_release.js' import {AppVersionsDiffSchema} from '../../api/graphql/app_versions_diff.js' import { @@ -908,16 +903,6 @@ export class AppManagementClient implements DeveloperPlatformClient { throw new BugError('Not implemented: convertToTransferDisabledStore') } - async updateDeveloperPreview( - _input: DevelopmentStorePreviewUpdateInput, - ): Promise { - throw new BugError('Not implemented: updateDeveloperPreview') - } - - async appPreviewMode(_input: FindAppPreviewModeVariables): Promise { - throw new BugError('Not implemented: appPreviewMode') - } - async sendSampleWebhook(input: SendSampleWebhookVariables, organizationId: string): Promise { const query = CliTesting const variables = { diff --git a/packages/app/src/cli/utilities/developer-platform-client/partners-client.ts b/packages/app/src/cli/utilities/developer-platform-client/partners-client.ts index e17d136507b..c32a62a2c77 100644 --- a/packages/app/src/cli/utilities/developer-platform-client/partners-client.ts +++ b/packages/app/src/cli/utilities/developer-platform-client/partners-client.ts @@ -60,16 +60,6 @@ import { AppVersionsQueryVariables, AppVersionsQuerySchema, } from '../../api/graphql/get_versions_list.js' -import { - DevelopmentStorePreviewUpdateInput, - DevelopmentStorePreviewUpdateQuery, - DevelopmentStorePreviewUpdateSchema, -} from '../../api/graphql/development_preview.js' -import { - FindAppPreviewModeQuery, - FindAppPreviewModeSchema, - FindAppPreviewModeVariables, -} from '../../api/graphql/find_app_preview_mode.js' import { AppVersionsDiffQuery, AppVersionsDiffSchema, @@ -517,16 +507,6 @@ export class PartnersClient implements DeveloperPlatformClient { // This is a no-op for partners } - async updateDeveloperPreview( - input: DevelopmentStorePreviewUpdateInput, - ): Promise { - return this.request(DevelopmentStorePreviewUpdateQuery, input) - } - - async appPreviewMode(input: FindAppPreviewModeVariables): Promise { - return this.request(FindAppPreviewModeQuery, input) - } - async sendSampleWebhook( input: SendSampleWebhookVariables, _organizationId: string,