Skip to content
Draft
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: 5 additions & 0 deletions .changeset/unsafe-app-dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/app': patch
---

Add an `--unsafe` flag to `shopify app dev`.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type DevSessionCreateMutationVariables = Types.Exact<{
appId: Types.Scalars['String']['input']
assetsUrl: Types.Scalars['String']['input']
websocketUrl?: Types.InputMaybe<Types.Scalars['String']['input']>
unsafe?: Types.InputMaybe<Types.Scalars['Boolean']['input']>
}>

export type DevSessionCreateMutation = {
Expand Down Expand Up @@ -46,6 +47,11 @@ export const DevSessionCreate = {
variable: {kind: 'Variable', name: {kind: 'Name', value: 'websocketUrl'}},
type: {kind: 'NamedType', name: {kind: 'Name', value: 'String'}},
},
{
kind: 'VariableDefinition',
variable: {kind: 'Variable', name: {kind: 'Name', value: 'unsafe'}},
type: {kind: 'NamedType', name: {kind: 'Name', value: 'Boolean'}},
},
],
selectionSet: {
kind: 'SelectionSet',
Expand All @@ -69,6 +75,11 @@ export const DevSessionCreate = {
name: {kind: 'Name', value: 'websocketUrl'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'websocketUrl'}},
},
{
kind: 'Argument',
name: {kind: 'Name', value: 'unsafe'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'unsafe'}},
},
],
selectionSet: {
kind: 'SelectionSet',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type DevSessionUpdateMutationVariables = Types.Exact<{
assetsUrl?: Types.InputMaybe<Types.Scalars['String']['input']>
manifest?: Types.InputMaybe<Types.Scalars['JSON']['input']>
inheritedModuleUids: Types.Scalars['String']['input'][] | Types.Scalars['String']['input']
unsafe?: Types.InputMaybe<Types.Scalars['Boolean']['input']>
}>

export type DevSessionUpdateMutation = {
Expand All @@ -19,6 +20,7 @@ export type DevSessionUpdateMutation = {
user?: {id: string; email?: string | null} | null
app: {id: string; key: string}
} | null
warnings?: {message: string; code: Types.DevSessionWarningCode}[] | null
userErrors: {message: string; on: JsonMapType; field?: string[] | null; category: string}[]
} | null
}
Expand Down Expand Up @@ -57,6 +59,11 @@ export const DevSessionUpdate = {
},
},
},
{
kind: 'VariableDefinition',
variable: {kind: 'Variable', name: {kind: 'Name', value: 'unsafe'}},
type: {kind: 'NamedType', name: {kind: 'Name', value: 'Boolean'}},
},
],
selectionSet: {
kind: 'SelectionSet',
Expand Down Expand Up @@ -85,6 +92,11 @@ export const DevSessionUpdate = {
name: {kind: 'Name', value: 'inheritedModuleUids'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'inheritedModuleUids'}},
},
{
kind: 'Argument',
name: {kind: 'Name', value: 'unsafe'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'unsafe'}},
},
],
selectionSet: {
kind: 'SelectionSet',
Expand Down Expand Up @@ -125,6 +137,18 @@ export const DevSessionUpdate = {
],
},
},
{
kind: 'Field',
name: {kind: 'Name', value: 'warnings'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'message'}},
{kind: 'Field', name: {kind: 'Name', value: 'code'}},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
{
kind: 'Field',
name: {kind: 'Name', value: 'userErrors'},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mutation DevSessionCreate($appId: String!, $assetsUrl: String!, $websocketUrl: String) {
devSessionCreate(appId: $appId, assetsUrl: $assetsUrl, websocketUrl: $websocketUrl) {
mutation DevSessionCreate($appId: String!, $assetsUrl: String!, $websocketUrl: String, $unsafe: Boolean) {
devSessionCreate(appId: $appId, assetsUrl: $assetsUrl, websocketUrl: $websocketUrl, unsafe: $unsafe) {
devSession {
websocketUrl
updatedAt
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mutation DevSessionUpdate($appId: String!, $assetsUrl: String, $manifest: JSON, $inheritedModuleUids: [String!]!) {
devSessionUpdate(appId: $appId, assetsUrl: $assetsUrl, manifest: $manifest, inheritedModuleUids: $inheritedModuleUids) {
mutation DevSessionUpdate($appId: String!, $assetsUrl: String, $manifest: JSON, $inheritedModuleUids: [String!]!, $unsafe: Boolean) {
devSessionUpdate(appId: $appId, assetsUrl: $assetsUrl, manifest: $manifest, inheritedModuleUids: $inheritedModuleUids, unsafe: $unsafe) {
devSession {
websocketUrl
updatedAt
Expand All @@ -12,6 +12,10 @@ mutation DevSessionUpdate($appId: String!, $assetsUrl: String, $manifest: JSON,
key
}
}
warnings {
message
code
}
userErrors {
message
on
Expand Down
26 changes: 25 additions & 1 deletion packages/app/src/cli/commands/app/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,31 @@ describe('app dev command', () => {
tunnelUrl: undefined,
localhostPort: undefined,
})
expect(dev).toHaveBeenCalledWith(expect.objectContaining({installMkcert: false, tunnel: {mode: 'auto'}}))
expect(dev).toHaveBeenCalledWith(
expect.objectContaining({installMkcert: false, unsafe: false, tunnel: {mode: 'auto'}}),
)
})
})

test('passes --unsafe to dev', async () => {
await inTemporaryDirectory(async (tmp) => {
const app = testAppLinked({directory: tmp})
vi.mocked(linkedAppContext).mockResolvedValue({
app,
remoteApp: testOrganizationApp(),
organization: testOrganization(),
project: testProject(),
activeConfig: {} as never,
specifications: [],
developerPlatformClient: testDeveloperPlatformClient(),
} as Awaited<ReturnType<typeof linkedAppContext>>)
const store = testOrganizationStore({shopDomain: 'dev-store.myshopify.com'})
vi.mocked(storeContext).mockResolvedValue(store)
vi.mocked(getTunnelMode).mockResolvedValue({mode: 'auto'})

await Dev.run(['--path', tmp, '--store', store.shopDomain, '--unsafe'], import.meta.url)

expect(dev).toHaveBeenCalledWith(expect.objectContaining({unsafe: true}))
})
})

Expand Down
6 changes: 6 additions & 0 deletions packages/app/src/cli/commands/app/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export default class Dev extends AppLinkedCommand {
env: 'SHOPIFY_FLAG_NO_UPDATE',
default: false,
}),
unsafe: Flags.boolean({
description: 'Skip validations that are unsafe for production, such as the Remote DOM bundle size limit.',
env: 'SHOPIFY_FLAG_UNSAFE',
default: false,
}),
'subscription-product-url': Flags.string({
description: 'Resource URL for subscription UI extension. Format: "/products/{productId}"',
env: 'SHOPIFY_FLAG_SUBSCRIPTION_PRODUCT_URL',
Expand Down Expand Up @@ -137,6 +142,7 @@ export default class Dev extends AppLinkedCommand {
store,
directory: flags.path,
update: !flags['no-update'],
unsafe: flags.unsafe,
skipDependenciesInstallation: flags['skip-dependencies-installation'],
commandConfig: this.config,
subscriptionProductUrl: flags['subscription-product-url'],
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/cli/services/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface DevOptions {
store: OrganizationStore
directory: string
update: boolean
unsafe?: boolean
commandConfig: Config
skipDependenciesInstallation: boolean
subscriptionProductUrl?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ describe('pushUpdatesForDevSession', () => {
},
// The unaffected extension is listed in inheritedModuleUids
inheritedModuleUids: [unaffectedExtension.uid],
unsafe: false,
})
})

Expand Down Expand Up @@ -530,6 +531,7 @@ describe('pushUpdatesForDevSession', () => {
assetsUrl: 'https://gcs.url',
manifest: expect.any(Object),
inheritedModuleUids: [],
unsafe: false,
})
})

Expand All @@ -549,6 +551,7 @@ describe('pushUpdatesForDevSession', () => {
appId: 'app123',
assetsUrl: 'https://gcs.url',
websocketUrl: 'wss://test.dev/extensions',
unsafe: false,
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface DevSessionProcessOptions {
appWatcher: AppEventWatcher
appPreviewURL: string
devSessionStatusManager: DevSessionStatusManager
unsafe?: boolean
}

export interface DevSessionProcess extends BaseProcess<DevSessionProcessOptions> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ export class DevSession {
assetsUrl: signedURL,
manifest,
inheritedModuleUids,
unsafe: this.options.unsafe ?? false,
}
return this.devSessionUpdateWithRetry(payload)
} else {
Expand All @@ -317,6 +318,7 @@ export class DevSession {
appId: this.options.appId,
assetsUrl: signedURL,
websocketUrl,
unsafe: this.options.unsafe ?? false,
}
return this.devSessionCreateWithRetry(payload)
}
Expand Down Expand Up @@ -430,6 +432,7 @@ export class DevSession {
private async devSessionUpdateWithRetry(payload: DevSessionUpdateOptions): Promise<DevSessionResult> {
const result = await this.options.developerPlatformClient.devSessionUpdate(payload)
const errors = result.devSessionUpdate?.userErrors ?? []
const warnings = result.devSessionUpdate?.warnings ?? []
const devSession = result.devSessionUpdate?.devSession

// Check for session takeover
Expand Down Expand Up @@ -460,6 +463,15 @@ export class DevSession {
}
}

if (warnings.length > 0) {
await Promise.all(
warnings.map((warning) => {
const message = warning.code === 'SESSION_TAKEOVER' ? `⚠️ ${warning.message}` : warning.message
return this.logger.warning(message)
}),
)
}

if (errors.length) return {status: 'remote-error', error: errors}
return {status: 'updated'}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export async function setupDevProcesses({
appWatcher,
appPreviewURL: appPreviewUrl,
devSessionStatusManager,
unsafe: commandOptions.unsafe,
}),
await setupPreviewThemeAppExtensionsProcess({
remoteApp,
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/cli/utilities/developer-platform-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,14 @@ interface DevSessionSharedOptions {
export interface DevSessionCreateOptions extends DevSessionSharedOptions {
assetsUrl?: string
websocketUrl?: string
unsafe?: boolean
}

export interface DevSessionUpdateOptions extends DevSessionSharedOptions {
assetsUrl?: string
manifest: AppManifest
inheritedModuleUids: string[]
unsafe?: boolean
}

export type DevSessionDeleteOptions = DevSessionSharedOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -989,12 +989,13 @@ export class AppManagementClient implements DeveloperPlatformClient {
assetsUrl,
shopFqdn,
websocketUrl,
unsafe,
}: DevSessionCreateOptions): Promise<DevSessionCreateMutation> {
const appIdNumber = String(numberFromGid(appId))
return this.appDevRequest({
query: DevSessionCreate,
shopFqdn,
variables: {appId: appIdNumber, assetsUrl: assetsUrl ?? '', websocketUrl},
variables: {appId: appIdNumber, assetsUrl: assetsUrl ?? '', websocketUrl, unsafe},
requestOptions: {requestMode: 'slow-request'},
})
}
Expand All @@ -1005,13 +1006,15 @@ export class AppManagementClient implements DeveloperPlatformClient {
shopFqdn,
manifest,
inheritedModuleUids,
unsafe,
}: DevSessionUpdateOptions): Promise<DevSessionUpdateMutation> {
const appIdNumber = String(numberFromGid(appId))
const variables: DevSessionUpdateMutationVariables = {
appId: appIdNumber,
assetsUrl,
manifest: JSON.stringify(manifest),
inheritedModuleUids,
unsafe,
}
return this.appDevRequest({query: DevSessionUpdate, shopFqdn, variables})
}
Expand Down
Loading