diff --git a/docs-shopify.dev/commands/interfaces/theme-dev.interface.ts b/docs-shopify.dev/commands/interfaces/theme-dev.interface.ts index 311f0b9d57..4f0dd213d0 100644 --- a/docs-shopify.dev/commands/interfaces/theme-dev.interface.ts +++ b/docs-shopify.dev/commands/interfaces/theme-dev.interface.ts @@ -90,6 +90,12 @@ export interface themedev { */ '--path '?: string + /** + * Use polling to detect file changes. Use this when file system events are unreliable, such as with build tools that preserve timestamps, Docker volumes, or network filesystems. + * @environment SHOPIFY_FLAG_POLL + */ + '--poll'?: '' + /** * Local port to serve theme preview from. * @environment SHOPIFY_FLAG_PORT diff --git a/docs-shopify.dev/generated/generated_docs_data.json b/docs-shopify.dev/generated/generated_docs_data.json index 8d43ba63af..468e4b0549 100644 --- a/docs-shopify.dev/generated/generated_docs_data.json +++ b/docs-shopify.dev/generated/generated_docs_data.json @@ -6259,6 +6259,15 @@ "isOptional": true, "environmentValue": "SHOPIFY_FLAG_PATH" }, + { + "filePath": "docs-shopify.dev/commands/interfaces/theme-dev.interface.ts", + "syntaxKind": "PropertySignature", + "name": "--poll", + "value": "\"\"", + "description": "Use polling to detect file changes. Use this when file system events are unreliable, such as with build tools that preserve timestamps, Docker volumes, or network filesystems.", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_POLL" + }, { "filePath": "docs-shopify.dev/commands/interfaces/theme-dev.interface.ts", "syntaxKind": "PropertySignature", @@ -6359,7 +6368,7 @@ "environmentValue": "SHOPIFY_FLAG_IGNORE" } ], - "value": "export interface themedev {\n /**\n * Allow development on a live theme.\n * @environment SHOPIFY_FLAG_ALLOW_LIVE\n */\n '-a, --allow-live'?: ''\n\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment '?: string\n\n /**\n * Controls the visibility of the error overlay when an theme asset upload fails:\n- silent Prevents the error overlay from appearing.\n- default Displays the error overlay.\n \n * @environment SHOPIFY_FLAG_ERROR_OVERLAY\n */\n '--error-overlay '?: string\n\n /**\n * Set which network interface the web server listens on. The default value is 127.0.0.1.\n * @environment SHOPIFY_FLAG_HOST\n */\n '--host '?: string\n\n /**\n * Skip hot reloading any files that match the specified pattern.\n * @environment SHOPIFY_FLAG_IGNORE\n */\n '-x, --ignore '?: string\n\n /**\n * The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.\n * @environment SHOPIFY_FLAG_LISTING\n */\n '--listing '?: string\n\n /**\n * 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\n * @environment SHOPIFY_FLAG_LIVE_RELOAD\n */\n '--live-reload '?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Prevents files from being deleted in the remote theme when a file has been deleted locally. This applies to files that are deleted while the command is running, and files that have been deleted locally before the command is run.\n * @environment SHOPIFY_FLAG_NODELETE\n */\n '-n, --nodelete'?: ''\n\n /**\n * The file path or URL. The file path is to a file that you want updated on idle. The URL path is where you want a webhook posted to report on file changes.\n * @environment SHOPIFY_FLAG_NOTIFY\n */\n '--notify '?: string\n\n /**\n * Hot reload only files that match the specified pattern.\n * @environment SHOPIFY_FLAG_ONLY\n */\n '-o, --only '?: string\n\n /**\n * Automatically launch the theme preview in your default web browser.\n * @environment SHOPIFY_FLAG_OPEN\n */\n '--open'?: ''\n\n /**\n * Password generated from the Theme Access app or an Admin API token.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password '?: 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 '?: string\n\n /**\n * Local port to serve theme preview from.\n * @environment SHOPIFY_FLAG_PORT\n */\n '--port '?: 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 '?: string\n\n /**\n * The password for storefronts with password protection.\n * @environment SHOPIFY_FLAG_STORE_PASSWORD\n */\n '--store-password '?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme '?: string\n\n /**\n * Synchronize Theme Editor updates in the local theme files.\n * @environment SHOPIFY_FLAG_THEME_EDITOR_SYNC\n */\n '--theme-editor-sync'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" + "value": "export interface themedev {\n /**\n * Allow development on a live theme.\n * @environment SHOPIFY_FLAG_ALLOW_LIVE\n */\n '-a, --allow-live'?: ''\n\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment '?: string\n\n /**\n * Controls the visibility of the error overlay when an theme asset upload fails:\n- silent Prevents the error overlay from appearing.\n- default Displays the error overlay.\n \n * @environment SHOPIFY_FLAG_ERROR_OVERLAY\n */\n '--error-overlay '?: string\n\n /**\n * Set which network interface the web server listens on. The default value is 127.0.0.1.\n * @environment SHOPIFY_FLAG_HOST\n */\n '--host '?: string\n\n /**\n * Skip hot reloading any files that match the specified pattern.\n * @environment SHOPIFY_FLAG_IGNORE\n */\n '-x, --ignore '?: string\n\n /**\n * The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.\n * @environment SHOPIFY_FLAG_LISTING\n */\n '--listing '?: string\n\n /**\n * 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\n * @environment SHOPIFY_FLAG_LIVE_RELOAD\n */\n '--live-reload '?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Prevents files from being deleted in the remote theme when a file has been deleted locally. This applies to files that are deleted while the command is running, and files that have been deleted locally before the command is run.\n * @environment SHOPIFY_FLAG_NODELETE\n */\n '-n, --nodelete'?: ''\n\n /**\n * The file path or URL. The file path is to a file that you want updated on idle. The URL path is where you want a webhook posted to report on file changes.\n * @environment SHOPIFY_FLAG_NOTIFY\n */\n '--notify '?: string\n\n /**\n * Hot reload only files that match the specified pattern.\n * @environment SHOPIFY_FLAG_ONLY\n */\n '-o, --only '?: string\n\n /**\n * Automatically launch the theme preview in your default web browser.\n * @environment SHOPIFY_FLAG_OPEN\n */\n '--open'?: ''\n\n /**\n * Password generated from the Theme Access app or an Admin API token.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password '?: 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 '?: string\n\n /**\n * Use polling to detect file changes. Use this when file system events are unreliable, such as with build tools that preserve timestamps, Docker volumes, or network filesystems.\n * @environment SHOPIFY_FLAG_POLL\n */\n '--poll'?: ''\n\n /**\n * Local port to serve theme preview from.\n * @environment SHOPIFY_FLAG_PORT\n */\n '--port '?: 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 '?: string\n\n /**\n * The password for storefronts with password protection.\n * @environment SHOPIFY_FLAG_STORE_PASSWORD\n */\n '--store-password '?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme '?: string\n\n /**\n * Synchronize Theme Editor updates in the local theme files.\n * @environment SHOPIFY_FLAG_THEME_EDITOR_SYNC\n */\n '--theme-editor-sync'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" } } } diff --git a/packages/cli-kit/src/public/node/themes/types.ts b/packages/cli-kit/src/public/node/themes/types.ts index 55f4a2ffef..a878c88467 100644 --- a/packages/cli-kit/src/public/node/themes/types.ts +++ b/packages/cli-kit/src/public/node/themes/types.ts @@ -30,6 +30,7 @@ export interface ThemeFileSystemOptions { listing?: string noDelete?: boolean notify?: string + poll?: boolean } /** diff --git a/packages/cli/README.md b/packages/cli/README.md index def3a91294..bfa6688304 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -2163,8 +2163,8 @@ Uploads the current theme as a development theme to the connected store, then pr USAGE $ shopify theme dev [-a] [-e ...] [--error-overlay silent|default] [--host ] [-x ...] [--listing ] [--live-reload hot-reload|full-page|off] [--no-color] [-n] [--notify ] [-o ...] - [--open] [--password ] [--path ] [--port ] [-s ] [--store-password ] [-t ] - [--theme-editor-sync] [--verbose] + [--open] [--password ] [--path ] [--poll] [--port ] [-s ] [--store-password ] [-t + ] [--theme-editor-sync] [--verbose] FLAGS -a, --allow-live @@ -2230,6 +2230,10 @@ FLAGS --path= [env: SHOPIFY_FLAG_PATH] The path where you want to run the command. Defaults to the current working directory. + --poll + [env: SHOPIFY_FLAG_POLL] Use polling to detect file changes. Use this when file system events are unreliable, such + as with build tools that preserve timestamps, Docker volumes, or network filesystems. + --port= [env: SHOPIFY_FLAG_PORT] Local port to serve theme preview from. diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 329f37fb02..07de9f6411 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -6218,9 +6218,8 @@ }, "poll": { "allowNo": false, - "description": "Force polling to detect file changes.", + "description": "Use polling to detect file changes. Use this when file system events are unreliable, such as with build tools that preserve timestamps, Docker volumes, or network filesystems.", "env": "SHOPIFY_FLAG_POLL", - "hidden": true, "name": "poll", "type": "boolean" }, @@ -7856,9 +7855,8 @@ }, "poll": { "allowNo": false, - "description": "Force polling to detect file changes.", + "description": "Use polling to detect file changes. Use this when file system events are unreliable, such as with build tools that preserve timestamps, Docker volumes, or network filesystems.", "env": "SHOPIFY_FLAG_POLL", - "hidden": true, "name": "poll", "type": "boolean" }, diff --git a/packages/theme/src/cli/commands/theme/dev.ts b/packages/theme/src/cli/commands/theme/dev.ts index 99eb6c25b7..5408f37409 100644 --- a/packages/theme/src/cli/commands/theme/dev.ts +++ b/packages/theme/src/cli/commands/theme/dev.ts @@ -69,8 +69,8 @@ You can run this command only in a directory that matches the [default Shopify t env: 'SHOPIFY_FLAG_ERROR_OVERLAY', }), poll: Flags.boolean({ - hidden: true, - description: 'Force polling to detect file changes.', + description: + 'Use polling to detect file changes. Use this when file system events are unreliable, such as with build tools that preserve timestamps, Docker volumes, or network filesystems.', env: 'SHOPIFY_FLAG_POLL', }), 'theme-editor-sync': Flags.boolean({ @@ -183,6 +183,7 @@ You can run this command only in a directory that matches the [default Shopify t ignore, only, notify: flags.notify, + poll: flags.poll, }) await metafieldsPull({ diff --git a/packages/theme/src/cli/services/dev.ts b/packages/theme/src/cli/services/dev.ts index 47ad2a174a..57fc693a20 100644 --- a/packages/theme/src/cli/services/dev.ts +++ b/packages/theme/src/cli/services/dev.ts @@ -40,6 +40,7 @@ interface DevOptions { only: string[] notify?: string listing?: string + poll?: boolean } export async function dev(options: DevOptions) { @@ -83,6 +84,7 @@ export async function dev(options: DevOptions) { listing: options.listing, noDelete: options.noDelete, notify: options.notify, + poll: options.poll, }) const host = options.host ?? DEFAULT_HOST diff --git a/packages/theme/src/cli/utilities/theme-fs.test.ts b/packages/theme/src/cli/utilities/theme-fs.test.ts index ac981cbf5c..dca4d00b9f 100644 --- a/packages/theme/src/cli/utilities/theme-fs.test.ts +++ b/packages/theme/src/cli/utilities/theme-fs.test.ts @@ -135,6 +135,42 @@ describe('theme-fs', () => { ) }) + test('passes usePolling and useFsEvents options to chokidar when poll is true', async () => { + // Given + const root = joinPath(locationOfThisFile, 'fixtures/theme') + const watchSpy = vi.spyOn(chokidar, 'watch') + + // When + const themeFileSystem = mountThemeFileSystem(root, {poll: true}) + await themeFileSystem.ready() + await themeFileSystem.startWatcher('123', {token: 'token'} as any) + + // Then + expect(watchSpy).toHaveBeenCalledWith(expect.any(Array), { + ignored: expect.any(Array), + persistent: expect.any(Boolean), + ignoreInitial: true, + usePolling: true, + useFsEvents: false, + }) + }) + + test('does not include usePolling or useFsEvents in chokidar options when poll is not set', async () => { + // Given + const root = joinPath(locationOfThisFile, 'fixtures/theme') + const watchSpy = vi.spyOn(chokidar, 'watch') + + // When + const themeFileSystem = mountThemeFileSystem(root) + await themeFileSystem.ready() + await themeFileSystem.startWatcher('123', {token: 'token'} as any) + + // Then + const chokidarOptions = watchSpy.mock.calls[0]?.[1] as Record + expect(chokidarOptions).not.toHaveProperty('usePolling') + expect(chokidarOptions).not.toHaveProperty('useFsEvents') + }) + test('does not include listing directory when no listing is specified', async () => { // Given const root = joinPath(locationOfThisFile, 'fixtures/theme') diff --git a/packages/theme/src/cli/utilities/theme-fs.ts b/packages/theme/src/cli/utilities/theme-fs.ts index 3f4781f419..cd1185a9f1 100644 --- a/packages/theme/src/cli/utilities/theme-fs.ts +++ b/packages/theme/src/cli/utilities/theme-fs.ts @@ -321,6 +321,7 @@ export function mountThemeFileSystem(root: string, options?: ThemeFileSystemOpti ignored: DEFAULT_IGNORE_PATTERNS, persistent: !process.env.SHOPIFY_UNIT_TEST, ignoreInitial: true, + ...(options?.poll ? {usePolling: true, useFsEvents: false} : {}), }) // Debounce file events per-file and per-event-type