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/new-sails-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/theme': minor
---

Add --json flag to theme preview to configure a json output
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ export interface themepreview {
*/
'-e, --environment <value>'?: string

/**
* Output the preview URL and identifier as JSON.
* @environment SHOPIFY_FLAG_JSON
*/
'--json'?: ''

/**
* Disable color output.
* @environment SHOPIFY_FLAG_NO_COLOR
Expand Down
11 changes: 10 additions & 1 deletion docs-shopify.dev/generated/generated_docs_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -7186,6 +7186,15 @@
"name": "themepreview",
"description": "",
"members": [
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-preview.interface.ts",
"syntaxKind": "PropertySignature",
"name": "--json",
"value": "\"\"",
"description": "Output the preview URL and identifier as JSON.",
"isOptional": true,
"environmentValue": "SHOPIFY_FLAG_JSON"
},
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-preview.interface.ts",
"syntaxKind": "PropertySignature",
Expand Down Expand Up @@ -7275,7 +7284,7 @@
"environmentValue": "SHOPIFY_FLAG_THEME_ID"
}
],
"value": "export interface themepreview {\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment <value>'?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Automatically launch the theme preview in your default web browser.\n * @environment SHOPIFY_FLAG_OPEN\n */\n '--open'?: ''\n\n /**\n * Path to a JSON overrides file.\n * @environment SHOPIFY_FLAG_OVERRIDES\n */\n '--overrides <value>': string\n\n /**\n * Password generated from the Theme Access app or an Admin API token.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password <value>'?: string\n\n /**\n * The path where you want to run the command. Defaults to the current working directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path <value>'?: string\n\n /**\n * An existing preview identifier to update instead of creating a new preview.\n * @environment SHOPIFY_FLAG_PREVIEW_ID\n */\n '--preview-id <value>'?: string\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store <value>'?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme <value>': string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}"
"value": "export interface themepreview {\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment <value>'?: string\n\n /**\n * Output the preview URL and identifier as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '--json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Automatically launch the theme preview in your default web browser.\n * @environment SHOPIFY_FLAG_OPEN\n */\n '--open'?: ''\n\n /**\n * Path to a JSON overrides file.\n * @environment SHOPIFY_FLAG_OVERRIDES\n */\n '--overrides <value>': string\n\n /**\n * Password generated from the Theme Access app or an Admin API token.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password <value>'?: string\n\n /**\n * The path where you want to run the command. Defaults to the current working directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path <value>'?: string\n\n /**\n * An existing preview identifier to update instead of creating a new preview.\n * @environment SHOPIFY_FLAG_PREVIEW_ID\n */\n '--preview-id <value>'?: string\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store <value>'?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme <value>': string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}"
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2549,14 +2549,15 @@ Applies JSON overrides to a theme and returns a preview URL.

