Skip to content
Open
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
7 changes: 7 additions & 0 deletions .changeset/polite-eyes-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@shopify/cli-kit': patch
'@shopify/theme': patch
'@shopify/cli': patch
---

Add --listing flag to theme dev, push, and share commands
6 changes: 6 additions & 0 deletions docs-shopify.dev/commands/interfaces/theme-dev.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ export interface themedev {
*/
'-x, --ignore <value>'?: string

/**
* The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.
* @environment SHOPIFY_FLAG_LISTING
*/
'--listing <value>'?: string

/**
* The live reload mode switches the server behavior when a file is modified:
- hot-reload Hot reloads local changes to CSS and sections (default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export interface themepush {
*/
'-j, --json'?: ''

/**
* The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.
* @environment SHOPIFY_FLAG_LISTING
*/
'--listing <value>'?: string

/**
* Push theme files from your remote live theme.
* @environment SHOPIFY_FLAG_LIVE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ export interface themeshare {
*/
'-e, --environment <value>'?: string

/**
* The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.
* @environment SHOPIFY_FLAG_LISTING
*/
'--listing <value>'?: string

/**
* Disable color output.
* @environment SHOPIFY_FLAG_NO_COLOR
Expand Down
33 changes: 30 additions & 3 deletions docs-shopify.dev/generated/generated_docs_data.json

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion packages/cli-kit/src/public/common/string.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {takeRandomFromArray} from './array.js'
import {unstyled} from '../../public/node/output.js'
import {Token, TokenItem} from '../../private/node/ui/components/TokenizedText.js'
import {camelCase, constantCase, paramCase, snakeCase, pascalCase} from 'change-case'
import {camelCase, capitalCase, constantCase, paramCase, snakeCase, pascalCase} from 'change-case'

const SAFE_RANDOM_BUSINESS_ADJECTIVES = [
'commercial',
Expand Down Expand Up @@ -307,6 +307,16 @@ export function camelize(input: string): string {
return camelCase(input)
}

/**
* Transform a string to capitalCase.
*
* @param input - String to transform.
* @returns The transformed string.
*/
export function capitalizeWords(input: string): string {
return capitalCase(input)
}

/**
* Transform a string to param-case.
*
Expand Down
3 changes: 2 additions & 1 deletion packages/cli-kit/src/public/node/themes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ export type ThemeFSEventPayload<T extends ThemeFSEventName = 'add'> = (ThemeFSEv

export interface ThemeFileSystemOptions {
filters?: {ignore?: string[]; only?: string[]}
notify?: string
listing?: string
noDelete?: boolean
notify?: string
}

/**
Expand Down
15 changes: 11 additions & 4 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1908,8 +1908,8 @@ Uploads the current theme as a development theme to the connected store, then pr
```
USAGE
$ shopify theme dev [-a] [-e <value>...] [--error-overlay silent|default] [--host <value>] [-x <value>...]
[--live-reload hot-reload|full-page|off] [--no-color] [-n] [--notify <value>] [-o <value>...] [--open] [--password
<value>] [--path <value>] [--port <value>] [-s <value>] [--store-password <value>] [-t <value>]
[--listing <value>] [--live-reload hot-reload|full-page|off] [--no-color] [-n] [--notify <value>] [-o <value>...]
[--open] [--password <value>] [--path <value>] [--port <value>] [-s <value>] [--store-password <value>] [-t <value>]
[--theme-editor-sync] [--verbose]

FLAGS
Expand Down Expand Up @@ -1946,6 +1946,9 @@ FLAGS
--host=<value>
Set which network interface the web server listens on. The default value is 127.0.0.1.

--listing=<value>
The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.

--live-reload=<option>
[default: hot-reload] The live reload mode switches the server behavior when a file is modified:
- hot-reload Hot reloads local changes to CSS and sections (default)
Expand Down Expand Up @@ -2395,6 +2398,8 @@ FLAGS
-u, --unpublished Create a new unpublished theme and push to it.
-x, --ignore=<value>... Skip uploading the specified files (Multiple flags allowed). Wrap the value in double
quotes if you're using wildcards.
--listing=<value> The listing preset to use for multi-preset themes. Applies preset files from
listings/[preset-name] directory.
--no-color Disable color output.
--password=<value> Password generated from the Theme Access app or an Admin API token.
--path=<value> The path where you want to run the command. Defaults to the current working directory.
Expand Down Expand Up @@ -2475,13 +2480,15 @@ Creates a shareable, unpublished, and new theme on your theme library with a ran

```
USAGE
$ shopify theme share [-e <value>...] [--no-color] [--password <value>] [--path <value>] [-s <value>]
[--verbose]
$ shopify theme share [-e <value>...] [--listing <value>] [--no-color] [--password <value>] [--path <value>]
[-s <value>] [--verbose]

FLAGS
-e, --environment=<value>... The environment to apply to the current command.
-s, --store=<value> Store URL. It can be the store prefix (example) or the full myshopify.com URL
(example.myshopify.com, https://example.myshopify.com).
--listing=<value> The listing preset to use for multi-preset themes. Applies preset files from
listings/[preset-name] directory.
--no-color Disable color output.
--password=<value> Password generated from the Theme Access app or an Admin API token.
--path=<value> The path where you want to run the command. Defaults to the current working directory.
Expand Down
32 changes: 32 additions & 0 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5913,6 +5913,14 @@
"name": "ignore",
"type": "option"
},
"listing": {
"description": "The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.",
"env": "SHOPIFY_FLAG_LISTING",
"hasDynamicHelp": false,
"multiple": false,
"name": "listing",
"type": "option"
},
"live-reload": {
"default": "hot-reload",
"description": "The live reload mode switches the server behavior when a file is modified:\n- hot-reload Hot reloads local changes to CSS and sections (default)\n- full-page Always refreshes the entire page\n- off Deactivate live reload",
Expand Down Expand Up @@ -7106,6 +7114,14 @@
"name": "json",
"type": "boolean"
},
"listing": {
"description": "The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.",
"env": "SHOPIFY_FLAG_LISTING",
"hasDynamicHelp": false,
"multiple": false,
"name": "listing",
"type": "option"
},
"live": {
"allowNo": false,
"char": "l",
Expand Down Expand Up @@ -7411,6 +7427,14 @@
"name": "ignore",
"type": "option"
},
"listing": {
"description": "The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.",
"env": "SHOPIFY_FLAG_LISTING",
"hasDynamicHelp": false,
"multiple": false,
"name": "listing",
"type": "option"
},
"live-reload": {
"default": "hot-reload",
"description": "The live reload mode switches the server behavior when a file is modified:\n- hot-reload Hot reloads local changes to CSS and sections (default)\n- full-page Always refreshes the entire page\n- off Deactivate live reload",
Expand Down Expand Up @@ -7578,6 +7602,14 @@
"name": "force",
"type": "boolean"
},
"listing": {
"description": "The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.",
"env": "SHOPIFY_FLAG_LISTING",
"hasDynamicHelp": false,
"multiple": false,
"name": "listing",
"type": "option"
},
"no-color": {
"allowNo": false,
"description": "Disable color output.",
Expand Down
6 changes: 6 additions & 0 deletions packages/theme/src/cli/commands/theme/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ You can run this command only in a directory that matches the [default Shopify t
description: 'Theme ID or name of the remote theme.',
env: 'SHOPIFY_FLAG_THEME_ID',
}),
listing: Flags.string({
description:
'The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.',
env: 'SHOPIFY_FLAG_LISTING',
}),
nodelete: Flags.boolean({
char: 'n',
description:
Expand Down Expand Up @@ -165,6 +170,7 @@ You can run this command only in a directory that matches the [default Shopify t
password: flags.password,
storePassword: flags['store-password'],
theme,
listing: flags.listing,
host: flags.host,
port: flags.port,
'live-reload': flags['live-reload'] as LiveReload,
Expand Down
5 changes: 5 additions & 0 deletions packages/theme/src/cli/commands/theme/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ export default class Push extends ThemeCommand {
description: 'Require theme check to pass without errors before pushing. Warnings are allowed.',
env: 'SHOPIFY_FLAG_STRICT_PUSH',
}),
listing: Flags.string({
description:
'The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.',
env: 'SHOPIFY_FLAG_LISTING',
}),
environment: themeFlags.environment,
}

Expand Down
6 changes: 6 additions & 0 deletions packages/theme/src/cli/commands/theme/share.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export default class Share extends ThemeCommand {
description: 'Proceed without confirmation, if current directory does not seem to be theme directory.',
env: 'SHOPIFY_FLAG_FORCE',
}),
listing: Flags.string({
description:
'The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.',
env: 'SHOPIFY_FLAG_LISTING',
}),
}

static multiEnvironmentsFlags = ['store', 'password', 'path']
Expand All @@ -49,6 +54,7 @@ export default class Share extends ThemeCommand {
store: flags.store,
theme: getRandomName('creative'),
unpublished: true,
listing: flags.listing,
}

recordTiming('theme-command:share')
Expand Down
9 changes: 8 additions & 1 deletion packages/theme/src/cli/services/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {isStorefrontPasswordProtected} from '../utilities/theme-environment/stor
import {ensureValidPassword} from '../utilities/theme-environment/storefront-password-prompt.js'
import {emptyThemeExtFileSystem} from '../utilities/theme-fs-empty.js'
import {initializeDevServerSession} from '../utilities/theme-environment/dev-server-session.js'
import {ensureListingExists} from '../utilities/theme-listing.js'
import {renderSuccess, renderWarning} from '@shopify/cli-kit/node/ui'
import {AdminSession} from '@shopify/cli-kit/node/session'
import {Theme} from '@shopify/cli-kit/node/themes/types'
Expand Down Expand Up @@ -37,6 +38,7 @@ interface DevOptions {
ignore: string[]
only: string[]
notify?: string
listing?: string
}

export async function dev(options: DevOptions) {
Expand Down Expand Up @@ -66,15 +68,20 @@ export async function dev(options: DevOptions) {
})
}

if (options.listing) {
await ensureListingExists(options.directory, options.listing)
}

const storefrontPasswordPromise = await isStorefrontPasswordProtected(options.adminSession).then((needsPassword) =>
needsPassword ? ensureValidPassword(options.storePassword, options.adminSession.storeFqdn) : undefined,
)

const localThemeExtensionFileSystem = emptyThemeExtFileSystem()
const localThemeFileSystem = mountThemeFileSystem(options.directory, {
filters: options,
notify: options.notify,
listing: options.listing,
noDelete: options.noDelete,
notify: options.notify,
})

const host = options.host ?? DEFAULT_HOST
Expand Down
22 changes: 21 additions & 1 deletion packages/theme/src/cli/services/push.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import {uploadTheme} from '../utilities/theme-uploader.js'
import {ensureThemeStore} from '../utilities/theme-store.js'
import {findOrSelectTheme} from '../utilities/theme-selector.js'
import {runThemeCheck} from '../commands/theme/check.js'
import {mountThemeFileSystem} from '../utilities/theme-fs.js'
import {buildTheme} from '@shopify/cli-kit/node/themes/factories'
import {test, describe, vi, expect, beforeEach} from 'vitest'
import {themeCreate, fetchTheme, themePublish} from '@shopify/cli-kit/node/themes/api'
import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
import {
DEVELOPMENT_THEME_ROLE,
LIVE_THEME_ROLE,
promptThemeName,
UNPUBLISHED_THEME_ROLE,
promptThemeName,
} from '@shopify/cli-kit/node/themes/utils'
import {renderConfirmationPrompt, renderError} from '@shopify/cli-kit/node/ui'
import {AbortError} from '@shopify/cli-kit/node/error'
Expand All @@ -24,12 +25,14 @@ vi.mock('../utilities/theme-uploader.js')
vi.mock('../utilities/theme-store.js')
vi.mock('../utilities/theme-selector.js')
vi.mock('./local-storage.js')
vi.mock('../utilities/theme-listing.js')
vi.mock('@shopify/cli-kit/node/themes/utils')
vi.mock('@shopify/cli-kit/node/session')
vi.mock('@shopify/cli-kit/node/themes/api')
vi.mock('@shopify/cli-kit/node/ui')
vi.mock('../commands/theme/check.js')
vi.mock('@shopify/cli-kit/node/output')
vi.mock('../utilities/theme-fs.js')

const path = '/my-theme'
const defaultFlags: PullFlags = {
Expand Down Expand Up @@ -240,6 +243,23 @@ describe('push', () => {
expect(runThemeCheck).toHaveBeenCalledWith(path, 'json')
})
})

test('passes listing flag to file system when provided', async () => {
// Given
const theme = buildTheme({id: 1, name: 'Theme', role: 'development'})!
vi.mocked(findOrSelectTheme).mockResolvedValue(theme)

// When
await push({...defaultFlags, listing: 'my-preset'})

// Then
expect(mountThemeFileSystem).toHaveBeenCalledWith(
path,
expect.objectContaining({
listing: 'my-preset',
}),
)
})
})

describe('createOrSelectTheme', async () => {
Expand Down
15 changes: 14 additions & 1 deletion packages/theme/src/cli/services/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {Role} from '../utilities/theme-selector/fetch.js'
import {configureCLIEnvironment} from '../utilities/cli-config.js'
import {runThemeCheck} from '../commands/theme/check.js'
import {ensureThemeStore} from '../utilities/theme-store.js'
import {ensureListingExists} from '../utilities/theme-listing.js'
import {AdminSession, ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
import {themeCreate, fetchChecksums, themePublish} from '@shopify/cli-kit/node/themes/api'
import {Result, Theme} from '@shopify/cli-kit/node/themes/types'
Expand Down Expand Up @@ -38,6 +39,7 @@ interface PushOptions {
allowLive?: boolean
environment?: string
multiEnvironment?: boolean
listing?: string
}

interface JsonOutput {
Expand Down Expand Up @@ -108,6 +110,9 @@ export interface PushFlags {

/** The environment to push the theme to. */
environment?: string[]

/** The listing preset to use for multi-preset themes. */
listing?: string
}

/**
Expand Down Expand Up @@ -161,6 +166,10 @@ export async function push(
return
}

if (flags.listing) {
await ensureListingExists(workingDirectory, flags.listing)
}

const selectedTheme: Theme | undefined = await createOrSelectTheme(session, flags, multiEnvironment)
if (!selectedTheme) {
return
Expand All @@ -182,6 +191,7 @@ export async function push(
only: flags.only ?? [],
path: workingDirectory,
publish: flags.publish ?? false,
listing: flags.listing,
},
context,
)
Expand All @@ -202,7 +212,10 @@ async function executePush(
) {
recordTiming('theme-service:push:file-system')
const themeChecksums = await fetchChecksums(theme.id, session)
const themeFileSystem = mountThemeFileSystem(options.path, {filters: options})
const themeFileSystem = mountThemeFileSystem(options.path, {
filters: options,
listing: options.listing,
})
recordTiming('theme-service:push:file-system')

const {uploadResults, renderThemeSyncProgress} = uploadTheme(
Expand Down
Loading
Loading