```
USAGE
$ shopify theme preview --overrides <value> -t <value> [-e <value>...] [--no-color] [--open] [--password <value>]
[--path <value>] [--preview-id <value>] [-s <value>] [--verbose]
$ shopify theme preview --overrides <value> -t <value> [-e <value>...] [--json] [--no-color] [--open] [--password
<value>] [--path <value>] [--preview-id <value>] [-s <value>] [--verbose]

FLAGS
-e, --environment=<value>... [env: SHOPIFY_FLAG_ENVIRONMENT] The environment to apply to the current command.
-s, --store=<value> [env: SHOPIFY_FLAG_STORE] Store URL. It can be the store prefix (example) or the full
myshopify.com URL (example.myshopify.com, https://example.myshopify.com).
-t, --theme=<value> (required) [env: SHOPIFY_FLAG_THEME_ID] Theme ID or name of the remote theme.
--json [env: SHOPIFY_FLAG_JSON] Output the preview URL and identifier as JSON.
--no-color [env: SHOPIFY_FLAG_NO_COLOR] Disable color output.
--open [env: SHOPIFY_FLAG_OPEN] Automatically launch the theme preview in your default web
browser.
Expand Down
7 changes: 7 additions & 0 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6953,6 +6953,13 @@
"name": "environment",
"type": "option"
},
"json": {
"allowNo": false,
"description": "Output the preview URL and identifier as JSON.",
"env": "SHOPIFY_FLAG_JSON",
"name": "json",
"type": "boolean"
},
"no-color": {
"allowNo": false,
"description": "Disable color output.",
Expand Down
14 changes: 14 additions & 0 deletions packages/theme/src/cli/commands/theme/preview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,18 @@ describe('Preview', () => {
}),
)
})

test('passes --json to devWithOverrideFile when provided', async () => {
const expectedTheme = buildTheme({id: 5, name: 'Expected Theme', role: 'unpublished'})!
vi.mocked(findOrSelectTheme).mockResolvedValue(expectedTheme)

await run(['--overrides=/path/to/overrides.json', `--theme=${expectedTheme.id}`, '--json'])

expect(devWithOverrideFile).toHaveBeenCalledWith(
expect.objectContaining({
themeId: expectedTheme.id.toString(),
json: true,
}),
)
})
})
6 changes: 6 additions & 0 deletions packages/theme/src/cli/commands/theme/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export default class Preview extends ThemeCommand {
env: 'SHOPIFY_FLAG_OPEN',
default: false,
}),
json: Flags.boolean({
description: 'Output the preview URL and identifier as JSON.',
env: 'SHOPIFY_FLAG_JSON',
default: false,
}),
}

static multiEnvironmentsFlags: RequiredFlags = null
Expand All @@ -55,6 +60,7 @@ export default class Preview extends ThemeCommand {
previewIdentifier: flags['preview-id'],
open: flags.open,
password: flags.password,
json: flags.json,
})
}
}
36 changes: 36 additions & 0 deletions packages/theme/src/cli/services/dev-override.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {fetchDevServerSession} from '../utilities/theme-environment/dev-server-s
import {createThemePreview, updateThemePreview} from '../utilities/theme-previews/preview.js'
import {describe, expect, test, vi} from 'vitest'
import {renderSuccess} from '@shopify/cli-kit/node/ui'
import {collectedLogs, clearCollectedLogs} from '@shopify/cli-kit/node/output'
import {fileExistsSync, readFile} from '@shopify/cli-kit/node/fs'

vi.mock('../utilities/theme-environment/dev-server-session.js')
Expand Down Expand Up @@ -84,6 +85,7 @@ describe('devWithOverrideFile', () => {
themeId: expectedThemeId,
previewIdentifier: expectedPreviewId,
open: false,
json: false,
})

// Then
Expand Down Expand Up @@ -120,6 +122,7 @@ describe('devWithOverrideFile', () => {
overrideJson: '/bad.json',
themeId: '123',
open: false,
json: false,
}).catch((err) => err)
expect(error.message).toBe('Failed to parse override file: /bad.json')
expect(error.tryMessage).toMatch(/not valid json/i)
Expand Down Expand Up @@ -166,10 +169,43 @@ describe('devWithOverrideFile', () => {
overrideJson: '/overrides.json',
themeId: '789',
open: false,
json: false,
password: 'shptka_abc123',
})

// Then
expect(fetchDevServerSession).toHaveBeenCalledWith('789', adminSession, 'shptka_abc123')
})

test('outputs JSON when json flag is true', async () => {
// Given
vi.mocked(fileExistsSync).mockReturnValue(true)
vi.mocked(readFile).mockResolvedValue(Buffer.from(JSON.stringify({templates: {}})))
vi.mocked(fetchDevServerSession).mockResolvedValue(mockSession)
vi.mocked(createThemePreview).mockResolvedValue({url: expectedPreviewUrl, preview_identifier: expectedPreviewId})
clearCollectedLogs()

// When
await devWithOverrideFile({adminSession, overrideJson: '/overrides.json', themeId: '789', open: false, json: true})

// Then
const expectedJson = JSON.stringify({url: expectedPreviewUrl, preview_identifier: expectedPreviewId})
expect(collectedLogs.info).toContainEqual(expectedJson)
expect(renderSuccess).not.toHaveBeenCalled()
})

test('renders success body by default when json flag is omitted', async () => {
// Given
vi.mocked(fileExistsSync).mockReturnValue(true)
vi.mocked(readFile).mockResolvedValue(Buffer.from(JSON.stringify({templates: {}})))
vi.mocked(fetchDevServerSession).mockResolvedValue(mockSession)
vi.mocked(createThemePreview).mockResolvedValue({url: expectedPreviewUrl, preview_identifier: expectedPreviewId})
clearCollectedLogs()

// When
await devWithOverrideFile({adminSession, overrideJson: '/overrides.json', themeId: '789', open: false})

// Then
expect(renderSuccess).toHaveBeenCalled()
})
})
24 changes: 15 additions & 9 deletions packages/theme/src/cli/services/dev-override.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {openURLSafely} from './dev.js'
import {fetchDevServerSession} from '../utilities/theme-environment/dev-server-session.js'
import {createThemePreview, updateThemePreview} from '../utilities/theme-previews/preview.js'
import {renderSuccess} from '@shopify/cli-kit/node/ui'
import {outputInfo} from '@shopify/cli-kit/node/output'
import {AdminSession} from '@shopify/cli-kit/node/session'
import {AbortError} from '@shopify/cli-kit/node/error'
import {readFile, fileExistsSync} from '@shopify/cli-kit/node/fs'
Expand All @@ -17,6 +18,7 @@ interface DevWithOverrideFileOptions {
previewIdentifier?: string
open: boolean
password?: string
json?: boolean
}

/**
Expand Down Expand Up @@ -53,16 +55,20 @@ export async function devWithOverrideFile(options: DevWithOverrideFileOptions) {
themeId: options.themeId,
})

renderSuccess({
body: [
{
list: {
title: options.previewIdentifier ? 'Preview updated' : 'Preview is ready',
items: [{link: {url: preview.url}}, `Preview ID: ${preview.preview_identifier}`],
if (options.json) {
outputInfo(JSON.stringify({url: preview.url, preview_identifier: preview.preview_identifier}))
} else {
renderSuccess({
body: [
{
list: {
title: options.previewIdentifier ? 'Preview updated' : 'Preview is ready',
items: [{link: {url: preview.url}}, `Preview ID: ${preview.preview_identifier}`],
},
},
},
],
})
],
})
}

if (options.open) {
openURLSafely(preview.url, 'theme preview')
Expand Down
Loading