From ead29baa51c6afe1cbd9abebc407c1f1a9a5d532 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Tue, 14 Apr 2026 15:26:53 -0400 Subject: [PATCH 1/4] Lazy command loading: oclif pattern strategy with thin re-exports (Option B) Switch from explicit command strategy (single barrel file loading all packages) to oclif's native pattern strategy with per-command re-export files. Key changes: - Add bootstrap.ts as lightweight CLI entry point (no heavy package imports) - Create commands/ directory with 108 thin re-export files (one per command) - Switch oclif config from explicit to pattern strategy - Split hooks from index.ts barrel into individual files with dynamic imports - Defer app/hydrogen init hooks to prerun (only run when relevant command executes) - Add customPluginName assignment in prerun hook for analytics attribution - Enable Node.js compile cache for faster repeat startups Heavy packages (@shopify/app, @shopify/theme, @shopify/cli-hydrogen) are no longer imported at startup for non-app/theme/hydrogen commands. The version command confirms zero heavy package loads. Performance: 1.594s mean (vs 1.778s main baseline = -10.3%) The remaining time is @shopify/cli-kit + oclif framework overhead. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/lazy-loading-options.md | 432 +++++++++ docs/lazy-loading-refactor-plan.md | 616 ++++++++++++ packages/cli/README.md | 182 ++++ packages/cli/bin/dev.js | 2 +- packages/cli/bin/run.js | 6 +- packages/cli/oclif.manifest.json | 877 ++++++++++++++++-- packages/cli/package.json | 45 +- packages/cli/src/bootstrap.ts | 67 ++ packages/cli/src/commands/app/build.ts | 3 + packages/cli/src/commands/app/bulk/cancel.ts | 3 + packages/cli/src/commands/app/bulk/execute.ts | 3 + packages/cli/src/commands/app/bulk/status.ts | 3 + packages/cli/src/commands/app/config/link.ts | 3 + packages/cli/src/commands/app/config/pull.ts | 3 + packages/cli/src/commands/app/config/use.ts | 3 + .../cli/src/commands/app/config/validate.ts | 3 + packages/cli/src/commands/app/deploy.ts | 3 + packages/cli/src/commands/app/dev.ts | 3 + packages/cli/src/commands/app/dev/clean.ts | 3 + packages/cli/src/commands/app/env/pull.ts | 3 + packages/cli/src/commands/app/env/show.ts | 3 + packages/cli/src/commands/app/execute.ts | 3 + .../cli/src/commands/app/function/build.ts | 3 + .../cli/src/commands/app/function/info.ts | 3 + .../cli/src/commands/app/function/replay.ts | 3 + packages/cli/src/commands/app/function/run.ts | 3 + .../cli/src/commands/app/function/schema.ts | 3 + .../cli/src/commands/app/function/typegen.ts | 3 + .../src/commands/app/generate/extension.ts | 3 + .../cli/src/commands/app/generate/schema.ts | 3 + .../app/import-custom-data-definitions.ts | 3 + .../cli/src/commands/app/import-extensions.ts | 3 + packages/cli/src/commands/app/info.ts | 3 + packages/cli/src/commands/app/init.ts | 3 + packages/cli/src/commands/app/logs.ts | 3 + packages/cli/src/commands/app/release.ts | 3 + .../cli/src/commands/app/versions/list.ts | 3 + .../cli/src/commands/app/webhook/trigger.ts | 3 + packages/cli/src/commands/auth/login.ts | 1 + packages/cli/src/commands/auth/logout.ts | 1 + packages/cli/src/commands/cache/clear.ts | 1 + packages/cli/src/commands/commands.ts | 3 + .../src/commands/config/autocorrect/off.ts | 3 + .../cli/src/commands/config/autocorrect/on.ts | 3 + .../src/commands/config/autocorrect/status.ts | 3 + .../cli/src/commands/debug/command-flags.ts | 1 + packages/cli/src/commands/demo/watcher.ts | 3 + packages/cli/src/commands/docs/generate.ts | 1 + .../cli/src/commands/doctor-release/index.ts | 1 + .../commands/doctor-release/theme/index.ts | 1 + packages/cli/src/commands/help.ts | 1 + packages/cli/src/commands/hydrogen/build.ts | 3 + packages/cli/src/commands/hydrogen/check.ts | 3 + packages/cli/src/commands/hydrogen/codegen.ts | 3 + .../hydrogen/customer-account-push.ts | 3 + .../cli/src/commands/hydrogen/debug/cpu.ts | 3 + packages/cli/src/commands/hydrogen/deploy.ts | 3 + packages/cli/src/commands/hydrogen/dev.ts | 3 + .../cli/src/commands/hydrogen/env/list.ts | 3 + .../cli/src/commands/hydrogen/env/pull.ts | 3 + .../cli/src/commands/hydrogen/env/push.ts | 3 + packages/cli/src/commands/hydrogen/g.ts | 3 + .../src/commands/hydrogen/generate/route.ts | 3 + .../src/commands/hydrogen/generate/routes.ts | 3 + packages/cli/src/commands/hydrogen/init.ts | 3 + packages/cli/src/commands/hydrogen/link.ts | 3 + packages/cli/src/commands/hydrogen/list.ts | 3 + packages/cli/src/commands/hydrogen/login.ts | 3 + packages/cli/src/commands/hydrogen/logout.ts | 3 + packages/cli/src/commands/hydrogen/preview.ts | 3 + packages/cli/src/commands/hydrogen/setup.ts | 3 + .../cli/src/commands/hydrogen/setup/css.ts | 3 + .../src/commands/hydrogen/setup/markets.ts | 3 + .../cli/src/commands/hydrogen/setup/vite.ts | 3 + .../cli/src/commands/hydrogen/shortcut.ts | 3 + packages/cli/src/commands/hydrogen/unlink.ts | 3 + packages/cli/src/commands/hydrogen/upgrade.ts | 3 + .../cli/src/commands/kitchen-sink/async.ts | 1 + .../cli/src/commands/kitchen-sink/index.ts | 1 + .../cli/src/commands/kitchen-sink/prompts.ts | 1 + .../cli/src/commands/kitchen-sink/static.ts | 1 + .../src/commands/notifications/generate.ts | 1 + .../cli/src/commands/notifications/list.ts | 1 + .../cli/src/commands/organization/list.ts | 3 + packages/cli/src/commands/plugins/index.ts | 5 + packages/cli/src/commands/plugins/inspect.ts | 3 + packages/cli/src/commands/plugins/install.ts | 5 + packages/cli/src/commands/plugins/link.ts | 3 + packages/cli/src/commands/plugins/reset.ts | 3 + .../cli/src/commands/plugins/uninstall.ts | 3 + packages/cli/src/commands/plugins/update.ts | 3 + packages/cli/src/commands/search.ts | 1 + packages/cli/src/commands/theme/check.ts | 3 + packages/cli/src/commands/theme/console.ts | 3 + packages/cli/src/commands/theme/delete.ts | 3 + packages/cli/src/commands/theme/dev.ts | 3 + packages/cli/src/commands/theme/duplicate.ts | 3 + packages/cli/src/commands/theme/info.ts | 3 + packages/cli/src/commands/theme/init.ts | 3 + .../cli/src/commands/theme/language-server.ts | 3 + packages/cli/src/commands/theme/list.ts | 3 + .../cli/src/commands/theme/metafields/pull.ts | 3 + packages/cli/src/commands/theme/open.ts | 3 + packages/cli/src/commands/theme/package.ts | 3 + packages/cli/src/commands/theme/preview.ts | 3 + packages/cli/src/commands/theme/profile.ts | 3 + packages/cli/src/commands/theme/publish.ts | 3 + packages/cli/src/commands/theme/pull.ts | 3 + packages/cli/src/commands/theme/push.ts | 3 + packages/cli/src/commands/theme/rename.ts | 3 + packages/cli/src/commands/theme/serve.ts | 3 + packages/cli/src/commands/theme/share.ts | 3 + packages/cli/src/commands/upgrade.ts | 1 + packages/cli/src/commands/version.ts | 1 + packages/cli/src/commands/webhook/trigger.ts | 3 + packages/cli/src/hooks/app-init.ts | 9 + packages/cli/src/hooks/app-public-metadata.ts | 7 + .../cli/src/hooks/app-sensitive-metadata.ts | 7 + packages/cli/src/hooks/did-you-mean.ts | 7 + packages/cli/src/hooks/hydrogen-init.ts | 8 + packages/cli/src/hooks/plugin-plugins.ts | 8 + packages/cli/src/hooks/prerun.ts | 59 +- packages/cli/src/hooks/tunnel-provider.ts | 7 + packages/cli/src/hooks/tunnel-start.ts | 7 + packages/cli/src/index.ts | 160 +--- 125 files changed, 2514 insertions(+), 283 deletions(-) create mode 100644 docs/lazy-loading-options.md create mode 100644 docs/lazy-loading-refactor-plan.md create mode 100644 packages/cli/src/bootstrap.ts create mode 100644 packages/cli/src/commands/app/build.ts create mode 100644 packages/cli/src/commands/app/bulk/cancel.ts create mode 100644 packages/cli/src/commands/app/bulk/execute.ts create mode 100644 packages/cli/src/commands/app/bulk/status.ts create mode 100644 packages/cli/src/commands/app/config/link.ts create mode 100644 packages/cli/src/commands/app/config/pull.ts create mode 100644 packages/cli/src/commands/app/config/use.ts create mode 100644 packages/cli/src/commands/app/config/validate.ts create mode 100644 packages/cli/src/commands/app/deploy.ts create mode 100644 packages/cli/src/commands/app/dev.ts create mode 100644 packages/cli/src/commands/app/dev/clean.ts create mode 100644 packages/cli/src/commands/app/env/pull.ts create mode 100644 packages/cli/src/commands/app/env/show.ts create mode 100644 packages/cli/src/commands/app/execute.ts create mode 100644 packages/cli/src/commands/app/function/build.ts create mode 100644 packages/cli/src/commands/app/function/info.ts create mode 100644 packages/cli/src/commands/app/function/replay.ts create mode 100644 packages/cli/src/commands/app/function/run.ts create mode 100644 packages/cli/src/commands/app/function/schema.ts create mode 100644 packages/cli/src/commands/app/function/typegen.ts create mode 100644 packages/cli/src/commands/app/generate/extension.ts create mode 100644 packages/cli/src/commands/app/generate/schema.ts create mode 100644 packages/cli/src/commands/app/import-custom-data-definitions.ts create mode 100644 packages/cli/src/commands/app/import-extensions.ts create mode 100644 packages/cli/src/commands/app/info.ts create mode 100644 packages/cli/src/commands/app/init.ts create mode 100644 packages/cli/src/commands/app/logs.ts create mode 100644 packages/cli/src/commands/app/release.ts create mode 100644 packages/cli/src/commands/app/versions/list.ts create mode 100644 packages/cli/src/commands/app/webhook/trigger.ts create mode 100644 packages/cli/src/commands/auth/login.ts create mode 100644 packages/cli/src/commands/auth/logout.ts create mode 100644 packages/cli/src/commands/cache/clear.ts create mode 100644 packages/cli/src/commands/commands.ts create mode 100644 packages/cli/src/commands/config/autocorrect/off.ts create mode 100644 packages/cli/src/commands/config/autocorrect/on.ts create mode 100644 packages/cli/src/commands/config/autocorrect/status.ts create mode 100644 packages/cli/src/commands/debug/command-flags.ts create mode 100644 packages/cli/src/commands/demo/watcher.ts create mode 100644 packages/cli/src/commands/docs/generate.ts create mode 100644 packages/cli/src/commands/doctor-release/index.ts create mode 100644 packages/cli/src/commands/doctor-release/theme/index.ts create mode 100644 packages/cli/src/commands/help.ts create mode 100644 packages/cli/src/commands/hydrogen/build.ts create mode 100644 packages/cli/src/commands/hydrogen/check.ts create mode 100644 packages/cli/src/commands/hydrogen/codegen.ts create mode 100644 packages/cli/src/commands/hydrogen/customer-account-push.ts create mode 100644 packages/cli/src/commands/hydrogen/debug/cpu.ts create mode 100644 packages/cli/src/commands/hydrogen/deploy.ts create mode 100644 packages/cli/src/commands/hydrogen/dev.ts create mode 100644 packages/cli/src/commands/hydrogen/env/list.ts create mode 100644 packages/cli/src/commands/hydrogen/env/pull.ts create mode 100644 packages/cli/src/commands/hydrogen/env/push.ts create mode 100644 packages/cli/src/commands/hydrogen/g.ts create mode 100644 packages/cli/src/commands/hydrogen/generate/route.ts create mode 100644 packages/cli/src/commands/hydrogen/generate/routes.ts create mode 100644 packages/cli/src/commands/hydrogen/init.ts create mode 100644 packages/cli/src/commands/hydrogen/link.ts create mode 100644 packages/cli/src/commands/hydrogen/list.ts create mode 100644 packages/cli/src/commands/hydrogen/login.ts create mode 100644 packages/cli/src/commands/hydrogen/logout.ts create mode 100644 packages/cli/src/commands/hydrogen/preview.ts create mode 100644 packages/cli/src/commands/hydrogen/setup.ts create mode 100644 packages/cli/src/commands/hydrogen/setup/css.ts create mode 100644 packages/cli/src/commands/hydrogen/setup/markets.ts create mode 100644 packages/cli/src/commands/hydrogen/setup/vite.ts create mode 100644 packages/cli/src/commands/hydrogen/shortcut.ts create mode 100644 packages/cli/src/commands/hydrogen/unlink.ts create mode 100644 packages/cli/src/commands/hydrogen/upgrade.ts create mode 100644 packages/cli/src/commands/kitchen-sink/async.ts create mode 100644 packages/cli/src/commands/kitchen-sink/index.ts create mode 100644 packages/cli/src/commands/kitchen-sink/prompts.ts create mode 100644 packages/cli/src/commands/kitchen-sink/static.ts create mode 100644 packages/cli/src/commands/notifications/generate.ts create mode 100644 packages/cli/src/commands/notifications/list.ts create mode 100644 packages/cli/src/commands/organization/list.ts create mode 100644 packages/cli/src/commands/plugins/index.ts create mode 100644 packages/cli/src/commands/plugins/inspect.ts create mode 100644 packages/cli/src/commands/plugins/install.ts create mode 100644 packages/cli/src/commands/plugins/link.ts create mode 100644 packages/cli/src/commands/plugins/reset.ts create mode 100644 packages/cli/src/commands/plugins/uninstall.ts create mode 100644 packages/cli/src/commands/plugins/update.ts create mode 100644 packages/cli/src/commands/search.ts create mode 100644 packages/cli/src/commands/theme/check.ts create mode 100644 packages/cli/src/commands/theme/console.ts create mode 100644 packages/cli/src/commands/theme/delete.ts create mode 100644 packages/cli/src/commands/theme/dev.ts create mode 100644 packages/cli/src/commands/theme/duplicate.ts create mode 100644 packages/cli/src/commands/theme/info.ts create mode 100644 packages/cli/src/commands/theme/init.ts create mode 100644 packages/cli/src/commands/theme/language-server.ts create mode 100644 packages/cli/src/commands/theme/list.ts create mode 100644 packages/cli/src/commands/theme/metafields/pull.ts create mode 100644 packages/cli/src/commands/theme/open.ts create mode 100644 packages/cli/src/commands/theme/package.ts create mode 100644 packages/cli/src/commands/theme/preview.ts create mode 100644 packages/cli/src/commands/theme/profile.ts create mode 100644 packages/cli/src/commands/theme/publish.ts create mode 100644 packages/cli/src/commands/theme/pull.ts create mode 100644 packages/cli/src/commands/theme/push.ts create mode 100644 packages/cli/src/commands/theme/rename.ts create mode 100644 packages/cli/src/commands/theme/serve.ts create mode 100644 packages/cli/src/commands/theme/share.ts create mode 100644 packages/cli/src/commands/upgrade.ts create mode 100644 packages/cli/src/commands/version.ts create mode 100644 packages/cli/src/commands/webhook/trigger.ts create mode 100644 packages/cli/src/hooks/app-init.ts create mode 100644 packages/cli/src/hooks/app-public-metadata.ts create mode 100644 packages/cli/src/hooks/app-sensitive-metadata.ts create mode 100644 packages/cli/src/hooks/did-you-mean.ts create mode 100644 packages/cli/src/hooks/hydrogen-init.ts create mode 100644 packages/cli/src/hooks/plugin-plugins.ts create mode 100644 packages/cli/src/hooks/tunnel-provider.ts create mode 100644 packages/cli/src/hooks/tunnel-start.ts diff --git a/docs/lazy-loading-options.md b/docs/lazy-loading-options.md new file mode 100644 index 00000000000..eca843dbf28 --- /dev/null +++ b/docs/lazy-loading-options.md @@ -0,0 +1,432 @@ +# Lazy Command Loading: Architectural Options + +## Background + +The Shopify CLI uses [oclif](https://oclif.io/) as its command framework. The current architecture uses the **explicit** command discovery strategy, where all ~108 commands are exported from a single barrel file (`index.ts`). This means running `shopify version` eagerly imports `@shopify/app`, `@shopify/theme`, `@shopify/cli-hydrogen`, `@oclif/plugin-commands`, `@oclif/plugin-plugins`, and `@shopify/plugin-did-you-mean` — even though only one command runs. + +This document compares three approaches to fixing that, evaluated against these principles: + +> **Implementation plan:** [`.claude/plans/lazy-loading-refactor.md`](lazy-loading-refactor-plan.md) + +### Principles + +1. **Startup performance** — The CLI must only pay the import cost of the command being run. Running `shopify version` should not import `@shopify/app`, `@shopify/theme`, or `@shopify/cli-hydrogen`. +2. **Idiomatic code** — Apply well-understood patterns appropriate for the tools and problems at hand. When using a framework like oclif, work *with* its conventions rather than around them. Avoid reimplementing framework internals or relying on undocumented behavior. +3. **Ease of maintenance** — The solution should be easy to evolve. Adding, removing, or renaming a command should be a small, obvious change. Manual registries that drift from the source of truth are a maintenance liability. The less custom machinery layered on top of the framework, the less there is to break on upgrades. +4. **Testability** — The solution should minimize the amount of custom infrastructure that needs its own tests. Framework-provided behavior doesn't need to be re-tested; hand-rolled replacements for framework behavior do. +5. **Codebase compatibility** — The solution must work with the codebase as it exists today. External packages (`@shopify/cli-hydrogen`) live outside this monorepo. Each plugin package has its own export conventions and build pipeline. The approach must be compatible with these realities without requiring coordinated changes across teams or repositories. +6. **Small refactor scope** *(least important)* — All else being equal, prefer the approach that requires fewer file moves, fewer package-level structural changes, and less CI/build pipeline rework to land. A smaller diff is easier to review, less likely to introduce regressions, and faster to ship. + +--- + +## Option A: Custom Command Registry (current branch) + +| | | +|---|---| +| **Branch** | `faster-startup-4` | +| **Perf baseline (`main`)** | 1.778s ± 0.106s (range 1.647s … 2.010s) | +| **Perf result** | 0.388s ± 0.024s (range 0.362s … 0.441s) | +| **Delta** | **-78.2%** | + +**Override oclif's `Config.runCommand()` with a hand-rolled registry that maps command IDs to dynamic imports.** + +Keep the `explicit` strategy but bypass oclif's default loading by subclassing `Config` and intercepting command execution. A new `command-registry.ts` maps each command ID to a dynamic `import()` call. + +### Key files + +**`bootstrap.ts`** — new lightweight entry point (replaces `index.ts` as the startup file). It moves the global proxy agent setup, signal handlers, and `uncaughtException` handler out of `index.ts` so they run without importing any command modules: + +```ts +// bootstrap.ts — intentionally imports nothing heavy +import {loadCommand} from './command-registry.js' +import {createGlobalProxyAgent} from 'global-agent' +import {runCLI} from '@shopify/cli-kit/node/cli' +import fs from 'fs' + +createGlobalProxyAgent({ + environmentVariableNamespace: 'SHOPIFY_', + forceGlobalAgent: true, + socketConnectionTimeout: 60000, +}) + +process.on('uncaughtException', async (err) => { + try { + const {FatalError} = await import('@shopify/cli-kit/node/error') + if (err instanceof FatalError) { + const {renderFatalError} = await import('@shopify/cli-kit/node/ui') + renderFatalError(err) + } else { + fs.writeSync(process.stderr.fd, `${err.stack ?? err.message ?? err}\n`) + } + } catch { + fs.writeSync(process.stderr.fd, `${err.stack ?? err.message ?? err}\n`) + } + process.exit(1) +}) + +const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'] +signals.forEach((signal) => { + process.on(signal, () => { process.exit(1) }) +}) + +async function runShopifyCLI({development}: {development: boolean}) { + await runCLI({ + moduleURL: import.meta.url, + development, + lazyCommandLoader: loadCommand, + }) +} + +export default runShopifyCLI +``` + +**`command-registry.ts`** — maps command IDs to dynamic imports: + +```ts +const cliCommands: Record Promise> = { + version: () => import('./cli/commands/version.js'), + search: () => import('./cli/commands/search.js'), + 'auth:logout': () => import('./cli/commands/auth/logout.js'), + // ... every CLI-local command (17 total) +} + +const appCommandIds = ['app:build', 'app:dev', 'app:deploy', /* ... 33 total */] + +// Helper to find the default export or first class with a .run() method +function searchForDefault(module: any): any { + if (module.default?.run) return module.default + for (const value of Object.values(module)) { + if (typeof value === 'function' && typeof (value as any).run === 'function') return value + } + return undefined +} + +export async function loadCommand(id: string): Promise { + // CLI-local commands: import just that file + const cliLoader = cliCommands[id] + if (cliLoader) { + const module = await cliLoader() + return searchForDefault(module) + } + + // App commands: import the package, grab one command + if (appCommandIds.includes(id)) { + const {commands} = await import('@shopify/app') + return commands[id] + } + + // Theme commands + if (id.startsWith('theme:')) { + const themeModule = await import('@shopify/theme') + return themeModule.default?.[id] + } + + // Hydrogen commands + if (id.startsWith('hydrogen:')) { + const {COMMANDS} = await import('@shopify/cli-hydrogen') + return COMMANDS?.[id] + } + + // Plugin commands (oclif built-ins, did-you-mean) + if (id === 'commands') { + const {commands} = await import('@oclif/plugin-commands') + return commands[id] + } + if (id.startsWith('plugins')) { + const {commands} = await import('@oclif/plugin-plugins') + return commands[id] + } + if (id.startsWith('config:autocorrect')) { + const {DidYouMeanCommands} = await import('@shopify/plugin-did-you-mean') + return DidYouMeanCommands[id] + } + + return undefined +} +``` + +**`custom-oclif-loader.ts`** — subclass of oclif's `Config`. Note: `ShopifyConfig` already existed before lazy loading — it handles hydrogen monorepo detection in dev mode and a custom plugin priority method. The lazy loading additions are `lazyCommandLoader`, `setLazyCommandLoader`, and the `runCommand` override: + +```ts +import {Command, Config} from '@oclif/core' + +export class ShopifyConfig extends Config { + private lazyCommandLoader?: (id: string) => Promise + + constructor(options: Options) { + // Pre-existing: hydrogen monorepo detection and pluginAdditions for dev mode + // Pre-existing: custom determinePriority override for dev mode + super(options) + } + + setLazyCommandLoader(loader: LazyCommandLoader): void { + this.lazyCommandLoader = loader + } + + async runCommand(id: string, argv: string[] = [], cachedCommand?: Command.Loadable | null): Promise { + if (this.lazyCommandLoader) { + const cmd = cachedCommand ?? this.findCommand(id) + if (cmd) { + const commandClass = await this.lazyCommandLoader(id) + if (commandClass) { + commandClass.id = id + // Note: rootPlugin is a private oclif property accessed via cast + commandClass.plugin = cmd.plugin ?? (this as any).rootPlugin + await this.runHook('prerun', {argv, Command: commandClass}) + const result = await commandClass.run(argv, this) + await this.runHook('postrun', {argv, Command: commandClass, result}) + return result + } + } + } + return super.runCommand(id, argv, cachedCommand) + } +} +``` + +**`package.json`** hooks point to individual files instead of the barrel: + +```json +"hooks": { + "init": ["./dist/hooks/app-init.js", "./dist/hooks/hydrogen-init.js"], + "command_not_found": "./dist/hooks/did-you-mean.js", + "tunnel_start": "./dist/hooks/tunnel-start.js" +} +``` + +Each hook file is a one-liner re-export: + +```ts +// hooks/app-init.ts +export {AppInitHook as default} from '@shopify/app' +``` + +### Evaluation + +- **Startup performance:** Good. Avoids importing `@shopify/app`, `@shopify/theme`, and `@shopify/cli-hydrogen` until the target command runs. Does not achieve per-command isolation within a package (running `app:dev` still loads all of `@shopify/app`). +- **Idiomatic code:** Low. Overrides `runCommand()` to bypass oclif's own command loading, manually calling `prerun`/`postrun` hooks and setting `.id`/`.plugin` on command classes. The `searchForDefault()` heuristic — scanning module exports for something with `.run()` — exists because the registry bypasses the normal contract where oclif knows what a command class looks like. This reimplements what oclif's `pattern` strategy does natively. +- **Ease of maintenance:** Low. `command-registry.ts` is a manual mapping that must be kept in sync with commands across 5+ packages. Adding `app:new-thing` in `@shopify/app` requires also updating the registry in `@shopify/cli` — this will drift. The `runCommand()` override reimplements oclif lifecycle internals; if oclif adds a new lifecycle step, this path silently skips it. `customPluginName` is not set on lazy-loaded commands, breaking analytics attribution — fixing it requires threading it through both the registry and the `runCommand()` override. +- **Testability:** Low. The `runCommand()` override, `searchForDefault()` heuristic, and command-registry mappings are all custom infrastructure that need their own tests. The registry correctness is hard to cover exhaustively since it must match every command export shape across all packages. +- **Codebase compatibility:** High. Works with all packages as they exist today — no structural changes required in `@shopify/app`, `@shopify/theme`, or the external `@shopify/cli-hydrogen`. Only requires that each package exports commands by name, which they already do. +- **Small refactor scope:** Medium. Adds `bootstrap.ts`, `command-registry.ts`, and hook files in `@shopify/cli`. Modifies `ShopifyConfig` in `cli-kit`. Does not touch plugin packages. Moderate diff, but the new files are net-new custom infrastructure. + +--- + +## Option B: Pattern Strategy with Thin Re-export Files + +| | | +|---|---| +| **Branch** | `lazy-loading-option-b` | +| **Perf baseline (`main`)** | 1.778s ± 0.106s (range 1.647s … 2.010s) | +| **Perf result** | 1.594s ± 0.027s (range 1.556s … 1.646s) | +| **Delta** | **-10.3%** | +| **Note** | Heavy packages (`@shopify/app`, `@shopify/theme`, `@shopify/cli-hydrogen`) are NOT loaded for `version`. The remaining 1.6s is `@shopify/cli-kit` + oclif baseline cost. | + +**Switch to oclif's native `pattern` discovery with a `commands/` directory where each file is a one-line re-export.** + +oclif's `pattern` strategy discovers commands by scanning a `commands/` directory. Each file's path becomes the command ID (e.g., `commands/app/dev.ts` → `app:dev`). oclif already lazy-loads each file individually when the command runs — no custom `Config` subclass needed. + +### Key files + +**`package.json`** — switch strategy: + +```json +"oclif": { + "commands": { + "strategy": "pattern", + "target": "./dist/commands" + } +} +``` + +**`commands/`** directory with thin re-exports — one file per command: + +``` +packages/cli/src/commands/ +├── version.ts ← local command (full implementation) +├── search.ts ← local command (full implementation) +├── upgrade.ts ← local command (full implementation) +├── auth/ +│ ├── login.ts ← local command +│ └── logout.ts ← local command +├── app/ +│ ├── dev.ts ← re-export from @shopify/app +│ ├── build.ts ← re-export from @shopify/app +│ ├── deploy.ts ← re-export from @shopify/app +│ ├── info.ts ← re-export from @shopify/app +│ ├── config/ +│ │ ├── link.ts +│ │ └── use.ts +│ └── ... +├── theme/ +│ ├── dev.ts ← re-export from @shopify/theme +│ ├── push.ts ← re-export from @shopify/theme +│ └── ... +└── hydrogen/ + ├── dev.ts ← re-export from @shopify/cli-hydrogen + └── ... +``` + +Each re-export file is minimal: + +```ts +// commands/app/dev.ts +import {commands} from '@shopify/app' +export default commands['app:dev'] +``` + +```ts +// commands/theme/push.ts +import ThemeCommands from '@shopify/theme' +export default ThemeCommands['theme:push'] +``` + +```ts +// commands/hydrogen/dev.ts +import {COMMANDS} from '@shopify/cli-hydrogen' +export default COMMANDS['hydrogen:dev'] +``` + +**`bootstrap.ts`** simplifies — no custom loader needed, but still needs the proxy agent setup, signal handlers, and `uncaughtException` handler (moved from `index.ts`): + +```ts +import {createGlobalProxyAgent} from 'global-agent' +import {runCLI} from '@shopify/cli-kit/node/cli' +import fs from 'fs' + +createGlobalProxyAgent({/* ... */}) +process.on('uncaughtException', async (err) => { /* ... */ }) +// ... signal handlers, column detection ... + +async function runShopifyCLI({development}: {development: boolean}) { + await runCLI({ + moduleURL: import.meta.url, + development, + // no lazyCommandLoader — oclif handles it + }) +} + +export default runShopifyCLI +``` + +**`cli-launcher.ts`** drops the lazy loader plumbing but still needs `ShopifyConfig` (not stock `Config`) because it contains hydrogen monorepo detection and custom plugin priority logic unrelated to lazy loading: + +```ts +import {ShopifyConfig} from './custom-oclif-loader.js' +import {run, flush, Errors, settings} from '@oclif/core' + +const config = new ShopifyConfig({root: fileURLToPath(options.moduleURL)}) +await config.load() +// no setLazyCommandLoader call — oclif handles lazy loading via pattern strategy +await run(options.argv, config) +``` + +### Evaluation + +- **Startup performance:** Good. Same as Option A — avoids importing `@shopify/app`, `@shopify/theme`, and `@shopify/cli-hydrogen` until the target command runs. Does not achieve per-command isolation within a package (running `app:dev` still loads all of `@shopify/app`). +- **Idiomatic code:** High. Uses oclif's native `pattern` strategy — the framework's built-in answer to lazy loading. No `runCommand()` override, no manual registry. The filesystem convention (`commands/app/dev.ts` → `app:dev`) is the same pattern used by Salesforce CLI, Heroku CLI, and other large oclif projects. `ShopifyConfig` is still required for hydrogen monorepo detection and plugin priority, but the lazy-loading parts are removed. +- **Ease of maintenance:** High. No manual registry to keep in sync — adding a command means adding a one-line file. The filesystem is the source of truth. Won't break on oclif upgrades since it uses the supported API. `customPluginName` needs a new home (each re-export or a hook), but the fix is trivial. The ~108 re-export files are one line each, grep-able, git-blame-able, and individually deletable. +- **Testability:** High. Removes the `runCommand()` override entirely — oclif handles command loading, so there is no custom loading machinery to test. Each re-export file is independently importable. `bootstrap.ts` simplifies because it no longer threads a `lazyCommandLoader` through the call stack. +- **Codebase compatibility:** High. Works with all packages as they exist today. Only requires that each package exports commands by name, which they already do. The external `@shopify/cli-hydrogen` needs no changes. +- **Small refactor scope:** Medium. Adds ~108 one-line re-export files in a `commands/` directory, switches `package.json` to `strategy: "pattern"`, and carries over the `bootstrap.ts` and hook-splitting work. No changes to plugin packages. The re-export files are trivial individually but the file count is high. + +--- + +## Option C: Proper oclif Plugin Architecture (with Wrapper Packages) + +| | | +|---|---| +| **Branch** | _TODO: add branch URL (based on Option B branch)_ | +| **Perf baseline (`main`)** | 1.778s ± 0.106s | +| **Perf result** | _TODO: `shopify version` median time_ | +| **Delta vs B** | _TODO_ | + +**Create thin wrapper plugin packages (`plugin-app`, `plugin-theme`, `plugin-hydrogen`) that re-export commands from the real packages, making each a proper oclif plugin without modifying the underlying packages.** + +oclif's plugin system is designed for exactly this — a CLI composed of multiple independent command packages. Each plugin declares its own commands, hooks, and manifest. The root CLI just lists which plugins to load, and oclif handles discovery and lazy loading per-plugin. + +### Key files + +**Root `packages/cli/package.json`** — declare plugins: + +```json +"oclif": { + "commands": { + "strategy": "pattern", + "target": "./dist/commands" + }, + "plugins": [ + "@shopify/app", + "@shopify/theme", + "@shopify/cli-hydrogen", + "@shopify/plugin-did-you-mean", + "@oclif/plugin-commands", + "@oclif/plugin-plugins" + ] +} +``` + +The root CLI only has its own local commands (`version`, `search`, `upgrade`, `auth:*`, etc.). + +**Each plugin package** gets its own oclif config. For example, `packages/app/package.json`: + +```json +"oclif": { + "commands": { + "strategy": "pattern", + "target": "./dist/commands" + }, + "hooks": { + "init": "./dist/hooks/init.js", + "sensitive_command_metadata": "./dist/hooks/sensitive-metadata.js", + "public_command_metadata": "./dist/hooks/public-metadata.js" + } +} +``` + +**Plugin command files** use oclif's filesystem convention: + +``` +packages/app/src/commands/ +├── app/ +│ ├── dev.ts ← full command implementation +│ ├── build.ts +│ ├── deploy.ts +│ └── config/ +│ ├── link.ts +│ └── use.ts +``` + +**No barrel files needed.** Each plugin is self-contained. The root CLI's `index.ts` shrinks to almost nothing: + +```ts +import {runCLI} from '@shopify/cli-kit/node/cli' + +async function runShopifyCLI({development}: {development: boolean}) { + await runCLI({moduleURL: import.meta.url, development}) +} + +export default runShopifyCLI +``` + +### Evaluation + +- **Startup performance:** Best. Full per-plugin isolation — oclif lazy-loads each plugin, so a plugin's code is only loaded when one of its commands runs. However, the marginal improvement over Option B is small because the expensive imports are cross-package, not intra-package. +- **Idiomatic code:** Highest. The textbook oclif architecture — how Salesforce CLI, Heroku CLI, and other large oclif CLIs are structured. Each plugin declares its own commands, hooks, and manifest. No barrel files, no manual registries, no `runCommand()` override. `customPluginName` becomes unnecessary since oclif tracks plugin ownership natively. +- **Ease of maintenance:** High. Each wrapper plugin is self-contained — adding a command means adding a one-line re-export in the appropriate wrapper. Hooks are co-located with their plugin. The wrapper approach avoids restructuring `@shopify/app`, `@shopify/theme`, or coordinating with the Hydrogen team. `customPluginName` becomes unnecessary since oclif tracks plugin ownership natively. +- **Testability:** Highest in theory. Each plugin is independently testable with its own manifest. No custom loading infrastructure to test. The wrapper packages are trivial (one-line re-exports) so there's little to break. +- **Codebase compatibility:** High. Wrapper packages (`plugin-app`, `plugin-theme`, `plugin-hydrogen`) live in the monorepo and depend on the real packages as regular npm dependencies. No changes required to `@shopify/app`, `@shopify/theme`, or the external `@shopify/cli-hydrogen`. +- **Small refactor scope:** Medium. Creates 3 new wrapper packages with one-line re-export files, moves hooks from root CLI into wrappers, and updates root CLI's plugin config. The re-export files from Option B are redistributed, not rewritten. No changes to existing plugin packages. + +--- + +## Comparison Summary + +| Principle | A: Custom Registry | B: Pattern + Re-exports | C: Plugin Architecture | +|---|---|---|---| +| **Startup performance** | Good — per-package isolation | Good — per-package isolation | Best — per-plugin isolation (marginal delta over B) | +| **Idiomatic code** | Low — overrides `runCommand()`, reimplements framework internals | High — uses oclif's native `pattern` strategy | Highest — textbook oclif plugin architecture | +| **Ease of maintenance** | Low — manual registry drifts, `runCommand()` override to keep current | High — filesystem is the source of truth, no custom loading code | Mixed — best structure long-term, but major cross-repo refactor to get there | +| **Testability** | Low — registry mappings, `searchForDefault()`, and `runCommand()` override all need tests | High — no custom loading infrastructure to test | Highest in theory, but migration creates regression risk | +| **Codebase compatibility** | High — works with all packages as-is | High — works with all packages as-is | Low — requires cross-team coordination and restructuring external `cli-hydrogen` | +| **Small refactor scope** | Medium — new files in `cli` and `cli-kit`, no plugin changes | Medium — ~108 one-line files, no plugin changes | Very large — touches every package, cross-repo coordination | diff --git a/docs/lazy-loading-refactor-plan.md b/docs/lazy-loading-refactor-plan.md new file mode 100644 index 00000000000..d861ed8a604 --- /dev/null +++ b/docs/lazy-loading-refactor-plan.md @@ -0,0 +1,616 @@ +# Lazy Loading Refactor: Option B → Option C Implementation Plan + +> **Context document:** [`docs/lazy-loading-options.md`](lazy-loading-options.md) +> **Current branch:** `faster-startup` (has Option A implemented) +> **Goal:** Establish baseline measurements, implement Option B on a new branch, measure performance, then extend to Option C on a second branch. Both branches must pass CI (type-check, lint, bundle, tests, oclif-checks, knip). + +--- + +## Phase 0: Measure `main` baseline + +**Purpose:** Establish the baseline startup performance of `main` (no lazy loading). This is the number every other option is measured against. + +### Step 1: Build and measure + +```bash +git checkout main +pnpm build +hyperfine --warmup 3 --runs 20 'node packages/cli/bin/run.js version' +``` + +Record the full hyperfine output. This is the **worst-case baseline** — every command package (`@shopify/app`, `@shopify/theme`, `@shopify/cli-hydrogen`) is eagerly imported on every CLI invocation. + +### Step 2: Record baseline in `docs/lazy-loading-options.md` + +Add a baseline row at the top of each option's performance table. The `main` numbers will be reused in all subsequent phases — no need to re-measure. + +| Metric | `main` (no lazy loading) | +|--------|--------------------------| +| **Mean** | _Xs ± Ys_ | +| **Median** | _Xs_ | +| **Range** | _Xs … Xs_ | + +--- + +## Phase 1: Measure `faster-startup` branch (Option A) + +**Purpose:** Measure the current `faster-startup` branch (Option A — custom command registry) against the `main` baseline. + +### Step 1: Build and measure + +```bash +git checkout faster-startup +pnpm build +hyperfine --warmup 3 --runs 20 'node packages/cli/bin/run.js version' +``` + +### Step 2: Side-by-side comparison (preferred) + +If both branches are built in separate worktrees: + +```bash +hyperfine --warmup 3 --runs 20 \ + -n 'main (no lazy loading)' 'node /path/to/main/packages/cli/bin/run.js version' \ + -n 'faster-startup (Option A)' 'node /path/to/faster-startup/packages/cli/bin/run.js version' +``` + +### Step 3: Record in `docs/lazy-loading-options.md` + +Fill in the Option A performance table: + +| Metric | `main` (no lazy loading) | `faster-startup` (Option A) | Delta | +|--------|--------------------------|----------------------------|-------| +| **Mean** | _from Phase 0_ | _Xs ± Ys_ | _-X%_ | +| **Median** | _from Phase 0_ | _Xs_ | _-X%_ | +| **Range** | _from Phase 0_ | _Xs … Xs_ | | + +--- + +## Phase 2: Option B — Pattern Strategy with Thin Re-exports + +**Branch:** `lazy-loading-option-b` (based off `main`) + +### Step 1: Switch oclif strategy from `explicit` to `pattern` + +**File:** `packages/cli/package.json` + +Change: +```json +"commands": { + "strategy": "explicit", + "target": "./dist/index.js", + "identifier": "COMMANDS" +} +``` +To: +```json +"commands": { + "strategy": "pattern", + "target": "./dist/commands" +} +``` + +### Step 2: Create `packages/cli/src/commands/` directory with re-export files + +Create one file per command. Each file is a thin re-export that maps to the real command class. + +#### CLI-local commands (17 files) — move or re-export from `src/cli/commands/` + +These already live in the monorepo. Each file re-exports the default from its current location: + +``` +commands/version.ts → export {default} from '../cli/commands/version.js' +commands/search.ts → export {default} from '../cli/commands/search.js' +commands/upgrade.ts → export {default} from '../cli/commands/upgrade.js' +commands/help.ts → export {default} from '../cli/commands/help.js' +commands/auth/logout.ts → export {default} from '../../cli/commands/auth/logout.js' +commands/auth/login.ts → export {default} from '../../cli/commands/auth/login.js' +commands/debug/command-flags.ts +commands/kitchen-sink/index.ts +commands/kitchen-sink/async.ts +commands/kitchen-sink/prompts.ts +commands/kitchen-sink/static.ts +commands/doctor-release/doctor-release.ts ← NOTE: command ID is "doctor-release", file must match +commands/doctor-release/theme/index.ts +commands/docs/generate.ts +commands/notifications/list.ts +commands/notifications/generate.ts +commands/cache/clear.ts +``` + +**Important:** oclif's pattern strategy derives command IDs from file paths. `commands/app/dev.ts` → `app:dev`. The path must match the command ID exactly. + +**Edge case — `doctor-release` command:** The command ID is `doctor-release`, but the implementation file is `doctor-release.ts`. With pattern strategy, `commands/doctor-release.ts` would produce command ID `doctor-release`. If the implementation is at `commands/doctor-release/doctor-release.ts`, the ID would be `doctor-release:doctor-release`. Need to verify: does the current command have ID `doctor-release` or is it nested? Check the manifest. The manifest shows `"id": "doctor-release"` — so the file should be `commands/doctor-release.ts` (not in a subdirectory), OR we use `commands/doctor-release/index.ts` which oclif resolves to `doctor-release`. + +**Edge case — `webhook:trigger` (deprecated alias):** This command lives under app commands but its ID doesn't start with `app:`. It needs `commands/webhook/trigger.ts` re-exporting from `@shopify/app`. + +**Edge case — `demo:watcher` and `organization:list`:** Same situation — app commands with non-`app:` prefixes. Need `commands/demo/watcher.ts` and `commands/organization/list.ts`. + +#### App commands (34 files) — re-export from `@shopify/app` + +Each file imports the `commands` object from `@shopify/app` and re-exports the specific command: + +```ts +// commands/app/dev.ts +import {commands} from '@shopify/app' +export default commands['app:dev'] +``` + +Full list: +``` +commands/app/build.ts +commands/app/bulk/cancel.ts +commands/app/bulk/status.ts +commands/app/bulk/execute.ts +commands/app/config/link.ts +commands/app/config/use.ts +commands/app/config/pull.ts +commands/app/config/validate.ts +commands/app/deploy.ts +commands/app/dev.ts +commands/app/dev/clean.ts +commands/app/env/pull.ts +commands/app/env/show.ts +commands/app/execute.ts +commands/app/function/build.ts +commands/app/function/info.ts +commands/app/function/replay.ts +commands/app/function/run.ts +commands/app/function/schema.ts +commands/app/function/typegen.ts +commands/app/generate/extension.ts +commands/app/generate/schema.ts +commands/app/import-custom-data-definitions.ts +commands/app/import-extensions.ts +commands/app/info.ts +commands/app/init.ts +commands/app/logs.ts +commands/app/logs/sources.ts +commands/app/release.ts +commands/app/versions/list.ts +commands/app/webhook/trigger.ts +commands/webhook/trigger.ts ← deprecated alias, different path +commands/demo/watcher.ts ← app command with non-app prefix +commands/organization/list.ts ← app command with non-app prefix +``` + +#### Theme commands (20 files) — re-export from `@shopify/theme` + +```ts +// commands/theme/dev.ts +import ThemeCommands from '@shopify/theme' +export default ThemeCommands['theme:dev'] +``` + +Full list: +``` +commands/theme/check.ts +commands/theme/console.ts +commands/theme/delete.ts +commands/theme/dev.ts +commands/theme/duplicate.ts +commands/theme/info.ts +commands/theme/init.ts +commands/theme/language-server.ts +commands/theme/list.ts +commands/theme/metafields/pull.ts +commands/theme/open.ts +commands/theme/package.ts +commands/theme/preview.ts +commands/theme/profile.ts +commands/theme/publish.ts +commands/theme/pull.ts +commands/theme/push.ts +commands/theme/rename.ts +commands/theme/serve.ts +commands/theme/share.ts +``` + +#### Hydrogen commands (~25 files) — re-export from `@shopify/cli-hydrogen` + +```ts +// commands/hydrogen/dev.ts +import {COMMANDS} from '@shopify/cli-hydrogen' +export default COMMANDS['hydrogen:dev'] +``` + +Full list (verify against manifest — hydrogen commands are numerous): +``` +commands/hydrogen/build.ts +commands/hydrogen/check.ts +commands/hydrogen/codegen.ts +commands/hydrogen/customer-account-push.ts +commands/hydrogen/debug/cpu.ts +commands/hydrogen/deploy.ts +commands/hydrogen/dev.ts +commands/hydrogen/env/list.ts +commands/hydrogen/env/pull.ts +commands/hydrogen/env/push.ts +commands/hydrogen/g.ts +commands/hydrogen/generate/route.ts +commands/hydrogen/generate/routes.ts +commands/hydrogen/init.ts +commands/hydrogen/link.ts +commands/hydrogen/list.ts +commands/hydrogen/login.ts +commands/hydrogen/logout.ts +commands/hydrogen/preview.ts +commands/hydrogen/setup.ts +commands/hydrogen/setup/css.ts +commands/hydrogen/setup/markets.ts +commands/hydrogen/setup/vite.ts +commands/hydrogen/shortcut.ts +commands/hydrogen/unlink.ts +commands/hydrogen/upgrade.ts +``` + +#### Plugin commands — re-export from oclif plugins and did-you-mean + +``` +commands/commands.ts ← from @oclif/plugin-commands +commands/plugins/index.ts ← from @oclif/plugin-plugins +commands/plugins/inspect.ts +commands/plugins/install.ts +commands/plugins/link.ts +commands/plugins/reset.ts +commands/plugins/uninstall.ts +commands/plugins/update.ts +commands/config/autocorrect/off.ts ← from @shopify/plugin-did-you-mean +commands/config/autocorrect/on.ts +commands/config/autocorrect/status.ts +``` + +### Step 3: Handle `customPluginName` for analytics + +Currently `index.ts` sets `customPluginName` on every command class at import time (lines 92-126). This is used by `packages/cli-kit/src/private/node/analytics.ts` to attribute commands to their source package. + +**Two approaches:** + +**(a) Set it in each re-export file:** +```ts +// commands/app/dev.ts +import {commands} from '@shopify/app' +const cmd = commands['app:dev']! +;(cmd as any).customPluginName = '@shopify/app' +export default cmd +``` + +**(b) Set it in a prerun hook** — the `prerun` hook already runs before every command. Add `customPluginName` assignment there based on command ID prefix. This is cleaner and centralizes the logic. + +**Recommended: (b)** — add to existing `packages/cli/src/hooks/prerun.ts` or create a wrapper that assigns `customPluginName` based on command ID prefix mapping. + +### Step 4: Simplify `bootstrap.ts` + +Remove `lazyCommandLoader` — oclif handles lazy loading natively with `pattern` strategy: + +```ts +async function runShopifyCLI({development}: {development: boolean}) { + await runCLI({ + moduleURL: import.meta.url, + development, + // no lazyCommandLoader — oclif handles it via pattern strategy + }) +} +``` + +### Step 5: Simplify `cli-launcher.ts` and `ShopifyConfig` + +- Remove `setLazyCommandLoader()` method from `ShopifyConfig` +- Remove `runCommand()` override (the entire lazy loading override) +- Remove `LazyCommandLoader` type export +- Remove `lazyCommandLoader` parameter from `runCLI()` and `launchCLI()` +- Keep `ShopifyConfig` class — it still has `customPriority` logic for hydrogen monorepo dev mode + +### Step 6: Clean up dead code + +- Delete `packages/cli/src/command-registry.ts` +- `packages/cli/src/index.ts` — keep it but remove the `COMMANDS` export and heavy imports. It may still be needed for non-command exports (`push`, `pull`, `fetchStoreThemes`, hook re-exports). Check what else imports from `@shopify/cli` — if only oclif uses it for commands, the barrel can be gutted. If other packages import utilities from it, keep those exports. +- Check if `searchForDefault()` is used anywhere else — if not, remove it. + +### Step 7: Handle `index.ts` non-command exports + +`index.ts` currently also exports: +- `DidYouMeanHook`, `TunnelStartHook`, `TunnelProviderHook`, `PluginHook` — hook re-exports +- `AppSensitiveMetadataHook`, `AppInitHook`, `AppPublicMetadataHook` — app hook re-exports +- `HydrogenInitHook` — hydrogen hook re-export +- `push`, `pull`, `fetchStoreThemes` — theme utility re-exports + +**These are consumed by the hook files** (`packages/cli/src/hooks/*.ts`). But the hooks already import directly from their source packages (e.g., `hooks/app-init.ts` does `export {AppInitHook as default} from '@shopify/app'`). So the re-exports in `index.ts` are likely dead code. + +**Verify with knip** — run `pnpm knip` to check if these exports are used. If unused, remove them. If used by external consumers, keep a minimal `index.ts` with just those exports. + +### Step 8: Regenerate manifests and verify + +```bash +pnpm build +pnpm refresh-manifests +``` + +Verify the generated `oclif.manifest.json` contains all expected commands with correct IDs, flags, and descriptions. Diff against the current manifest to ensure no commands were dropped. + +### Step 9: Run CI checks locally + +```bash +pnpm nx run-many --all --target=build +pnpm nx run-many --all --target=type-check +pnpm nx run-many --all --target=lint +pnpm nx run-many --all --target=bundle +pnpm vitest run +pnpm refresh-manifests # verify no diff +pnpm knip +``` + +### Step 10: Measure performance + +**This step is mandatory — the whole point of this refactor is measurable startup improvement.** + +Measure startup time of `shopify version` (a lightweight command that exercises the full CLI bootstrap path without doing real work). + +**Reuse the `main` baseline from Phase 0 and Option A baseline from Phase 1** — no need to re-measure. + +#### 10a. Measure Option B branch + +```bash +git checkout lazy-loading-option-b +pnpm build +hyperfine --warmup 3 --runs 20 'node packages/cli/bin/run.js version' +``` + +#### 10b. Side-by-side comparison with baselines (preferred) + +If worktrees from Phase 0 and Phase 1 are still available: + +```bash +hyperfine --warmup 3 --runs 20 \ + -n 'main (no lazy loading)' 'node /path/to/main/packages/cli/bin/run.js version' \ + -n 'faster-startup (Option A)' 'node /path/to/option-a/packages/cli/bin/run.js version' \ + -n 'option-b' 'node packages/cli/bin/run.js version' +``` + +#### 10c. If `hyperfine` is not available + +```bash +# Install it +brew install hyperfine + +# Or fallback to manual timing (less precise) +for i in {1..10}; do + /usr/bin/time -p node packages/cli/bin/run.js version 2>&1 | grep real +done +``` + +#### 10d. What to record + +Fill in the table in `docs/lazy-loading-options.md` under Option B: + +| Metric | `main` (no lazy loading) | Option A (`faster-startup`) | Option B | Delta (B vs main) | +|--------|--------------------------|----------------------------|----------|-------------------| +| **Mean** | _from Phase 0_ | _from Phase 1_ | _Xs ± Ys_ | _-X%_ | +| **Median** | _from Phase 0_ | _from Phase 1_ | _Xs_ | _-X%_ | +| **Range** | _from Phase 0_ | _from Phase 1_ | _Xs … Xs_ | | + +### Step 11: Update `docs/lazy-loading-options.md` + +Fill in the Option B section with: +1. **Branch URL** — the GitHub compare or branch URL +2. **Performance table** — the metrics from Step 10, including Option A for comparison +3. Any notes on unexpected findings (e.g., if hook loading dominates startup, or if B is faster/slower than A) + +--- + +## Phase 3: Option C — Plugin Wrapper Architecture + +**Branch:** `lazy-loading-option-c` (based off `lazy-loading-option-b`) + +Option C extends Option B by extracting re-export files into separate plugin wrapper packages. The `commands/` directory in `packages/cli/` shrinks to only CLI-local commands. + +### Step 1: Create `packages/plugin-app/` wrapper package + +``` +packages/plugin-app/ +├── package.json +├── tsconfig.json +├── tsconfig.build.json +├── project.json +└── src/ + └── commands/ + └── app/ + ├── build.ts ← re-export from @shopify/app + ├── dev.ts + └── ... + └── webhook/ + └── trigger.ts ← the deprecated alias + └── demo/ + └── watcher.ts + └── organization/ + └── list.ts +``` + +**`package.json`:** +```json +{ + "name": "@shopify/plugin-app", + "version": "3.92.0", + "private": true, + "type": "module", + "main": "dist/index.js", + "dependencies": { + "@shopify/app": "3.92.0" + }, + "oclif": { + "commands": { + "strategy": "pattern", + "target": "./dist/commands" + }, + "hooks": { + "init": "./dist/hooks/init.js", + "sensitive_command_metadata": "./dist/hooks/sensitive-metadata.js", + "public_command_metadata": "./dist/hooks/public-metadata.js" + } + } +} +``` + +**Hooks** move from `packages/cli/src/hooks/` into the wrapper: +- `packages/plugin-app/src/hooks/init.ts` ← re-export `AppInitHook` from `@shopify/app` +- `packages/plugin-app/src/hooks/sensitive-metadata.ts` +- `packages/plugin-app/src/hooks/public-metadata.ts` + +### Step 2: Create `packages/plugin-theme/` wrapper package + +Same pattern. No hooks (theme has none). + +``` +packages/plugin-theme/ +├── package.json +├── src/ +│ └── commands/ +│ └── theme/ +│ ├── dev.ts +│ ├── push.ts +│ └── ... +``` + +### Step 3: Create `packages/plugin-hydrogen/` wrapper package + +Wraps the external `@shopify/cli-hydrogen` package. + +``` +packages/plugin-hydrogen/ +├── package.json +├── src/ +│ └── commands/ +│ └── hydrogen/ +│ ├── dev.ts +│ └── ... +│ └── hooks/ +│ └── init.ts ← re-export from @shopify/cli-hydrogen HOOKS.init +``` + +### Step 4: Register plugins in root CLI + +**`packages/cli/package.json`:** +```json +"oclif": { + "commands": { + "strategy": "pattern", + "target": "./dist/commands" + }, + "plugins": [ + "@shopify/plugin-app", + "@shopify/plugin-theme", + "@shopify/plugin-hydrogen", + "@shopify/plugin-did-you-mean", + "@oclif/plugin-commands", + "@oclif/plugin-plugins" + ] +} +``` + +Remove app/theme/hydrogen hooks from root CLI's hooks config — they now live in their respective plugins. + +Root CLI hooks config shrinks to: +```json +"hooks": { + "prerun": "./dist/hooks/prerun.js", + "postrun": "./dist/hooks/postrun.js", + "tunnel_start": "./dist/hooks/tunnel-start.js", + "tunnel_provider": "./dist/hooks/tunnel-provider.js", + "update": "./dist/hooks/plugin-plugins.js" +} +``` + +### Step 5: Shrink `packages/cli/src/commands/` + +Only CLI-local commands remain: +``` +commands/version.ts +commands/search.ts +commands/upgrade.ts +commands/help.ts +commands/auth/login.ts +commands/auth/logout.ts +commands/cache/clear.ts +commands/debug/command-flags.ts +commands/kitchen-sink/... +commands/doctor-release/... +commands/docs/generate.ts +commands/notifications/... +``` + +All app/theme/hydrogen/plugin re-exports are deleted — they now live in wrapper packages. + +### Step 6: Build pipeline for wrapper packages + +Each wrapper package needs: +- A `project.json` with build, type-check, and lint targets +- Entry in `pnpm-workspace.yaml` (already covered by `packages/*` glob) +- TypeScript config extending the root config +- Manifest generation (add to `refresh-manifests` script) + +### Step 7: Handle `customPluginName` + +With proper oclif plugins, `customPluginName` becomes unnecessary — oclif natively tracks which plugin owns each command via `Command.plugin`. The analytics code in `packages/cli-kit/src/private/node/analytics.ts` should be updated to prefer `commandClass.plugin?.name` and fall back to `customPluginName` only if needed. + +### Step 8: Run CI checks + +```bash +pnpm nx run-many --all --target=build +pnpm nx run-many --all --target=type-check +pnpm nx run-many --all --target=lint +pnpm nx run-many --all --target=bundle +pnpm vitest run +pnpm refresh-manifests # verify no diff +pnpm knip +``` + +### Step 9: Measure performance + +Same methodology as Phase 2 Step 10. Compare against **both** `main` and Option B: + +```bash +hyperfine --warmup 3 --runs 20 \ + -n 'main' 'node /path/to/main/packages/cli/bin/run.js version' \ + -n 'option-b' 'node /path/to/option-b/packages/cli/bin/run.js version' \ + -n 'option-c' 'node packages/cli/bin/run.js version' +``` + +Fill in the table in `docs/lazy-loading-options.md` under Option C: + +| Metric | `main` (baseline) | Option B | Option C | Delta (C vs main) | +|--------|-------------------|----------|----------|-------------------| +| **Mean** | _Xs ± Ys_ | _Xs ± Ys_ | _Xs ± Ys_ | _-X%_ | +| **Median** | _Xs_ | _Xs_ | _Xs_ | _-X%_ | +| **Range** | _Xs … Xs_ | _Xs … Xs_ | _Xs … Xs_ | | + +### Step 10: Update `docs/lazy-loading-options.md` + +Fill in the Option C section with: +1. **Branch URL** — the GitHub compare or branch URL +2. **Performance table** — the metrics from Step 9, including comparison to both `main` and Option B +3. Any notes on whether plugin isolation provided measurable improvement over Option B + +--- + +## Risk Checklist + +| Risk | Mitigation | +|------|-----------| +| Command ID mismatch (file path doesn't match expected ID) | Diff `oclif.manifest.json` before/after — every command must appear with correct ID | +| `customPluginName` breaks analytics | Add to re-export files or prerun hook; verify analytics tests pass | +| `knip` flags new re-export files as unused | May need knip config update for `commands/` directory | +| `plugins:install` description override lost | Move the `description = ''` and `hidden = true` assignments to re-export files | +| Hook loading order changes | Verify init hooks fire in same order (app-init before hydrogen-init) | +| `@shopify/cli-hydrogen` command list changes between versions | Generate hydrogen re-exports from the installed package's exports, not hardcoded | +| Manifest generation with pattern strategy produces different output | Compare field-by-field; `pluginAlias` and `pluginType` may differ | +| Option C: wrapper packages not discovered by oclif in dev mode | Test with `bin/dev.js` (development=true) — ShopifyConfig's `customPriority` may need adjustment | + +--- + +## Performance Expectations + +Both Option B and Option C achieve **per-package** lazy loading (not per-command). Running `shopify version` should NOT import `@shopify/app`, `@shopify/theme`, or `@shopify/cli-hydrogen`. + +The performance delta between B and C should be minimal — the difference is whether oclif loads one plugin (with many commands) or several plugins (with fewer commands each). The expensive part is the package import, which both defer equally. + +The main performance gain vs `main` (no lazy loading) is avoiding the import of ~6 heavy packages on every CLI invocation. diff --git a/packages/cli/README.md b/packages/cli/README.md index bcaa53e5e81..551fa5e4712 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -126,6 +126,8 @@ DESCRIPTION extension to ensure that it's valid. ``` +_See code: [dist/commands/app/build.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/build.js)_ + ## `shopify app bulk cancel` Cancel a bulk operation. @@ -151,6 +153,8 @@ DESCRIPTION Cancels a running bulk operation by ID. ``` +_See code: [dist/commands/app/bulk/cancel.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/bulk/cancel.js)_ + ## `shopify app bulk execute` Execute bulk operations. @@ -197,6 +201,8 @@ DESCRIPTION operations. ``` +_See code: [dist/commands/app/bulk/execute.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/bulk/execute.js)_ + ## `shopify app bulk status` Check the status of bulk operations. @@ -230,6 +236,8 @@ DESCRIPTION Use "`bulk execute`" (https://shopify.dev/docs/api/shopify-cli/app/app-bulk-execute) to start a new bulk operation. ``` +_See code: [dist/commands/app/bulk/status.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/bulk/status.js)_ + ## `shopify app config link` Fetch your app configuration from the Developer Dashboard. @@ -256,6 +264,8 @@ DESCRIPTION (https://shopify.dev/docs/apps/tools/cli/configuration) page. ``` +_See code: [dist/commands/app/config/link.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/config/link.js)_ + ## `shopify app config pull` Refresh an already-linked app configuration without prompts. @@ -281,6 +291,8 @@ DESCRIPTION target a specific configuration file, or omit it to use the default one. ``` +_See code: [dist/commands/app/config/pull.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/config/pull.js)_ + ## `shopify app config use [config] [flags]` Activate an app configuration. @@ -306,6 +318,8 @@ DESCRIPTION be prompted to choose from the configuration files in your project. ``` +_See code: [dist/commands/app/config/use.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/config/use.js)_ + ## `shopify app config validate` Validate your app configuration and extensions. @@ -331,6 +345,8 @@ DESCRIPTION errors found. ``` +_See code: [dist/commands/app/config/validate.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/config/validate.js)_ + ## `shopify app deploy` Deploy your Shopify app. @@ -382,6 +398,8 @@ DESCRIPTION need to "deploy your web app" (https://shopify.dev/docs/apps/deployment/web) to your own hosting solution. ``` +_See code: [dist/commands/app/deploy.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/deploy.js)_ + ## `shopify app dev` Run the app. @@ -431,6 +449,8 @@ DESCRIPTION (https://shopify.dev/docs/apps/build/cli-for-apps/test-apps-locally). ``` +_See code: [dist/commands/app/dev.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/dev.js)_ + ## `shopify app dev clean` Cleans up the dev preview from the selected store. @@ -457,6 +477,8 @@ DESCRIPTION It restores the app's active version to the selected development store. ``` +_See code: [dist/commands/app/dev/clean.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/dev/clean.js)_ + ## `shopify app env pull` Pull app and extensions environment variables. @@ -484,6 +506,8 @@ DESCRIPTION variables and commented variables are preserved. ``` +_See code: [dist/commands/app/env/pull.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/env/pull.js)_ + ## `shopify app env show` Display app and extensions environment variables. @@ -506,6 +530,8 @@ DESCRIPTION Displays environment variables that can be used to deploy apps and app extensions. ``` +_See code: [dist/commands/app/env/show.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/env/show.js)_ + ## `shopify app execute` Execute GraphQL queries and mutations. @@ -547,6 +573,8 @@ DESCRIPTION (https://shopify.dev/docs/api/shopify-cli/app/app-bulk-execute) instead. ``` +_See code: [dist/commands/app/execute.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/execute.js)_ + ## `shopify app function build` Compile a function to wasm. @@ -569,6 +597,8 @@ DESCRIPTION Compiles the function in your current directory to WebAssembly (Wasm) for testing purposes. ``` +_See code: [dist/commands/app/function/build.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/function/build.js)_ + ## `shopify app function info` Print basic information about your function. @@ -601,6 +631,8 @@ DESCRIPTION - The function runner path ``` +_See code: [dist/commands/app/function/info.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/function/info.js)_ + ## `shopify app function replay` Replays a function run from an app log. @@ -631,6 +663,8 @@ DESCRIPTION errors occur, refer to "Shopify Functions error handling" (https://shopify.dev/docs/api/functions/errors). ``` +_See code: [dist/commands/app/function/replay.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/function/replay.js)_ + ## `shopify app function run` Run a function locally for testing. @@ -660,6 +694,8 @@ DESCRIPTION errors occur, refer to "Shopify Functions error handling" (https://shopify.dev/docs/api/functions/errors). ``` +_See code: [dist/commands/app/function/run.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/function/run.js)_ + ## `shopify app function schema` Fetch the latest GraphQL schema for a function. @@ -688,6 +724,8 @@ DESCRIPTION latest GraphQL schema. The schema is written to the `schema.graphql` file. ``` +_See code: [dist/commands/app/function/schema.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/function/schema.js)_ + ## `shopify app function typegen` Generate GraphQL types for a function. @@ -712,6 +750,8 @@ DESCRIPTION function. Supports JavaScript functions out of the box, or any language via the `build.typegen_command` configuration. ``` +_See code: [dist/commands/app/function/typegen.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/function/typegen.js)_ + ## `shopify app generate extension` Generate a new app Extension. @@ -747,6 +787,8 @@ DESCRIPTION your extension. ``` +_See code: [dist/commands/app/generate/extension.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/generate/extension.js)_ + ## `shopify app import-custom-data-definitions` Import metafield and metaobject definitions. @@ -774,6 +816,8 @@ DESCRIPTION definitions" (https://shopify.dev/docs/apps/build/custom-data/declarative-custom-data-definitions). ``` +_See code: [dist/commands/app/import-custom-data-definitions.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/import-custom-data-definitions.js)_ + ## `shopify app import-extensions` Import dashboard-managed extensions into your app. @@ -795,6 +839,8 @@ DESCRIPTION Import dashboard-managed extensions into your app. ``` +_See code: [dist/commands/app/import-extensions.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/import-extensions.js)_ + ## `shopify app info` Print basic information about your app and extensions. @@ -828,6 +874,8 @@ DESCRIPTION - System information, including the package manager and version of Shopify CLI used in the project. ``` +_See code: [dist/commands/app/info.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/info.js)_ + ## `shopify app init` Create a new app project @@ -857,6 +905,8 @@ FLAGS --verbose [env: SHOPIFY_FLAG_VERBOSE] Increase the verbosity of the output. ``` +_See code: [dist/commands/app/init.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/init.js)_ + ## `shopify app logs` Stream detailed logs for your Shopify app. @@ -893,6 +943,8 @@ DESCRIPTION ``` ``` +_See code: [dist/commands/app/logs.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/logs.js)_ + ## `shopify app logs sources` Print out a list of sources that may be used with the logs command. @@ -916,6 +968,8 @@ DESCRIPTION only function extensions are supported as sources. ``` +_See code: [dist/commands/app/logs/sources.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/logs/sources.js)_ + ## `shopify app release --version ` Release an app version. @@ -945,6 +999,8 @@ DESCRIPTION Releases an existing app version. Pass the name of the version that you want to release using the `--version` flag. ``` +_See code: [dist/commands/app/release.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/release.js)_ + ## `shopify app versions list` List deployed versions of your app. @@ -969,6 +1025,8 @@ DESCRIPTION Lists the deployed app versions. An app version is a snapshot of your app extensions. ``` +_See code: [dist/commands/app/versions/list.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/versions/list.js)_ + ## `shopify app webhook trigger` Trigger delivery of a sample webhook topic payload to a designated address. @@ -1049,6 +1107,8 @@ DESCRIPTION - You can't use this method to validate your API webhook subscriptions. ``` +_See code: [dist/commands/app/webhook/trigger.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/app/webhook/trigger.js)_ + ## `shopify auth login` Logs you in to your Shopify account. @@ -1064,6 +1124,8 @@ DESCRIPTION Logs you in to your Shopify account. ``` +_See code: [dist/commands/auth/login.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/auth/login.js)_ + ## `shopify auth logout` Logs you out of the Shopify account or Partner account and store. @@ -1076,6 +1138,8 @@ DESCRIPTION Logs you out of the Shopify account or Partner account and store. ``` +_See code: [dist/commands/auth/logout.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/auth/logout.js)_ + ## `shopify commands` List all shopify commands. @@ -1103,6 +1167,8 @@ DESCRIPTION List all shopify commands. ``` +_See code: [dist/commands/commands.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/commands.js)_ + ## `shopify config autocorrect off` Disable autocorrect. Off by default. @@ -1122,6 +1188,8 @@ DESCRIPTION When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands. ``` +_See code: [dist/commands/config/autocorrect/off.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/config/autocorrect/off.js)_ + ## `shopify config autocorrect on` Enable autocorrect. Off by default. @@ -1141,6 +1209,8 @@ DESCRIPTION When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands. ``` +_See code: [dist/commands/config/autocorrect/on.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/config/autocorrect/on.js)_ + ## `shopify config autocorrect status` Check whether autocorrect is enabled or disabled. On by default. @@ -1160,6 +1230,8 @@ DESCRIPTION When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands. ``` +_See code: [dist/commands/config/autocorrect/status.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/config/autocorrect/status.js)_ + ## `shopify help [command] [flags]` Display help for Shopify CLI @@ -1178,6 +1250,8 @@ DESCRIPTION Display help for Shopify CLI ``` +_See code: [dist/commands/help.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/help.js)_ + ## `shopify hydrogen build` Builds a Hydrogen storefront for production. @@ -1212,6 +1286,8 @@ DESCRIPTION Builds a Hydrogen storefront for production. ``` +_See code: [dist/commands/hydrogen/build.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/build.js)_ + ## `shopify hydrogen check RESOURCE` Returns diagnostic information about a Hydrogen storefront. @@ -1231,6 +1307,8 @@ DESCRIPTION Returns diagnostic information about a Hydrogen storefront. ``` +_See code: [dist/commands/hydrogen/check.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/check.js)_ + ## `shopify hydrogen codegen` Generate types for the Storefront API queries found in your project. @@ -1250,6 +1328,8 @@ DESCRIPTION Generate types for the Storefront API queries found in your project. ``` +_See code: [dist/commands/hydrogen/codegen.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/codegen.js)_ + ## `shopify hydrogen customer-account-push` Push project configuration to admin @@ -1274,6 +1354,8 @@ DESCRIPTION Push project configuration to admin ``` +_See code: [dist/commands/hydrogen/customer-account-push.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/customer-account-push.js)_ + ## `shopify hydrogen debug cpu` Builds and profiles the server startup time the app. @@ -1293,6 +1375,8 @@ DESCRIPTION Builds and profiles the server startup time the app. ``` +_See code: [dist/commands/hydrogen/debug/cpu.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/debug/cpu.js)_ + ## `shopify hydrogen deploy` Builds and deploys a Hydrogen storefront to Oxygen. @@ -1348,6 +1432,8 @@ DESCRIPTION Builds and deploys a Hydrogen storefront to Oxygen. ``` +_See code: [dist/commands/hydrogen/deploy.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/deploy.js)_ + ## `shopify hydrogen dev` Runs Hydrogen storefront in an Oxygen worker for development. @@ -1393,6 +1479,8 @@ DESCRIPTION Runs Hydrogen storefront in an Oxygen worker for development. ``` +_See code: [dist/commands/hydrogen/dev.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/dev.js)_ + ## `shopify hydrogen env list` List the environments on your linked Hydrogen storefront. @@ -1409,6 +1497,8 @@ DESCRIPTION List the environments on your linked Hydrogen storefront. ``` +_See code: [dist/commands/hydrogen/env/list.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/env/list.js)_ + ## `shopify hydrogen env pull` Populate your .env with variables from your Hydrogen storefront. @@ -1433,6 +1523,8 @@ DESCRIPTION Populate your .env with variables from your Hydrogen storefront. ``` +_See code: [dist/commands/hydrogen/env/pull.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/env/pull.js)_ + ## `shopify hydrogen env push` Push environment variables from the local .env file to your linked Hydrogen storefront. @@ -1453,6 +1545,8 @@ DESCRIPTION Push environment variables from the local .env file to your linked Hydrogen storefront. ``` +_See code: [dist/commands/hydrogen/env/push.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/env/push.js)_ + ## `shopify hydrogen generate route ROUTENAME` Generates a standard Shopify route. @@ -1482,6 +1576,8 @@ DESCRIPTION Generates a standard Shopify route. ``` +_See code: [dist/commands/hydrogen/generate/route.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/generate/route.js)_ + ## `shopify hydrogen generate routes` Generates all supported standard shopify routes. @@ -1505,6 +1601,8 @@ DESCRIPTION Generates all supported standard shopify routes. ``` +_See code: [dist/commands/hydrogen/generate/routes.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/generate/routes.js)_ + ## `shopify hydrogen init` Creates a new Hydrogen storefront. @@ -1539,6 +1637,8 @@ DESCRIPTION Creates a new Hydrogen storefront. ``` +_See code: [dist/commands/hydrogen/init.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/init.js)_ + ## `shopify hydrogen link` Link a local project to one of your shop's Hydrogen storefronts. @@ -1558,6 +1658,8 @@ DESCRIPTION Link a local project to one of your shop's Hydrogen storefronts. ``` +_See code: [dist/commands/hydrogen/link.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/link.js)_ + ## `shopify hydrogen list` Returns a list of Hydrogen storefronts available on a given shop. @@ -1574,6 +1676,8 @@ DESCRIPTION Returns a list of Hydrogen storefronts available on a given shop. ``` +_See code: [dist/commands/hydrogen/list.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/list.js)_ + ## `shopify hydrogen login` Login to your Shopify account. @@ -1592,6 +1696,8 @@ DESCRIPTION Login to your Shopify account. ``` +_See code: [dist/commands/hydrogen/login.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/login.js)_ + ## `shopify hydrogen logout` Logout of your local session. @@ -1608,6 +1714,8 @@ DESCRIPTION Logout of your local session. ``` +_See code: [dist/commands/hydrogen/logout.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/logout.js)_ + ## `shopify hydrogen preview` Runs a Hydrogen storefront in an Oxygen worker for production. @@ -1645,6 +1753,8 @@ DESCRIPTION Runs a Hydrogen storefront in an Oxygen worker for production. ``` +_See code: [dist/commands/hydrogen/preview.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/preview.js)_ + ## `shopify hydrogen setup` Scaffold routes and core functionality. @@ -1669,6 +1779,8 @@ DESCRIPTION Scaffold routes and core functionality. ``` +_See code: [dist/commands/hydrogen/setup.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/setup.js)_ + ## `shopify hydrogen setup css [STRATEGY]` Setup CSS strategies for your project. @@ -1693,6 +1805,8 @@ DESCRIPTION Setup CSS strategies for your project. ``` +_See code: [dist/commands/hydrogen/setup/css.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/setup/css.js)_ + ## `shopify hydrogen setup markets [STRATEGY]` Setup support for multiple markets in your project. @@ -1713,6 +1827,8 @@ DESCRIPTION Setup support for multiple markets in your project. ``` +_See code: [dist/commands/hydrogen/setup/markets.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/setup/markets.js)_ + ## `shopify hydrogen setup vite` EXPERIMENTAL: Upgrades the project to use Vite. @@ -1729,6 +1845,8 @@ DESCRIPTION EXPERIMENTAL: Upgrades the project to use Vite. ``` +_See code: [dist/commands/hydrogen/setup/vite.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/setup/vite.js)_ + ## `shopify hydrogen shortcut` Creates a global `h2` shortcut for the Hydrogen CLI @@ -1741,6 +1859,8 @@ DESCRIPTION Creates a global `h2` shortcut for the Hydrogen CLI ``` +_See code: [dist/commands/hydrogen/shortcut.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/shortcut.js)_ + ## `shopify hydrogen unlink` Unlink a local project from a Hydrogen storefront. @@ -1757,6 +1877,8 @@ DESCRIPTION Unlink a local project from a Hydrogen storefront. ``` +_See code: [dist/commands/hydrogen/unlink.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/unlink.js)_ + ## `shopify hydrogen upgrade` Upgrade Remix and Hydrogen npm dependencies. @@ -1775,6 +1897,8 @@ DESCRIPTION Upgrade Remix and Hydrogen npm dependencies. ``` +_See code: [dist/commands/hydrogen/upgrade.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/hydrogen/upgrade.js)_ + ## `shopify organization list` List Shopify organizations you have access to. @@ -1794,6 +1918,8 @@ DESCRIPTION Lists the Shopify organizations that you have access to, along with their organization IDs. ``` +_See code: [dist/commands/organization/list.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/organization/list.js)_ + ## `shopify plugins add PLUGIN` Installs a plugin into shopify. @@ -1856,6 +1982,8 @@ EXAMPLES $ shopify plugins inspect myplugin ``` +_See code: [dist/commands/plugins/inspect.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/plugins/inspect.js)_ + ## `shopify plugins install PLUGIN` Installs a plugin into shopify. @@ -1893,6 +2021,8 @@ EXAMPLES $ shopify plugins install someuser/someplugin ``` +_See code: [dist/commands/plugins/install.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/plugins/install.js)_ + ## `shopify plugins link PATH` Links a plugin into the CLI for development. @@ -1922,6 +2052,8 @@ EXAMPLES $ shopify plugins link myplugin ``` +_See code: [dist/commands/plugins/link.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/plugins/link.js)_ + ## `shopify plugins remove [PLUGIN]` Removes a plugin from the CLI. @@ -1961,6 +2093,8 @@ FLAGS --reinstall Reinstall all plugins after uninstalling. ``` +_See code: [dist/commands/plugins/reset.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/plugins/reset.js)_ + ## `shopify plugins uninstall [PLUGIN]` Removes a plugin from the CLI. @@ -1987,6 +2121,8 @@ EXAMPLES $ shopify plugins uninstall myplugin ``` +_See code: [dist/commands/plugins/uninstall.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/plugins/uninstall.js)_ + ## `shopify plugins unlink [PLUGIN]` Removes a plugin from the CLI. @@ -2029,6 +2165,8 @@ DESCRIPTION Update installed plugins. ``` +_See code: [dist/commands/plugins/update.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/plugins/update.js)_ + ## `shopify search [query]` Starts a search on shopify.dev. @@ -2049,6 +2187,8 @@ EXAMPLES shopify search "" ``` +_See code: [dist/commands/search.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/search.js)_ + ## `shopify theme check` Validate the theme. @@ -2088,6 +2228,8 @@ DESCRIPTION (https://shopify.dev/docs/themes/tools/theme-check/checks) ``` +_See code: [dist/commands/theme/check.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/check.js)_ + ## `shopify theme console` Shopify Liquid REPL (read-eval-print loop) tool @@ -2120,6 +2262,8 @@ DESCRIPTION You can also provide context to the console using a URL, as some Liquid objects are context-specific ``` +_See code: [dist/commands/theme/console.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/console.js)_ + ## `shopify theme delete` Delete remote themes from the connected store. This command can't be undone. @@ -2156,6 +2300,8 @@ DESCRIPTION confirmation using the `--force` flag. ``` +_See code: [dist/commands/theme/delete.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/delete.js)_ + ## `shopify theme dev` Uploads the current theme as a development theme to the connected store, then prints theme editor and preview URLs to your terminal. While running, changes will push to the store in real time. @@ -2278,6 +2424,8 @@ DESCRIPTION (https://shopify.dev/docs/themes/tools/cli#directory-structure). ``` +_See code: [dist/commands/theme/dev.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/dev.js)_ + ## `shopify theme duplicate` Duplicates a theme from your theme library. @@ -2339,6 +2487,8 @@ DESCRIPTION ``` ``` +_See code: [dist/commands/theme/duplicate.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/duplicate.js)_ + ## `shopify theme info` Displays information about your theme environment, including your current store. Can also retrieve information about a specific theme. @@ -2367,6 +2517,8 @@ DESCRIPTION specific theme. ``` +_See code: [dist/commands/theme/info.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/info.js)_ + ## `shopify theme init [name] [flags]` Clones a Git repository to use as a starting point for building a new theme. @@ -2401,6 +2553,8 @@ DESCRIPTION (https://shopify.dev/docs/themes/store/requirements#uniqueness) so that it provides added value for users. ``` +_See code: [dist/commands/theme/init.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/init.js)_ + ## `shopify theme language-server` Start a Language Server Protocol server. @@ -2419,6 +2573,8 @@ DESCRIPTION Starts the "Language Server" (https://shopify.dev/docs/themes/tools/cli/language-server). ``` +_See code: [dist/commands/theme/language-server.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/language-server.js)_ + ## `shopify theme list` Lists the themes in your store, along with their IDs and statuses. @@ -2448,6 +2604,8 @@ DESCRIPTION Lists the themes in your store, along with their IDs and statuses. ``` +_See code: [dist/commands/theme/list.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/list.js)_ + ## `shopify theme metafields pull` Download metafields definitions from your shop into a local file. @@ -2476,6 +2634,8 @@ DESCRIPTION If the metafields file already exists, it will be overwritten. ``` +_See code: [dist/commands/theme/metafields/pull.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/metafields/pull.js)_ + ## `shopify theme open` Opens the preview of your remote theme. @@ -2514,6 +2674,8 @@ DESCRIPTION store. ``` +_See code: [dist/commands/theme/open.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/open.js)_ + ## `shopify theme package` Package your theme into a .zip file, ready to upload to the Online Store. @@ -2544,6 +2706,8 @@ DESCRIPTION (https://shopify.dev/docs/storefronts/themes/architecture/config/settings-schema-json) file. ``` +_See code: [dist/commands/theme/package.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/package.js)_ + ## `shopify theme preview` Applies JSON overrides to a theme and returns a preview URL. @@ -2579,6 +2743,8 @@ DESCRIPTION to update an existing preview instead of creating a new one. ``` +_See code: [dist/commands/theme/preview.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/preview.js)_ + ## `shopify theme profile` Profile the Liquid rendering of a theme page. @@ -2613,6 +2779,8 @@ DESCRIPTION page. ``` +_See code: [dist/commands/theme/profile.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/profile.js)_ + ## `shopify theme publish` Set a remote theme as the live theme. @@ -2650,6 +2818,8 @@ DESCRIPTION you want to publish the specified theme. You can skip this confirmation using the `--force` flag. ``` +_See code: [dist/commands/theme/publish.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/publish.js)_ + ## `shopify theme pull` Download your remote theme files locally. @@ -2686,6 +2856,8 @@ DESCRIPTION If no theme is specified, then you're prompted to select the theme to pull from the list of the themes in your store. ``` +_See code: [dist/commands/theme/pull.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/pull.js)_ + ## `shopify theme push` Uploads your local theme files to the connected store, overwriting the remote version if specified. @@ -2765,6 +2937,8 @@ DESCRIPTION ``` ``` +_See code: [dist/commands/theme/push.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/push.js)_ + ## `shopify theme rename` Renames an existing theme. @@ -2798,6 +2972,8 @@ DESCRIPTION your store. ``` +_See code: [dist/commands/theme/rename.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/rename.js)_ + ## `shopify theme share` Creates a shareable, unpublished, and new theme on your theme library with a randomized name. @@ -2830,6 +3006,8 @@ DESCRIPTION share with others. ``` +_See code: [dist/commands/theme/share.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/theme/share.js)_ + ## `shopify upgrade` Shows details on how to upgrade Shopify CLI. @@ -2844,6 +3022,8 @@ DESCRIPTION Shows details on how to upgrade Shopify CLI. ``` +_See code: [dist/commands/upgrade.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/upgrade.js)_ + ## `shopify version` Shopify CLI version currently installed. @@ -2855,4 +3035,6 @@ USAGE DESCRIPTION Shopify CLI version currently installed. ``` + +_See code: [dist/commands/version.js](https://github.com/Shopify/cli/blob/v3.92.0/dist/commands/version.js)_ diff --git a/packages/cli/bin/dev.js b/packages/cli/bin/dev.js index da29908c490..02df9f257f1 100755 --- a/packages/cli/bin/dev.js +++ b/packages/cli/bin/dev.js @@ -1,4 +1,4 @@ -import runCLI from '../dist/index.js' +import runCLI from '../dist/bootstrap.js' process.removeAllListeners('warning') diff --git a/packages/cli/bin/run.js b/packages/cli/bin/run.js index 6cc0c0bc3cc..dec351662d8 100755 --- a/packages/cli/bin/run.js +++ b/packages/cli/bin/run.js @@ -1,7 +1,11 @@ #!/usr/bin/env node -import runCLI from '../dist/index.js' +// eslint-disable-next-line n/no-unsupported-features/node-builtins +import {enableCompileCache} from 'node:module' + +enableCompileCache() process.removeAllListeners('warning') +const {default: runCLI} = await import('../dist/bootstrap.js') runCLI({development: false}) diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index ba23f85d8b5..8240e7285a3 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -5,7 +5,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "This command executes the build script specified in the element's TOML file. You can specify a custom script in the file. To learn about configuration files in Shopify apps, refer to \"App configuration\" (https://shopify.dev/docs/apps/tools/cli/configuration).\n\n If you're building a \"theme app extension\" (https://shopify.dev/docs/apps/online-store/theme-app-extensions), then running the `build` command runs \"Theme Check\" (https://shopify.dev/docs/themes/tools/theme-check) against your extension to ensure that it's valid.", "descriptionWithMarkdown": "This command executes the build script specified in the element's TOML file. You can specify a custom script in the file. To learn about configuration files in Shopify apps, refer to [App configuration](https://shopify.dev/docs/apps/tools/cli/configuration).\n\n If you're building a [theme app extension](https://shopify.dev/docs/apps/online-store/theme-app-extensions), then running the `build` command runs [Theme Check](https://shopify.dev/docs/themes/tools/theme-check) against your extension to ensure that it's valid.", "flags": { @@ -80,9 +79,16 @@ "hiddenAliases": [ ], "id": "app:build", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "build.js" + ], "strict": true, "summary": "Build the app, including extensions." }, @@ -91,7 +97,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Cancels a running bulk operation by ID.", "flags": { "client-id": { @@ -175,9 +180,17 @@ "hiddenAliases": [ ], "id": "app:bulk:cancel", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "bulk", + "cancel.js" + ], "strict": true, "summary": "Cancel a bulk operation." }, @@ -186,7 +199,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Executes an Admin API GraphQL query or mutation on the specified store, as a bulk operation. Mutations are only allowed on dev stores.\n\n Bulk operations allow you to process large amounts of data asynchronously. Learn more about \"bulk query operations\" (https://shopify.dev/docs/api/usage/bulk-operations/queries) and \"bulk mutation operations\" (https://shopify.dev/docs/api/usage/bulk-operations/imports).\n\n Use \"`bulk status`\" (https://shopify.dev/docs/api/shopify-cli/app/app-bulk-status) to check the status of your bulk operations.", "descriptionWithMarkdown": "Executes an Admin API GraphQL query or mutation on the specified store, as a bulk operation. Mutations are only allowed on dev stores.\n\n Bulk operations allow you to process large amounts of data asynchronously. Learn more about [bulk query operations](https://shopify.dev/docs/api/usage/bulk-operations/queries) and [bulk mutation operations](https://shopify.dev/docs/api/usage/bulk-operations/imports).\n\n Use [`bulk status`](https://shopify.dev/docs/api/shopify-cli/app/app-bulk-status) to check the status of your bulk operations.", "flags": { @@ -329,9 +341,17 @@ "hiddenAliases": [ ], "id": "app:bulk:execute", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "bulk", + "execute.js" + ], "strict": true, "summary": "Execute bulk operations." }, @@ -340,7 +360,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Check the status of a specific bulk operation by ID, or list all bulk operations belonging to this app on this store in the last 7 days.\n\n Bulk operations allow you to process large amounts of data asynchronously. Learn more about \"bulk query operations\" (https://shopify.dev/docs/api/usage/bulk-operations/queries) and \"bulk mutation operations\" (https://shopify.dev/docs/api/usage/bulk-operations/imports).\n\n Use \"`bulk execute`\" (https://shopify.dev/docs/api/shopify-cli/app/app-bulk-execute) to start a new bulk operation.", "descriptionWithMarkdown": "Check the status of a specific bulk operation by ID, or list all bulk operations belonging to this app on this store in the last 7 days.\n\n Bulk operations allow you to process large amounts of data asynchronously. Learn more about [bulk query operations](https://shopify.dev/docs/api/usage/bulk-operations/queries) and [bulk mutation operations](https://shopify.dev/docs/api/usage/bulk-operations/imports).\n\n Use [`bulk execute`](https://shopify.dev/docs/api/shopify-cli/app/app-bulk-execute) to start a new bulk operation.", "flags": { @@ -424,9 +443,17 @@ "hiddenAliases": [ ], "id": "app:bulk:status", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "bulk", + "status.js" + ], "strict": true, "summary": "Check the status of bulk operations." }, @@ -435,7 +462,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Pulls app configuration from the Developer Dashboard and creates or overwrites a configuration file. You can create a new app with this command to start with a default configuration file.\n\n For more information on the format of the created TOML configuration file, refer to the \"App configuration\" (https://shopify.dev/docs/apps/tools/cli/configuration) page.\n ", "descriptionWithMarkdown": "Pulls app configuration from the Developer Dashboard and creates or overwrites a configuration file. You can create a new app with this command to start with a default configuration file.\n\n For more information on the format of the created TOML configuration file, refer to the [App configuration](https://shopify.dev/docs/apps/tools/cli/configuration) page.\n ", "flags": { @@ -502,9 +528,17 @@ "hiddenAliases": [ ], "id": "app:config:link", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "config", + "link.js" + ], "strict": true, "summary": "Fetch your app configuration from the Developer Dashboard." }, @@ -513,7 +547,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Pulls the latest configuration from the already-linked Shopify app and updates the selected configuration file.\n\nThis command reuses the existing linked app and organization and skips all interactive prompts. Use `--config` to target a specific configuration file, or omit it to use the default one.", "descriptionWithMarkdown": "Pulls the latest configuration from the already-linked Shopify app and updates the selected configuration file.\n\nThis command reuses the existing linked app and organization and skips all interactive prompts. Use `--config` to target a specific configuration file, or omit it to use the default one.", "flags": { @@ -580,9 +613,17 @@ "hiddenAliases": [ ], "id": "app:config:pull", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "config", + "pull.js" + ], "strict": true, "summary": "Refresh an already-linked app configuration without prompts." }, @@ -595,7 +636,6 @@ "name": "config" } }, - "customPluginName": "@shopify/app", "description": "Sets default configuration when you run app-related CLI commands. If you omit the `config-name` parameter, then you'll be prompted to choose from the configuration files in your project.", "descriptionWithMarkdown": "Sets default configuration when you run app-related CLI commands. If you omit the `config-name` parameter, then you'll be prompted to choose from the configuration files in your project.", "flags": { @@ -652,9 +692,17 @@ "hiddenAliases": [ ], "id": "app:config:use", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "config", + "use.js" + ], "strict": true, "summary": "Activate an app configuration.", "usage": "app config use [config] [flags]" @@ -664,7 +712,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Validates the selected app configuration file and all extension configurations against their schemas and reports any errors found.", "descriptionWithMarkdown": "Validates the selected app configuration file and all extension configurations against their schemas and reports any errors found.", "flags": { @@ -740,9 +787,17 @@ "hiddenAliases": [ ], "id": "app:config:validate", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "config", + "validate.js" + ], "strict": true, "summary": "Validate your app configuration and extensions." }, @@ -751,7 +806,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "\"Builds the app\" (https://shopify.dev/docs/api/shopify-cli/app/app-build), then deploys your app configuration and extensions.\n\n This command creates an app version, which is a snapshot of your app configuration and all extensions. This version is then released to users.\n\n This command doesn't deploy your \"web app\" (https://shopify.dev/docs/apps/tools/cli/structure#web-components). You need to \"deploy your web app\" (https://shopify.dev/docs/apps/deployment/web) to your own hosting solution.\n ", "descriptionWithMarkdown": "[Builds the app](https://shopify.dev/docs/api/shopify-cli/app/app-build), then deploys your app configuration and extensions.\n\n This command creates an app version, which is a snapshot of your app configuration and all extensions. This version is then released to users.\n\n This command doesn't deploy your [web app](https://shopify.dev/docs/apps/tools/cli/structure#web-components). You need to [deploy your web app](https://shopify.dev/docs/apps/deployment/web) to your own hosting solution.\n ", "flags": { @@ -889,9 +943,16 @@ "hiddenAliases": [ ], "id": "app:deploy", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "deploy.js" + ], "strict": true, "summary": "Deploy your Shopify app." }, @@ -900,7 +961,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Builds and previews your app on a dev store, and watches for changes. \"Read more about testing apps locally\" (https://shopify.dev/docs/apps/build/cli-for-apps/test-apps-locally).", "descriptionWithMarkdown": "Builds and previews your app on a dev store, and watches for changes. [Read more about testing apps locally](https://shopify.dev/docs/apps/build/cli-for-apps/test-apps-locally).", "flags": { @@ -1078,9 +1138,16 @@ "hiddenAliases": [ ], "id": "app:dev", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "dev.js" + ], "strict": true, "summary": "Run the app." }, @@ -1089,7 +1156,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Stop the dev preview that was started with `shopify app dev`.\n\n It restores the app's active version to the selected development store.\n ", "descriptionWithMarkdown": "Stop the dev preview that was started with `shopify app dev`.\n\n It restores the app's active version to the selected development store.\n ", "flags": { @@ -1166,9 +1232,17 @@ "hiddenAliases": [ ], "id": "app:dev:clean", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "dev", + "clean.js" + ], "strict": true, "summary": "Cleans up the dev preview from the selected store." }, @@ -1177,7 +1251,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Creates or updates an `.env` files that contains app and app extension environment variables.\n\n When an existing `.env` file is updated, changes to the variables are displayed in the terminal output. Existing variables and commented variables are preserved.", "descriptionWithMarkdown": "Creates or updates an `.env` files that contains app and app extension environment variables.\n\n When an existing `.env` file is updated, changes to the variables are displayed in the terminal output. Existing variables and commented variables are preserved.", "flags": { @@ -1253,9 +1326,17 @@ "hiddenAliases": [ ], "id": "app:env:pull", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "env", + "pull.js" + ], "strict": true, "summary": "Pull app and extensions environment variables." }, @@ -1264,7 +1345,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Displays environment variables that can be used to deploy apps and app extensions.", "descriptionWithMarkdown": "Displays environment variables that can be used to deploy apps and app extensions.", "flags": { @@ -1331,9 +1411,17 @@ "hiddenAliases": [ ], "id": "app:env:show", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "env", + "show.js" + ], "strict": true, "summary": "Display app and extensions environment variables." }, @@ -1342,7 +1430,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Executes an Admin API GraphQL query or mutation on the specified store. Mutations are only allowed on dev stores.\n\n For operations that process large amounts of data, use \"`bulk execute`\" (https://shopify.dev/docs/api/shopify-cli/app/app-bulk-execute) instead.", "descriptionWithMarkdown": "Executes an Admin API GraphQL query or mutation on the specified store. Mutations are only allowed on dev stores.\n\n For operations that process large amounts of data, use [`bulk execute`](https://shopify.dev/docs/api/shopify-cli/app/app-bulk-execute) instead.", "flags": { @@ -1475,9 +1562,16 @@ "hiddenAliases": [ ], "id": "app:execute", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "execute.js" + ], "strict": true, "summary": "Execute GraphQL queries and mutations." }, @@ -1486,7 +1580,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Compiles the function in your current directory to WebAssembly (Wasm) for testing purposes.", "descriptionWithMarkdown": "Compiles the function in your current directory to WebAssembly (Wasm) for testing purposes.", "flags": { @@ -1554,9 +1647,17 @@ "hiddenAliases": [ ], "id": "app:function:build", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "function", + "build.js" + ], "strict": true, "summary": "Compile a function to wasm." }, @@ -1565,7 +1666,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "The information returned includes the following:\n\n - The function handle\n - The function name\n - The function API version\n - The targeting configuration\n - The schema path\n - The WASM path\n - The function runner path", "descriptionWithMarkdown": "The information returned includes the following:\n\n - The function handle\n - The function name\n - The function API version\n - The targeting configuration\n - The schema path\n - The WASM path\n - The function runner path", "flags": { @@ -1642,9 +1742,17 @@ "hiddenAliases": [ ], "id": "app:function:info", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "function", + "info.js" + ], "strict": true, "summary": "Print basic information about your function." }, @@ -1653,7 +1761,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Runs the function from your current directory for \"testing purposes\" (https://shopify.dev/docs/apps/functions/testing-and-debugging). To learn how you can monitor and debug functions when errors occur, refer to \"Shopify Functions error handling\" (https://shopify.dev/docs/api/functions/errors).", "descriptionWithMarkdown": "Runs the function from your current directory for [testing purposes](https://shopify.dev/docs/apps/functions/testing-and-debugging). To learn how you can monitor and debug functions when errors occur, refer to [Shopify Functions error handling](https://shopify.dev/docs/api/functions/errors).", "flags": { @@ -1748,9 +1855,17 @@ "hiddenAliases": [ ], "id": "app:function:replay", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "function", + "replay.js" + ], "strict": true, "summary": "Replays a function run from an app log." }, @@ -1759,7 +1874,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Runs the function from your current directory for \"testing purposes\" (https://shopify.dev/docs/apps/functions/testing-and-debugging). To learn how you can monitor and debug functions when errors occur, refer to \"Shopify Functions error handling\" (https://shopify.dev/docs/api/functions/errors).", "descriptionWithMarkdown": "Runs the function from your current directory for [testing purposes](https://shopify.dev/docs/apps/functions/testing-and-debugging). To learn how you can monitor and debug functions when errors occur, refer to [Shopify Functions error handling](https://shopify.dev/docs/api/functions/errors).", "flags": { @@ -1855,9 +1969,17 @@ "hiddenAliases": [ ], "id": "app:function:run", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "function", + "run.js" + ], "strict": true, "summary": "Run a function locally for testing." }, @@ -1866,7 +1988,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Generates the latest \"GraphQL schema\" (https://shopify.dev/docs/apps/functions/input-output#graphql-schema) for a function in your app. Run this command from the function directory.\n\n This command uses the API type and version of your function, as defined in your extension TOML file, to generate the latest GraphQL schema. The schema is written to the `schema.graphql` file.", "descriptionWithMarkdown": "Generates the latest [GraphQL schema](https://shopify.dev/docs/apps/functions/input-output#graphql-schema) for a function in your app. Run this command from the function directory.\n\n This command uses the API type and version of your function, as defined in your extension TOML file, to generate the latest GraphQL schema. The schema is written to the `schema.graphql` file.", "flags": { @@ -1942,9 +2063,17 @@ "hiddenAliases": [ ], "id": "app:function:schema", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "function", + "schema.js" + ], "strict": true, "summary": "Fetch the latest GraphQL schema for a function." }, @@ -1953,7 +2082,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Creates GraphQL types based on your \"input query\" (https://shopify.dev/docs/apps/functions/input-output#input) for a function. Supports JavaScript functions out of the box, or any language via the `build.typegen_command` configuration.", "descriptionWithMarkdown": "Creates GraphQL types based on your [input query](https://shopify.dev/docs/apps/functions/input-output#input) for a function. Supports JavaScript functions out of the box, or any language via the `build.typegen_command` configuration.", "flags": { @@ -2021,9 +2149,17 @@ "hiddenAliases": [ ], "id": "app:function:typegen", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "function", + "typegen.js" + ], "strict": true, "summary": "Generate GraphQL types for a function." }, @@ -2032,7 +2168,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Generates a new \"app extension\" (https://shopify.dev/docs/apps/build/app-extensions). For a list of app extensions that you can generate using this command, refer to \"Supported extensions\" (https://shopify.dev/docs/apps/build/app-extensions/list-of-app-extensions).\n\n Each new app extension is created in a folder under `extensions/`. To learn more about the extensions file structure, refer to \"App structure\" (https://shopify.dev/docs/apps/build/cli-for-apps/app-structure) and the documentation for your extension.\n ", "descriptionWithMarkdown": "Generates a new [app extension](https://shopify.dev/docs/apps/build/app-extensions). For a list of app extensions that you can generate using this command, refer to [Supported extensions](https://shopify.dev/docs/apps/build/app-extensions/list-of-app-extensions).\n\n Each new app extension is created in a folder under `extensions/`. To learn more about the extensions file structure, refer to [App structure](https://shopify.dev/docs/apps/build/cli-for-apps/app-structure) and the documentation for your extension.\n ", "flags": { @@ -2156,9 +2291,17 @@ "hiddenAliases": [ ], "id": "app:generate:extension", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "generate", + "extension.js" + ], "strict": true, "summary": "Generate a new app Extension." }, @@ -2167,7 +2310,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "\"DEPRECATED, use `app function schema`] Generates the latest [GraphQL schema\" (https://shopify.dev/docs/apps/functions/input-output#graphql-schema) for a function in your app. Run this command from the function directory.\n\n This command uses the API type and version of your function, as defined in your extension TOML file, to generate the latest GraphQL schema. The schema is written to the `schema.graphql` file.", "descriptionWithMarkdown": "[DEPRECATED, use `app function schema`] Generates the latest [GraphQL schema](https://shopify.dev/docs/apps/functions/input-output#graphql-schema) for a function in your app. Run this command from the function directory.\n\n This command uses the API type and version of your function, as defined in your extension TOML file, to generate the latest GraphQL schema. The schema is written to the `schema.graphql` file.", "flags": { @@ -2244,9 +2386,17 @@ "hiddenAliases": [ ], "id": "app:generate:schema", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "generate", + "schema.js" + ], "summary": "Fetch the latest GraphQL schema for a function." }, "app:import-custom-data-definitions": { @@ -2254,7 +2404,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Import metafield and metaobject definitions from your development store. \"Read more about declarative custom data definitions\" (https://shopify.dev/docs/apps/build/custom-data/declarative-custom-data-definitions).", "descriptionWithMarkdown": "Import metafield and metaobject definitions from your development store. [Read more about declarative custom data definitions](https://shopify.dev/docs/apps/build/custom-data/declarative-custom-data-definitions).", "flags": { @@ -2337,9 +2486,16 @@ "hiddenAliases": [ ], "id": "app:import-custom-data-definitions", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "import-custom-data-definitions.js" + ], "strict": true, "summary": "Import metafield and metaobject definitions." }, @@ -2348,7 +2504,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Import dashboard-managed extensions into your app.", "flags": { "client-id": { @@ -2414,9 +2569,16 @@ "hiddenAliases": [ ], "id": "app:import-extensions", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "import-extensions.js" + ], "strict": true }, "app:info": { @@ -2424,7 +2586,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "The information returned includes the following:\n\n - The app and dev store that's used when you run the \"dev\" (https://shopify.dev/docs/api/shopify-cli/app/app-dev) command. You can reset these configurations using \"`dev --reset`\" (https://shopify.dev/docs/api/shopify-cli/app/app-dev#flags-propertydetail-reset).\n - The \"structure\" (https://shopify.dev/docs/apps/tools/cli/structure) of your app project.\n - The \"access scopes\" (https://shopify.dev/docs/api/usage) your app has requested.\n - System information, including the package manager and version of Shopify CLI used in the project.", "descriptionWithMarkdown": "The information returned includes the following:\n\n - The app and dev store that's used when you run the [dev](https://shopify.dev/docs/api/shopify-cli/app/app-dev) command. You can reset these configurations using [`dev --reset`](https://shopify.dev/docs/api/shopify-cli/app/app-dev#flags-propertydetail-reset).\n - The [structure](https://shopify.dev/docs/apps/tools/cli/structure) of your app project.\n - The [access scopes](https://shopify.dev/docs/api/usage) your app has requested.\n - System information, including the package manager and version of Shopify CLI used in the project.", "flags": { @@ -2508,9 +2669,16 @@ "hiddenAliases": [ ], "id": "app:info", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "info.js" + ], "strict": true, "summary": "Print basic information about your app and extensions." }, @@ -2519,7 +2687,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "flags": { "client-id": { "description": "The Client ID of your app. Use this to automatically link your new project to an existing app. Using this flag avoids the app selection prompt.", @@ -2625,9 +2792,16 @@ "hiddenAliases": [ ], "id": "app:init", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "init.js" + ], "strict": true, "summary": "Create a new app project" }, @@ -2636,7 +2810,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "\n Opens a real-time stream of detailed app logs from the selected app and store.\n Use the `--source` argument to limit output to a particular log source, such as a specific Shopify Function handle. Use the `shopify app logs sources` command to view a list of sources.\n Use the `--status` argument to filter on status, either `success` or `failure`.\n ```\n shopify app logs --status=success --source=extension.discount-function\n ```\n ", "descriptionWithMarkdown": "\n Opens a real-time stream of detailed app logs from the selected app and store.\n Use the `--source` argument to limit output to a particular log source, such as a specific Shopify Function handle. Use the `shopify app logs sources` command to view a list of sources.\n Use the `--status` argument to filter on status, either `success` or `failure`.\n ```\n shopify app logs --status=success --source=extension.discount-function\n ```\n ", "flags": { @@ -2741,9 +2914,16 @@ "hiddenAliases": [ ], "id": "app:logs", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "logs.js" + ], "strict": true, "summary": "Stream detailed logs for your Shopify app." }, @@ -2752,7 +2932,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "The output source names can be used with the `--source` argument of `shopify app logs` to filter log output. Currently only function extensions are supported as sources.", "descriptionWithMarkdown": "The output source names can be used with the `--source` argument of `shopify app logs` to filter log output. Currently only function extensions are supported as sources.", "flags": { @@ -2819,9 +2998,17 @@ "hiddenAliases": [ ], "id": "app:logs:sources", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "logs", + "sources.js" + ], "strict": true, "summary": "Print out a list of sources that may be used with the logs command." }, @@ -2830,7 +3017,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Releases an existing app version. Pass the name of the version that you want to release using the `--version` flag.", "descriptionWithMarkdown": "Releases an existing app version. Pass the name of the version that you want to release using the `--version` flag.", "flags": { @@ -2932,9 +3118,16 @@ "hiddenAliases": [ ], "id": "app:release", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "release.js" + ], "strict": true, "summary": "Release an app version.", "usage": "app release --version " @@ -2944,7 +3137,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Lists the deployed app versions. An app version is a snapshot of your app extensions.", "descriptionWithMarkdown": "Lists the deployed app versions. An app version is a snapshot of your app extensions.", "flags": { @@ -3020,9 +3212,17 @@ "hiddenAliases": [ ], "id": "app:versions:list", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "versions", + "list.js" + ], "strict": true, "summary": "List deployed versions of your app." }, @@ -3031,7 +3231,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "\n Triggers the delivery of a sample Admin API event topic payload to a designated address.\n\n You should use this command to experiment with webhooks, to initially test your webhook configuration, or for unit testing. However, to test your webhook configuration from end to end, you should always trigger webhooks by performing the related action in Shopify.\n\n Because most webhook deliveries use remote endpoints, you can trigger the command from any directory where you can use Shopify CLI, and send the webhook to any of the supported endpoint types. For example, you can run the command from your app's local directory, but send the webhook to a staging environment endpoint.\n\n To learn more about using webhooks in a Shopify app, refer to \"Webhooks overview\" (https://shopify.dev/docs/apps/webhooks).\n\n ### Limitations\n\n - Webhooks triggered using this method always have the same payload, so they can't be used to test scenarios that differ based on the payload contents.\n - Webhooks triggered using this method aren't retried when they fail.\n - Trigger requests are rate-limited using the \"Partner API rate limit\" (https://shopify.dev/docs/api/partner#rate_limits).\n - You can't use this method to validate your API webhook subscriptions.\n ", "descriptionWithMarkdown": "\n Triggers the delivery of a sample Admin API event topic payload to a designated address.\n\n You should use this command to experiment with webhooks, to initially test your webhook configuration, or for unit testing. However, to test your webhook configuration from end to end, you should always trigger webhooks by performing the related action in Shopify.\n\n Because most webhook deliveries use remote endpoints, you can trigger the command from any directory where you can use Shopify CLI, and send the webhook to any of the supported endpoint types. For example, you can run the command from your app's local directory, but send the webhook to a staging environment endpoint.\n\n To learn more about using webhooks in a Shopify app, refer to [Webhooks overview](https://shopify.dev/docs/apps/webhooks).\n\n ### Limitations\n\n - Webhooks triggered using this method always have the same payload, so they can't be used to test scenarios that differ based on the payload contents.\n - Webhooks triggered using this method aren't retried when they fail.\n - Trigger requests are rate-limited using the [Partner API rate limit](https://shopify.dev/docs/api/partner#rate_limits).\n - You can't use this method to validate your API webhook subscriptions.\n ", "flags": { @@ -3156,9 +3355,17 @@ "hiddenAliases": [ ], "id": "app:webhook:trigger", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "app", + "webhook", + "trigger.js" + ], "strict": true, "summary": "Trigger delivery of a sample webhook topic payload to a designated address." }, @@ -3183,9 +3390,16 @@ "hiddenAliases": [ ], "id": "auth:login", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "auth", + "login.js" + ], "strict": true }, "auth:logout": { @@ -3201,9 +3415,16 @@ "hiddenAliases": [ ], "id": "auth:logout", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "auth", + "logout.js" + ], "strict": true }, "cache:clear": { @@ -3220,9 +3441,16 @@ "hiddenAliases": [ ], "id": "cache:clear", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "cache", + "clear.js" + ], "strict": true }, "commands": { @@ -3230,7 +3458,6 @@ ], "args": { }, - "customPluginName": "@oclif/plugin-commands", "description": "List all <%= config.bin %> commands.", "enableJsonFlag": true, "flags": { @@ -3318,9 +3545,15 @@ "hiddenAliases": [ ], "id": "commands", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "commands.js" + ], "strict": true }, "config:autocorrect:off": { @@ -3328,7 +3561,6 @@ ], "args": { }, - "customPluginName": "@shopify/plugin-did-you-mean", "description": "Disable autocorrect. Off by default.\n\n When autocorrection is enabled, Shopify CLI automatically runs a corrected version of your command if a correction is available.\n\n When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands.\n", "descriptionWithMarkdown": "Disable autocorrect. Off by default.\n\n When autocorrection is enabled, Shopify CLI automatically runs a corrected version of your command if a correction is available.\n\n When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands.\n", "enableJsonFlag": false, @@ -3338,9 +3570,17 @@ "hiddenAliases": [ ], "id": "config:autocorrect:off", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "config", + "autocorrect", + "off.js" + ], "strict": true, "summary": "Disable autocorrect. Off by default." }, @@ -3349,7 +3589,6 @@ ], "args": { }, - "customPluginName": "@shopify/plugin-did-you-mean", "description": "Enable autocorrect. Off by default.\n\n When autocorrection is enabled, Shopify CLI automatically runs a corrected version of your command if a correction is available.\n\n When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands.\n", "descriptionWithMarkdown": "Enable autocorrect. Off by default.\n\n When autocorrection is enabled, Shopify CLI automatically runs a corrected version of your command if a correction is available.\n\n When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands.\n", "enableJsonFlag": false, @@ -3359,9 +3598,17 @@ "hiddenAliases": [ ], "id": "config:autocorrect:on", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "config", + "autocorrect", + "on.js" + ], "strict": true, "summary": "Enable autocorrect. Off by default." }, @@ -3370,7 +3617,6 @@ ], "args": { }, - "customPluginName": "@shopify/plugin-did-you-mean", "description": "Check whether autocorrect is enabled or disabled. On by default.\n\n When autocorrection is enabled, Shopify CLI automatically runs a corrected version of your command if a correction is available.\n\n When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands.\n", "descriptionWithMarkdown": "Check whether autocorrect is enabled or disabled. On by default.\n\n When autocorrection is enabled, Shopify CLI automatically runs a corrected version of your command if a correction is available.\n\n When autocorrection is disabled, you need to confirm that you want to run corrections for mistyped commands.\n", "enableJsonFlag": false, @@ -3380,9 +3626,17 @@ "hiddenAliases": [ ], "id": "config:autocorrect:status", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "config", + "autocorrect", + "status.js" + ], "strict": true, "summary": "Check whether autocorrect is enabled or disabled. On by default." }, @@ -3407,9 +3661,16 @@ "hiddenAliases": [ ], "id": "debug:command-flags", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "debug", + "command-flags.js" + ], "strict": true }, "demo:watcher": { @@ -3417,7 +3678,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "flags": { "client-id": { "description": "The Client ID of your app.", @@ -3483,9 +3743,16 @@ "hiddenAliases": [ ], "id": "demo:watcher", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "demo", + "watcher.js" + ], "strict": true, "summary": "Watch and prints out changes to an app." }, @@ -3503,9 +3770,16 @@ "hiddenAliases": [ ], "id": "docs:generate", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "docs", + "generate.js" + ], "strict": true }, "doctor-release": { @@ -3522,9 +3796,16 @@ "hiddenAliases": [ ], "id": "doctor-release", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "doctor-release", + "index.js" + ], "strict": true }, "doctor-release:theme": { @@ -3594,9 +3875,17 @@ "hiddenAliases": [ ], "id": "doctor-release:theme", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "doctor-release", + "theme", + "index.js" + ], "strict": true }, "help": { @@ -3625,9 +3914,15 @@ "hiddenAliases": [ ], "id": "help", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "help.js" + ], "strict": false, "usage": "help [command] [flags]" }, @@ -3636,7 +3931,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Builds a Hydrogen storefront for production.", "descriptionWithMarkdown": "Builds a Hydrogen storefront for production. The client and app worker files are compiled to a `/dist` folder in your Hydrogen project directory.", "enableJsonFlag": false, @@ -3721,9 +4015,16 @@ "hiddenAliases": [ ], "id": "hydrogen:build", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "build.js" + ], "strict": true }, "hydrogen:check": { @@ -3739,7 +4040,6 @@ "required": true } }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Returns diagnostic information about a Hydrogen storefront.", "descriptionWithMarkdown": "Checks whether your Hydrogen app includes a set of standard Shopify routes.", "enableJsonFlag": false, @@ -3757,9 +4057,16 @@ "hiddenAliases": [ ], "id": "hydrogen:check", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "check.js" + ], "strict": true }, "hydrogen:codegen": { @@ -3767,7 +4074,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Generate types for the Storefront API queries found in your project.", "descriptionWithMarkdown": "Automatically generates GraphQL types for your project’s Storefront API queries.", "enableJsonFlag": false, @@ -3808,9 +4114,16 @@ "hiddenAliases": [ ], "id": "hydrogen:codegen", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "codegen.js" + ], "strict": true }, "hydrogen:customer-account-push": { @@ -3818,7 +4131,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Push project configuration to admin", "enableJsonFlag": false, "flags": { @@ -3864,9 +4176,16 @@ "hiddenAliases": [ ], "id": "hydrogen:customer-account-push", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "customer-account-push.js" + ], "strict": true }, "hydrogen:debug:cpu": { @@ -3874,7 +4193,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Builds and profiles the server startup time the app.", "descriptionWithMarkdown": "Builds the app and runs the resulting code to profile the server startup time, watching for changes. This command can be used to [debug slow app startup times](https://shopify.dev/docs/custom-storefronts/hydrogen/debugging/cpu-startup) that cause failed deployments in Oxygen.\n\n The profiling results are written to a `.cpuprofile` file that can be viewed with certain tools such as [Flame Chart Visualizer for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-js-profile-flame).", "enableJsonFlag": false, @@ -3909,9 +4227,17 @@ "hiddenAliases": [ ], "id": "hydrogen:debug:cpu", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "debug", + "cpu.js" + ], "strict": true }, "hydrogen:deploy": { @@ -3919,7 +4245,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Builds and deploys a Hydrogen storefront to Oxygen.", "descriptionWithMarkdown": "Builds and deploys your Hydrogen storefront to Oxygen. Requires an Oxygen deployment token to be set with the `--token` flag or an environment variable (`SHOPIFY_HYDROGEN_DEPLOYMENT_TOKEN`). If the storefront is [linked](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-link) then the Oxygen deployment token for the linked storefront will be used automatically.", "enableJsonFlag": false, @@ -4102,9 +4427,16 @@ "hiddenAliases": [ ], "id": "hydrogen:deploy", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "deploy.js" + ], "strict": true }, "hydrogen:dev": { @@ -4112,7 +4444,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Runs Hydrogen storefront in an Oxygen worker for development.", "descriptionWithMarkdown": "Runs a Hydrogen storefront in a local runtime that emulates an Oxygen worker for development.\n\n If your project is [linked](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-link) to a Hydrogen storefront, then its environment variables will be loaded with the runtime.", "enableJsonFlag": false, @@ -4255,9 +4586,16 @@ "hiddenAliases": [ ], "id": "hydrogen:dev", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "dev.js" + ], "strict": true }, "hydrogen:env:list": { @@ -4265,7 +4603,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "List the environments on your linked Hydrogen storefront.", "descriptionWithMarkdown": "Lists all environments available on the linked Hydrogen storefront.", "enableJsonFlag": false, @@ -4283,9 +4620,17 @@ "hiddenAliases": [ ], "id": "hydrogen:env:list", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "env", + "list.js" + ], "strict": true }, "hydrogen:env:pull": { @@ -4293,7 +4638,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Populate your .env with variables from your Hydrogen storefront.", "descriptionWithMarkdown": "Pulls environment variables from the linked Hydrogen storefront and writes them to an `.env` file.", "enableJsonFlag": false, @@ -4350,9 +4694,17 @@ "hiddenAliases": [ ], "id": "hydrogen:env:pull", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "env", + "pull.js" + ], "strict": true }, "hydrogen:env:push": { @@ -4360,7 +4712,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Push environment variables from the local .env file to your linked Hydrogen storefront.", "enableJsonFlag": false, "flags": { @@ -4396,9 +4747,17 @@ "hiddenAliases": [ ], "id": "hydrogen:env:push", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "env", + "push.js" + ], "strict": true }, "hydrogen:g": { @@ -4406,7 +4765,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Shortcut for `hydrogen generate`. See `hydrogen generate --help` for more information.", "enableJsonFlag": false, "flags": { @@ -4416,9 +4774,16 @@ "hiddenAliases": [ ], "id": "hydrogen:g", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "g.js" + ], "strict": false }, "hydrogen:generate:route": { @@ -4446,7 +4811,6 @@ "required": true } }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Generates a standard Shopify route.", "descriptionWithMarkdown": "Generates a set of default routes from the starter template.", "enableJsonFlag": false, @@ -4495,9 +4859,17 @@ "hiddenAliases": [ ], "id": "hydrogen:generate:route", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "generate", + "route.js" + ], "strict": true }, "hydrogen:generate:routes": { @@ -4505,7 +4877,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Generates all supported standard shopify routes.", "enableJsonFlag": false, "flags": { @@ -4553,9 +4924,17 @@ "hiddenAliases": [ ], "id": "hydrogen:generate:routes", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "generate", + "routes.js" + ], "strict": true }, "hydrogen:init": { @@ -4563,7 +4942,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Creates a new Hydrogen storefront.", "descriptionWithMarkdown": "Creates a new Hydrogen storefront.", "enableJsonFlag": false, @@ -4670,9 +5048,16 @@ "hiddenAliases": [ ], "id": "hydrogen:init", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "init.js" + ], "strict": true }, "hydrogen:link": { @@ -4680,7 +5065,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Link a local project to one of your shop's Hydrogen storefronts.", "descriptionWithMarkdown": "Links your local development environment to a remote Hydrogen storefront. You can link an unlimited number of development environments to a single Hydrogen storefront.\n\n Linking to a Hydrogen storefront enables you to run [dev](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-dev) and automatically inject your linked Hydrogen storefront's environment variables directly into the server runtime.\n\n After you run the `link` command, you can access the [env list](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-env-list), [env pull](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-env-pull), and [unlink](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-unlink) commands.", "enableJsonFlag": false, @@ -4714,9 +5098,16 @@ "hiddenAliases": [ ], "id": "hydrogen:link", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "link.js" + ], "strict": true }, "hydrogen:list": { @@ -4724,7 +5115,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Returns a list of Hydrogen storefronts available on a given shop.", "descriptionWithMarkdown": "Lists all remote Hydrogen storefronts available to link to your local development environment.", "enableJsonFlag": false, @@ -4742,9 +5132,16 @@ "hiddenAliases": [ ], "id": "hydrogen:list", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "list.js" + ], "strict": true }, "hydrogen:login": { @@ -4752,7 +5149,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Login to your Shopify account.", "descriptionWithMarkdown": "Logs in to the specified shop and saves the shop domain to the project.", "enableJsonFlag": false, @@ -4779,9 +5175,16 @@ "hiddenAliases": [ ], "id": "hydrogen:login", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "login.js" + ], "strict": true }, "hydrogen:logout": { @@ -4789,7 +5192,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Logout of your local session.", "descriptionWithMarkdown": "Log out from the current shop.", "enableJsonFlag": false, @@ -4807,9 +5209,16 @@ "hiddenAliases": [ ], "id": "hydrogen:logout", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "logout.js" + ], "strict": true }, "hydrogen:preview": { @@ -4817,7 +5226,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Runs a Hydrogen storefront in an Oxygen worker for production.", "descriptionWithMarkdown": "Runs a server in your local development environment that serves your Hydrogen app's production build. Requires running the [build](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-build) command first.", "enableJsonFlag": false, @@ -4944,9 +5352,16 @@ "hiddenAliases": [ ], "id": "hydrogen:preview", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "preview.js" + ], "strict": true }, "hydrogen:setup": { @@ -4954,7 +5369,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Scaffold routes and core functionality.", "enableJsonFlag": false, "flags": { @@ -5001,9 +5415,16 @@ "hiddenAliases": [ ], "id": "hydrogen:setup", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "setup.js" + ], "strict": true }, "hydrogen:setup:css": { @@ -5021,7 +5442,6 @@ ] } }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Setup CSS strategies for your project.", "descriptionWithMarkdown": "Adds support for certain CSS strategies to your project.", "enableJsonFlag": false, @@ -5054,9 +5474,17 @@ "hiddenAliases": [ ], "id": "hydrogen:setup:css", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "setup", + "css.js" + ], "strict": true }, "hydrogen:setup:markets": { @@ -5073,7 +5501,6 @@ ] } }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Setup support for multiple markets in your project.", "descriptionWithMarkdown": "Adds support for multiple [markets](https://shopify.dev/docs/custom-storefronts/hydrogen/markets) to your project by using the URL structure.", "enableJsonFlag": false, @@ -5091,9 +5518,17 @@ "hiddenAliases": [ ], "id": "hydrogen:setup:markets", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "setup", + "markets.js" + ], "strict": true }, "hydrogen:setup:vite": { @@ -5101,7 +5536,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "EXPERIMENTAL: Upgrades the project to use Vite.", "enableJsonFlag": false, "flags": { @@ -5118,9 +5552,17 @@ "hiddenAliases": [ ], "id": "hydrogen:setup:vite", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "setup", + "vite.js" + ], "strict": true }, "hydrogen:shortcut": { @@ -5128,7 +5570,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Creates a global `h2` shortcut for the Hydrogen CLI", "descriptionWithMarkdown": "Creates a global h2 shortcut for Shopify CLI using shell aliases.\n\n The following shells are supported:\n\n - Bash (using `~/.bashrc`)\n - ZSH (using `~/.zshrc`)\n - Fish (using `~/.config/fish/functions`)\n - PowerShell (added to `$PROFILE`)\n\n After the alias is created, you can call Shopify CLI from anywhere in your project using `h2 `.", "enableJsonFlag": false, @@ -5138,9 +5579,16 @@ "hiddenAliases": [ ], "id": "hydrogen:shortcut", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "shortcut.js" + ], "strict": true }, "hydrogen:unlink": { @@ -5148,7 +5596,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Unlink a local project from a Hydrogen storefront.", "descriptionWithMarkdown": "Unlinks your local development environment from a remote Hydrogen storefront.", "enableJsonFlag": false, @@ -5166,9 +5613,16 @@ "hiddenAliases": [ ], "id": "hydrogen:unlink", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "unlink.js" + ], "strict": true }, "hydrogen:upgrade": { @@ -5176,7 +5630,6 @@ ], "args": { }, - "customPluginName": "@shopify/cli-hydrogen", "description": "Upgrade Remix and Hydrogen npm dependencies.", "descriptionWithMarkdown": "Upgrade Hydrogen project dependencies, preview features, fixes and breaking changes. The command also generates an instruction file for each upgrade.", "enableJsonFlag": false, @@ -5211,9 +5664,16 @@ "hiddenAliases": [ ], "id": "hydrogen:upgrade", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "hydrogen", + "upgrade.js" + ], "strict": true }, "kitchen-sink": { @@ -5231,9 +5691,16 @@ "kitchen-sink all" ], "id": "kitchen-sink", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "kitchen-sink", + "index.js" + ], "strict": true }, "kitchen-sink:async": { @@ -5250,9 +5717,16 @@ "hiddenAliases": [ ], "id": "kitchen-sink:async", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "kitchen-sink", + "async.js" + ], "strict": true }, "kitchen-sink:prompts": { @@ -5269,9 +5743,16 @@ "hiddenAliases": [ ], "id": "kitchen-sink:prompts", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "kitchen-sink", + "prompts.js" + ], "strict": true }, "kitchen-sink:static": { @@ -5288,9 +5769,16 @@ "hiddenAliases": [ ], "id": "kitchen-sink:static", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "kitchen-sink", + "static.js" + ], "strict": true }, "notifications:generate": { @@ -5307,9 +5795,16 @@ "hiddenAliases": [ ], "id": "notifications:generate", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "notifications", + "generate.js" + ], "strict": true }, "notifications:list": { @@ -5334,9 +5829,16 @@ "hiddenAliases": [ ], "id": "notifications:list", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "notifications", + "list.js" + ], "strict": true }, "organization:list": { @@ -5344,7 +5846,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "Lists the Shopify organizations that you have access to, along with their organization IDs.", "descriptionWithMarkdown": "Lists the Shopify organizations that you have access to, along with their organization IDs.", "enableJsonFlag": false, @@ -5379,9 +5880,16 @@ "hiddenAliases": [ ], "id": "organization:list", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "organization", + "list.js" + ], "strict": true, "summary": "List Shopify organizations you have access to." }, @@ -5390,7 +5898,6 @@ ], "args": { }, - "customPluginName": "@oclif/plugin-plugins", "description": "List installed plugins.", "enableJsonFlag": true, "examples": [ @@ -5416,9 +5923,16 @@ "hiddenAliases": [ ], "id": "plugins", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "plugins", + "index.js" + ], "strict": true }, "plugins:inspect": { @@ -5432,7 +5946,6 @@ "required": true } }, - "customPluginName": "@oclif/plugin-plugins", "description": "Displays installation properties of a plugin.", "enableJsonFlag": true, "examples": [ @@ -5464,9 +5977,16 @@ "hiddenAliases": [ ], "id": "plugins:inspect", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "plugins", + "inspect.js" + ], "strict": false, "usage": "plugins:inspect PLUGIN..." }, @@ -5481,7 +6001,6 @@ "required": true } }, - "customPluginName": "@oclif/plugin-plugins", "description": "", "enableJsonFlag": true, "examples": [ @@ -5551,9 +6070,16 @@ "hiddenAliases": [ ], "id": "plugins:install", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "plugins", + "install.js" + ], "strict": false, "summary": "Installs a plugin into <%= config.bin %>." }, @@ -5568,7 +6094,6 @@ "required": true } }, - "customPluginName": "@oclif/plugin-plugins", "description": "Installation of a linked plugin will override a user-installed or core plugin.\n\ne.g. If you have a user-installed or core plugin that has a 'hello' command, installing a linked plugin with a 'hello' command will override the user-installed or core plugin implementation. This is useful for development work.\n", "enableJsonFlag": false, "examples": [ @@ -5599,9 +6124,16 @@ "hiddenAliases": [ ], "id": "plugins:link", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "plugins", + "link.js" + ], "strict": true, "summary": "Links a plugin into the CLI for development." }, @@ -5610,7 +6142,6 @@ ], "args": { }, - "customPluginName": "@oclif/plugin-plugins", "enableJsonFlag": false, "flags": { "hard": { @@ -5630,9 +6161,16 @@ "hiddenAliases": [ ], "id": "plugins:reset", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "plugins", + "reset.js" + ], "strict": true, "summary": "Remove all user-installed and linked plugins." }, @@ -5647,7 +6185,6 @@ "name": "plugin" } }, - "customPluginName": "@oclif/plugin-plugins", "description": "Removes a plugin from the CLI.", "enableJsonFlag": false, "examples": [ @@ -5672,9 +6209,16 @@ "hiddenAliases": [ ], "id": "plugins:uninstall", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "plugins", + "uninstall.js" + ], "strict": false }, "plugins:update": { @@ -5682,7 +6226,6 @@ ], "args": { }, - "customPluginName": "@oclif/plugin-plugins", "description": "Update installed plugins.", "enableJsonFlag": false, "flags": { @@ -5704,9 +6247,16 @@ "hiddenAliases": [ ], "id": "plugins:update", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "plugins", + "update.js" + ], "strict": true }, "search": { @@ -5728,9 +6278,15 @@ "hiddenAliases": [ ], "id": "search", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "search.js" + ], "strict": true, "usage": "search [query]" }, @@ -5739,7 +6295,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Calls and runs \"Theme Check\" (https://shopify.dev/docs/themes/tools/theme-check) to analyze your theme code for errors and to ensure that it follows theme and Liquid best practices. \"Learn more about the checks that Theme Check runs.\" (https://shopify.dev/docs/themes/tools/theme-check/checks)", "descriptionWithMarkdown": "Calls and runs [Theme Check](https://shopify.dev/docs/themes/tools/theme-check) to analyze your theme code for errors and to ensure that it follows theme and Liquid best practices. [Learn more about the checks that Theme Check runs.](https://shopify.dev/docs/themes/tools/theme-check/checks)", "flags": { @@ -5867,12 +6422,19 @@ "hiddenAliases": [ ], "id": "theme:check", + "isESM": true, "multiEnvironmentsFlags": [ "path" ], "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "check.js" + ], "strict": true, "summary": "Validate the theme." }, @@ -5881,7 +6443,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Starts the Shopify Liquid REPL (read-eval-print loop) tool. This tool provides an interactive terminal interface for evaluating Liquid code and exploring Liquid objects, filters, and tags using real store data.\n\n You can also provide context to the console using a URL, as some Liquid objects are context-specific", "descriptionWithMarkdown": "Starts the Shopify Liquid REPL (read-eval-print loop) tool. This tool provides an interactive terminal interface for evaluating Liquid code and exploring Liquid objects, filters, and tags using real store data.\n\n You can also provide context to the console using a URL, as some Liquid objects are context-specific", "flags": { @@ -5958,10 +6519,17 @@ "hiddenAliases": [ ], "id": "theme:console", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "console.js" + ], "strict": true, "summary": "Shopify Liquid REPL (read-eval-print loop) tool", "usage": [ @@ -5974,7 +6542,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Deletes a theme from your store.\n\n You can specify multiple themes by ID. If no theme is specified, then you're prompted to select the theme that you want to delete from the list of themes in your store.\n\n You're asked to confirm that you want to delete the specified themes before they are deleted. You can skip this confirmation using the `--force` flag.", "descriptionWithMarkdown": "Deletes a theme from your store.\n\n You can specify multiple themes by ID. If no theme is specified, then you're prompted to select the theme that you want to delete from the list of themes in your store.\n\n You're asked to confirm that you want to delete the specified themes before they are deleted. You can skip this confirmation using the `--force` flag.", "flags": { @@ -6067,6 +6634,7 @@ "hiddenAliases": [ ], "id": "theme:delete", + "isESM": true, "multiEnvironmentsFlags": [ "store", "password", @@ -6078,6 +6646,12 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "delete.js" + ], "strict": true, "summary": "Delete remote themes from the connected store. This command can't be undone." }, @@ -6086,7 +6660,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "\n Uploads the current theme as the specified theme, or a \"development theme\" (https://shopify.dev/docs/themes/tools/cli#development-themes), to a store so you can preview it.\n\nThis command returns the following information:\n\n- A link to your development theme at http://127.0.0.1:9292. This URL can hot reload local changes to CSS and sections, or refresh the entire page when a file changes, enabling you to preview changes in real time using the store's data.\n\n You can specify a different network interface and port using `--host` and `--port`.\n\n- A link to the \"editor\" (https://shopify.dev/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n\n- A \"preview link\" (https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with other developers.\n\nIf you already have a development theme for your current environment, then this command replaces the development theme with your local theme. You can override this using the `--theme-editor-sync` flag.\n\n> Note: You can't preview checkout customizations using http://127.0.0.1:9292.\n\nDevelopment themes are deleted when you run `shopify auth logout`. If you need a preview link that can be used after you log out, then you should \"share\" (https://shopify.dev/docs/api/shopify-cli/theme/theme-share) your theme or \"push\" (https://shopify.dev/docs/api/shopify-cli/theme/theme-push) to an unpublished theme on your store.\n\nYou can run this command only in a directory that matches the \"default Shopify theme folder structure\" (https://shopify.dev/docs/themes/tools/cli#directory-structure).", "descriptionWithMarkdown": "\n Uploads the current theme as the specified theme, or a [development theme](https://shopify.dev/docs/themes/tools/cli#development-themes), to a store so you can preview it.\n\nThis command returns the following information:\n\n- A link to your development theme at http://127.0.0.1:9292. This URL can hot reload local changes to CSS and sections, or refresh the entire page when a file changes, enabling you to preview changes in real time using the store's data.\n\n You can specify a different network interface and port using `--host` and `--port`.\n\n- A link to the [editor](https://shopify.dev/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n\n- A [preview link](https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with other developers.\n\nIf you already have a development theme for your current environment, then this command replaces the development theme with your local theme. You can override this using the `--theme-editor-sync` flag.\n\n> Note: You can't preview checkout customizations using http://127.0.0.1:9292.\n\nDevelopment themes are deleted when you run `shopify auth logout`. If you need a preview link that can be used after you log out, then you should [share](https://shopify.dev/docs/api/shopify-cli/theme/theme-share) your theme or [push](https://shopify.dev/docs/api/shopify-cli/theme/theme-push) to an unpublished theme on your store.\n\nYou can run this command only in a directory that matches the [default Shopify theme folder structure](https://shopify.dev/docs/themes/tools/cli#directory-structure).", "flags": { @@ -6287,10 +6860,17 @@ "hiddenAliases": [ ], "id": "theme:dev", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "dev.js" + ], "strict": true, "summary": "Uploads the current theme as a development theme to the connected store, then prints theme editor and preview URLs to your terminal. While running, changes will push to the store in real time." }, @@ -6299,7 +6879,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "If you want to duplicate your local theme, you need to run `shopify theme push` first.\n\nIf no theme ID is specified, you're prompted to select the theme that you want to duplicate from the list of themes in your store. You're asked to confirm that you want to duplicate the specified theme.\n\nPrompts and confirmations are not shown when duplicate is run in a CI environment or the `--force` flag is used, therefore you must specify a theme ID using the `--theme` flag.\n\nYou can optionally name the duplicated theme using the `--name` flag.\n\nIf you use the `--json` flag, then theme information is returned in JSON format, which can be used as a machine-readable input for scripts or continuous integration.\n\nSample JSON output:\n\n```json\n{\n \"theme\": {\n \"id\": 108267175958,\n \"name\": \"A Duplicated Theme\",\n \"role\": \"unpublished\",\n \"shop\": \"mystore.myshopify.com\"\n }\n}\n```\n\n```json\n{\n \"message\": \"The theme 'Summer Edition' could not be duplicated due to errors\",\n \"errors\": [\"Maximum number of themes reached\"],\n \"requestId\": \"12345-abcde-67890\"\n}\n```", "descriptionWithMarkdown": "If you want to duplicate your local theme, you need to run `shopify theme push` first.\n\nIf no theme ID is specified, you're prompted to select the theme that you want to duplicate from the list of themes in your store. You're asked to confirm that you want to duplicate the specified theme.\n\nPrompts and confirmations are not shown when duplicate is run in a CI environment or the `--force` flag is used, therefore you must specify a theme ID using the `--theme` flag.\n\nYou can optionally name the duplicated theme using the `--name` flag.\n\nIf you use the `--json` flag, then theme information is returned in JSON format, which can be used as a machine-readable input for scripts or continuous integration.\n\nSample JSON output:\n\n```json\n{\n \"theme\": {\n \"id\": 108267175958,\n \"name\": \"A Duplicated Theme\",\n \"role\": \"unpublished\",\n \"shop\": \"mystore.myshopify.com\"\n }\n}\n```\n\n```json\n{\n \"message\": \"The theme 'Summer Edition' could not be duplicated due to errors\",\n \"errors\": [\"Maximum number of themes reached\"],\n \"requestId\": \"12345-abcde-67890\"\n}\n```", "flags": { @@ -6385,9 +6964,16 @@ "hiddenAliases": [ ], "id": "theme:duplicate", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "duplicate.js" + ], "strict": true, "summary": "Duplicates a theme from your theme library.", "usage": [ @@ -6400,7 +6986,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Displays information about your theme environment, including your current store. Can also retrieve information about a specific theme.", "flags": { "development": { @@ -6485,6 +7070,7 @@ "hiddenAliases": [ ], "id": "theme:info", + "isESM": true, "multiEnvironmentsFlags": [ "store", "password" @@ -6492,6 +7078,12 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "info.js" + ], "strict": true }, "theme:init": { @@ -6504,7 +7096,6 @@ "required": false } }, - "customPluginName": "@shopify/theme", "description": "Clones a Git repository to your local machine to use as the starting point for building a theme.\n\n If no Git repository is specified, then this command creates a copy of Shopify's \"Skeleton theme\" (https://github.com/Shopify/skeleton-theme.git), with the specified name in the current folder. If no name is provided, then you're prompted to enter one.\n\n > Caution: If you're building a theme for the Shopify Theme Store, then you can use our example theme as a starting point. However, the theme that you submit needs to be \"substantively different from existing themes\" (https://shopify.dev/docs/themes/store/requirements#uniqueness) so that it provides added value for users.\n ", "descriptionWithMarkdown": "Clones a Git repository to your local machine to use as the starting point for building a theme.\n\n If no Git repository is specified, then this command creates a copy of Shopify's [Skeleton theme](https://github.com/Shopify/skeleton-theme.git), with the specified name in the current folder. If no name is provided, then you're prompted to enter one.\n\n > Caution: If you're building a theme for the Shopify Theme Store, then you can use our example theme as a starting point. However, the theme that you submit needs to be [substantively different from existing themes](https://shopify.dev/docs/themes/store/requirements#uniqueness) so that it provides added value for users.\n ", "flags": { @@ -6556,10 +7147,17 @@ "hiddenAliases": [ ], "id": "theme:init", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "init.js" + ], "strict": true, "summary": "Clones a Git repository to use as a starting point for building a new theme.", "usage": "theme init [name] [flags]" @@ -6569,7 +7167,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Starts the \"Language Server\" (https://shopify.dev/docs/themes/tools/cli/language-server).", "descriptionWithMarkdown": "Starts the [Language Server](https://shopify.dev/docs/themes/tools/cli/language-server).", "flags": { @@ -6594,10 +7191,17 @@ "hiddenAliases": [ ], "id": "theme:language-server", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "language-server.js" + ], "strict": true, "summary": "Start a Language Server Protocol server." }, @@ -6606,7 +7210,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Lists the themes in your store, along with their IDs and statuses.", "flags": { "environment": { @@ -6703,6 +7306,7 @@ "hiddenAliases": [ ], "id": "theme:list", + "isESM": true, "multiEnvironmentsFlags": [ "store", "password" @@ -6710,6 +7314,12 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "list.js" + ], "strict": true }, "theme:metafields:pull": { @@ -6717,7 +7327,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Retrieves metafields from Shopify Admin.\n\nIf the metafields file already exists, it will be overwritten.", "descriptionWithMarkdown": "Retrieves metafields from Shopify Admin.\n\nIf the metafields file already exists, it will be overwritten.", "flags": { @@ -6786,10 +7395,18 @@ "hiddenAliases": [ ], "id": "theme:metafields:pull", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "metafields", + "pull.js" + ], "strict": true, "summary": "Download metafields definitions from your shop into a local file." }, @@ -6798,7 +7415,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Returns links that let you preview the specified theme. The following links are returned:\n\n - A link to the \"editor\" (https://shopify.dev/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n - A \"preview link\" (https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with other developers.\n\n If you don't specify a theme, then you're prompted to select the theme to open from the list of the themes in your store.", "descriptionWithMarkdown": "Returns links that let you preview the specified theme. The following links are returned:\n\n - A link to the [editor](https://shopify.dev/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n - A [preview link](https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with other developers.\n\n If you don't specify a theme, then you're prompted to select the theme to open from the list of the themes in your store.", "flags": { @@ -6891,10 +7507,17 @@ "hiddenAliases": [ ], "id": "theme:open", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "open.js" + ], "strict": true, "summary": "Opens the preview of your remote theme." }, @@ -6903,7 +7526,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Packages your local theme files into a ZIP file that can be uploaded to Shopify.\n\n Only folders that match the \"default Shopify theme folder structure\" (https://shopify.dev/docs/storefronts/themes/tools/cli#directory-structure) are included in the package.\n\n The package includes the `listings` directory if present (required for multi-preset themes per \"Theme Store requirements\" (https://shopify.dev/docs/storefronts/themes/store/requirements#adding-presets-to-your-theme-zip-submission)).\n\n The ZIP file uses the name `theme_name-theme_version.zip`, based on parameters in your \"settings_schema.json\" (https://shopify.dev/docs/storefronts/themes/architecture/config/settings-schema-json) file.", "descriptionWithMarkdown": "Packages your local theme files into a ZIP file that can be uploaded to Shopify.\n\n Only folders that match the [default Shopify theme folder structure](https://shopify.dev/docs/storefronts/themes/tools/cli#directory-structure) are included in the package.\n\n The package includes the `listings` directory if present (required for multi-preset themes per [Theme Store requirements](https://shopify.dev/docs/storefronts/themes/store/requirements#adding-presets-to-your-theme-zip-submission)).\n\n The ZIP file uses the name `theme_name-theme_version.zip`, based on parameters in your [settings_schema.json](https://shopify.dev/docs/storefronts/themes/architecture/config/settings-schema-json) file.", "flags": { @@ -6937,10 +7559,17 @@ "hiddenAliases": [ ], "id": "theme:package", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "package.js" + ], "strict": true, "summary": "Package your theme into a .zip file, ready to upload to the Online Store." }, @@ -6949,7 +7578,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Applies a JSON overrides file to a theme and creates or updates a preview. This lets you quickly preview changes.\n\n The command returns a preview URL and a preview identifier. You can reuse the preview identifier with `--preview-id` to update an existing preview instead of creating a new one.", "descriptionWithMarkdown": "Applies a JSON overrides file to a theme and creates or updates a preview. This lets you quickly preview changes.\n\n The command returns a preview URL and a preview identifier. You can reuse the preview identifier with `--preview-id` to update an existing preview instead of creating a new one.", "flags": { @@ -7043,10 +7671,17 @@ "hiddenAliases": [ ], "id": "theme:preview", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "preview.js" + ], "strict": true, "summary": "Applies JSON overrides to a theme and returns a preview URL." }, @@ -7055,7 +7690,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Profile the Shopify Liquid on a given page.\n\n This command will open a web page with the Speedscope profiler detailing the time spent executing Liquid on the given page.", "descriptionWithMarkdown": "Profile the Shopify Liquid on a given page.\n\n This command will open a web page with the Speedscope profiler detailing the time spent executing Liquid on the given page.", "flags": { @@ -7150,10 +7784,17 @@ "hiddenAliases": [ ], "id": "theme:profile", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "profile.js" + ], "strict": true, "summary": "Profile the Liquid rendering of a theme page.", "usage": [ @@ -7166,7 +7807,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Publishes an unpublished theme from your theme library.\n\nIf no theme ID is specified, then you're prompted to select the theme that you want to publish from the list of themes in your store.\n\nYou can run this command only in a directory that matches the \"default Shopify theme folder structure\" (https://shopify.dev/docs/themes/tools/cli#directory-structure).\n\nIf you want to publish your local theme, then you need to run `shopify theme push` first. You're asked to confirm that you want to publish the specified theme. You can skip this confirmation using the `--force` flag.", "descriptionWithMarkdown": "Publishes an unpublished theme from your theme library.\n\nIf no theme ID is specified, then you're prompted to select the theme that you want to publish from the list of themes in your store.\n\nYou can run this command only in a directory that matches the [default Shopify theme folder structure](https://shopify.dev/docs/themes/tools/cli#directory-structure).\n\nIf you want to publish your local theme, then you need to run `shopify theme push` first. You're asked to confirm that you want to publish the specified theme. You can skip this confirmation using the `--force` flag.", "flags": { @@ -7243,6 +7883,7 @@ "hiddenAliases": [ ], "id": "theme:publish", + "isESM": true, "multiEnvironmentsFlags": [ "store", "password", @@ -7251,6 +7892,12 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "publish.js" + ], "strict": true, "summary": "Set a remote theme as the live theme." }, @@ -7259,7 +7906,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Retrieves theme files from Shopify.\n\nIf no theme is specified, then you're prompted to select the theme to pull from the list of the themes in your store.", "descriptionWithMarkdown": "Retrieves theme files from Shopify.\n\nIf no theme is specified, then you're prompted to select the theme to pull from the list of the themes in your store.", "flags": { @@ -7379,6 +8025,7 @@ "hiddenAliases": [ ], "id": "theme:pull", + "isESM": true, "multiEnvironmentsFlags": [ "store", "password", @@ -7392,6 +8039,12 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "pull.js" + ], "strict": true, "summary": "Download your remote theme files locally." }, @@ -7400,7 +8053,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Uploads your local theme files to Shopify, overwriting the remote version if specified.\n\n If no theme is specified, then you're prompted to select the theme to overwrite from the list of the themes in your store.\n\n You can run this command only in a directory that matches the \"default Shopify theme folder structure\" (https://shopify.dev/docs/themes/tools/cli#directory-structure).\n\n This command returns the following information:\n\n - A link to the \"editor\" (https://shopify.dev/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n - A \"preview link\" (https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with others.\n\n If you use the `--json` flag, then theme information is returned in JSON format, which can be used as a machine-readable input for scripts or continuous integration.\n\n Sample output:\n\n ```json\n {\n \"theme\": {\n \"id\": 108267175958,\n \"name\": \"MyTheme\",\n \"role\": \"unpublished\",\n \"shop\": \"mystore.myshopify.com\",\n \"editor_url\": \"https://mystore.myshopify.com/admin/themes/108267175958/editor\",\n \"preview_url\": \"https://mystore.myshopify.com/?preview_theme_id=108267175958\"\n }\n }\n ```\n ", "descriptionWithMarkdown": "Uploads your local theme files to Shopify, overwriting the remote version if specified.\n\n If no theme is specified, then you're prompted to select the theme to overwrite from the list of the themes in your store.\n\n You can run this command only in a directory that matches the [default Shopify theme folder structure](https://shopify.dev/docs/themes/tools/cli#directory-structure).\n\n This command returns the following information:\n\n - A link to the [editor](https://shopify.dev/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n - A [preview link](https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with others.\n\n If you use the `--json` flag, then theme information is returned in JSON format, which can be used as a machine-readable input for scripts or continuous integration.\n\n Sample output:\n\n ```json\n {\n \"theme\": {\n \"id\": 108267175958,\n \"name\": \"MyTheme\",\n \"role\": \"unpublished\",\n \"shop\": \"mystore.myshopify.com\",\n \"editor_url\": \"https://mystore.myshopify.com/admin/themes/108267175958/editor\",\n \"preview_url\": \"https://mystore.myshopify.com/?preview_theme_id=108267175958\"\n }\n }\n ```\n ", "flags": { @@ -7583,6 +8235,7 @@ "hiddenAliases": [ ], "id": "theme:push", + "isESM": true, "multiEnvironmentsFlags": [ "store", "password", @@ -7596,6 +8249,12 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "push.js" + ], "strict": true, "summary": "Uploads your local theme files to the connected store, overwriting the remote version if specified.", "usage": [ @@ -7608,7 +8267,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Renames a theme in your store.\n\n If no theme is specified, then you're prompted to select the theme that you want to rename from the list of themes in your store.\n ", "descriptionWithMarkdown": "Renames a theme in your store.\n\n If no theme is specified, then you're prompted to select the theme that you want to rename from the list of themes in your store.\n ", "flags": { @@ -7703,6 +8361,7 @@ "hiddenAliases": [ ], "id": "theme:rename", + "isESM": true, "multiEnvironmentsFlags": [ "store", "password", @@ -7716,6 +8375,12 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "rename.js" + ], "strict": true, "summary": "Renames an existing theme." }, @@ -7724,7 +8389,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "\n Uploads the current theme as the specified theme, or a \"development theme\" (https://shopify.dev/docs/themes/tools/cli#development-themes), to a store so you can preview it.\n\nThis command returns the following information:\n\n- A link to your development theme at http://127.0.0.1:9292. This URL can hot reload local changes to CSS and sections, or refresh the entire page when a file changes, enabling you to preview changes in real time using the store's data.\n\n You can specify a different network interface and port using `--host` and `--port`.\n\n- A link to the \"editor\" (https://shopify.dev/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n\n- A \"preview link\" (https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with other developers.\n\nIf you already have a development theme for your current environment, then this command replaces the development theme with your local theme. You can override this using the `--theme-editor-sync` flag.\n\n> Note: You can't preview checkout customizations using http://127.0.0.1:9292.\n\nDevelopment themes are deleted when you run `shopify auth logout`. If you need a preview link that can be used after you log out, then you should \"share\" (https://shopify.dev/docs/api/shopify-cli/theme/theme-share) your theme or \"push\" (https://shopify.dev/docs/api/shopify-cli/theme/theme-push) to an unpublished theme on your store.\n\nYou can run this command only in a directory that matches the \"default Shopify theme folder structure\" (https://shopify.dev/docs/themes/tools/cli#directory-structure).", "descriptionWithMarkdown": "\n Uploads the current theme as the specified theme, or a [development theme](https://shopify.dev/docs/themes/tools/cli#development-themes), to a store so you can preview it.\n\nThis command returns the following information:\n\n- A link to your development theme at http://127.0.0.1:9292. This URL can hot reload local changes to CSS and sections, or refresh the entire page when a file changes, enabling you to preview changes in real time using the store's data.\n\n You can specify a different network interface and port using `--host` and `--port`.\n\n- A link to the [editor](https://shopify.dev/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n\n- A [preview link](https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with other developers.\n\nIf you already have a development theme for your current environment, then this command replaces the development theme with your local theme. You can override this using the `--theme-editor-sync` flag.\n\n> Note: You can't preview checkout customizations using http://127.0.0.1:9292.\n\nDevelopment themes are deleted when you run `shopify auth logout`. If you need a preview link that can be used after you log out, then you should [share](https://shopify.dev/docs/api/shopify-cli/theme/theme-share) your theme or [push](https://shopify.dev/docs/api/shopify-cli/theme/theme-push) to an unpublished theme on your store.\n\nYou can run this command only in a directory that matches the [default Shopify theme folder structure](https://shopify.dev/docs/themes/tools/cli#directory-structure).", "flags": { @@ -7926,10 +8590,17 @@ "hiddenAliases": [ ], "id": "theme:serve", + "isESM": true, "multiEnvironmentsFlags": null, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "serve.js" + ], "summary": "Uploads the current theme as a development theme to the connected store, then prints theme editor and preview URLs to your terminal. While running, changes will push to the store in real time." }, "theme:share": { @@ -7937,7 +8608,6 @@ ], "args": { }, - "customPluginName": "@shopify/theme", "description": "Uploads your theme as a new, unpublished theme in your theme library. The theme is given a randomized name.\n\n This command returns a \"preview link\" (https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with others.", "descriptionWithMarkdown": "Uploads your theme as a new, unpublished theme in your theme library. The theme is given a randomized name.\n\n This command returns a [preview link](https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with others.", "flags": { @@ -8014,6 +8684,7 @@ "hiddenAliases": [ ], "id": "theme:share", + "isESM": true, "multiEnvironmentsFlags": [ "store", "password", @@ -8022,6 +8693,12 @@ "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "theme", + "share.js" + ], "strict": true, "summary": "Creates a shareable, unpublished, and new theme on your theme library with a randomized name." }, @@ -8039,9 +8716,15 @@ "hiddenAliases": [ ], "id": "upgrade", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "upgrade.js" + ], "strict": true, "summary": "Shows details on how to upgrade Shopify CLI." }, @@ -8058,9 +8741,15 @@ "hiddenAliases": [ ], "id": "version", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "version.js" + ], "strict": true }, "webhook:trigger": { @@ -8068,7 +8757,6 @@ ], "args": { }, - "customPluginName": "@shopify/app", "description": "\n Triggers the delivery of a sample Admin API event topic payload to a designated address.\n\n You should use this command to experiment with webhooks, to initially test your webhook configuration, or for unit testing. However, to test your webhook configuration from end to end, you should always trigger webhooks by performing the related action in Shopify.\n\n Because most webhook deliveries use remote endpoints, you can trigger the command from any directory where you can use Shopify CLI, and send the webhook to any of the supported endpoint types. For example, you can run the command from your app's local directory, but send the webhook to a staging environment endpoint.\n\n To learn more about using webhooks in a Shopify app, refer to \"Webhooks overview\" (https://shopify.dev/docs/apps/webhooks).\n\n ### Limitations\n\n - Webhooks triggered using this method always have the same payload, so they can't be used to test scenarios that differ based on the payload contents.\n - Webhooks triggered using this method aren't retried when they fail.\n - Trigger requests are rate-limited using the \"Partner API rate limit\" (https://shopify.dev/docs/api/partner#rate_limits).\n - You can't use this method to validate your API webhook subscriptions.\n ", "descriptionWithMarkdown": "\n Triggers the delivery of a sample Admin API event topic payload to a designated address.\n\n You should use this command to experiment with webhooks, to initially test your webhook configuration, or for unit testing. However, to test your webhook configuration from end to end, you should always trigger webhooks by performing the related action in Shopify.\n\n Because most webhook deliveries use remote endpoints, you can trigger the command from any directory where you can use Shopify CLI, and send the webhook to any of the supported endpoint types. For example, you can run the command from your app's local directory, but send the webhook to a staging environment endpoint.\n\n To learn more about using webhooks in a Shopify app, refer to [Webhooks overview](https://shopify.dev/docs/apps/webhooks).\n\n ### Limitations\n\n - Webhooks triggered using this method always have the same payload, so they can't be used to test scenarios that differ based on the payload contents.\n - Webhooks triggered using this method aren't retried when they fail.\n - Trigger requests are rate-limited using the [Partner API rate limit](https://shopify.dev/docs/api/partner#rate_limits).\n - You can't use this method to validate your API webhook subscriptions.\n ", "flags": { @@ -8194,9 +8882,16 @@ "hiddenAliases": [ ], "id": "webhook:trigger", + "isESM": true, "pluginAlias": "@shopify/cli", "pluginName": "@shopify/cli", "pluginType": "core", + "relativePath": [ + "dist", + "commands", + "webhook", + "trigger.js" + ], "summary": "Trigger delivery of a sample webhook topic payload to a designated address." } }, diff --git a/packages/cli/package.json b/packages/cli/package.json index bdae9faaf76..fcfb305b3c5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -86,9 +86,8 @@ "oclif": { "bin": "shopify", "commands": { - "strategy": "explicit", - "target": "./dist/index.js", - "identifier": "COMMANDS" + "strategy": "pattern", + "target": "./dist/commands" }, "scope": "shopify", "topicSeparator": " ", @@ -133,44 +132,20 @@ ], "hooks": { "init": [ - { - "target": "./dist/index.js", - "identifier": "AppInitHook" - }, - { - "target": "./dist/index.js", - "identifier": "HydrogenInitHook" - } + "./dist/hooks/app-init.js", + "./dist/hooks/hydrogen-init.js" ], "prerun": "./dist/hooks/prerun.js", "postrun": "./dist/hooks/postrun.js", - "command_not_found": { - "target": "./dist/index.js", - "identifier": "DidYouMeanHook" - }, - "tunnel_start": { - "target": "./dist/index.js", - "identifier": "TunnelStartHook" - }, - "tunnel_provider": { - "target": "./dist/index.js", - "identifier": "TunnelProviderHook" - }, - "update": { - "target": "./dist/index.js", - "identifier": "PluginHook" - }, + "command_not_found": "./dist/hooks/did-you-mean.js", + "tunnel_start": "./dist/hooks/tunnel-start.js", + "tunnel_provider": "./dist/hooks/tunnel-provider.js", + "update": "./dist/hooks/plugin-plugins.js", "sensitive_command_metadata": [ - { - "target": "./dist/index.js", - "identifier": "AppSensitiveMetadataHook" - } + "./dist/hooks/app-sensitive-metadata.js" ], "public_command_metadata": [ - { - "target": "./dist/index.js", - "identifier": "AppPublicMetadataHook" - } + "./dist/hooks/app-public-metadata.js" ] } } diff --git a/packages/cli/src/bootstrap.ts b/packages/cli/src/bootstrap.ts new file mode 100644 index 00000000000..f32dd900065 --- /dev/null +++ b/packages/cli/src/bootstrap.ts @@ -0,0 +1,67 @@ +/** + * Lightweight CLI bootstrap module. + * + * This file is the entry point for bin/dev.js and bin/run.js. + * It intentionally does NOT import any command modules or heavy packages. + * Commands are loaded lazily by oclif's pattern strategy from the commands/ directory. + */ +import {createGlobalProxyAgent} from 'global-agent' + +import fs from 'fs' + +// Setup global support for environment variable based proxy configuration. +createGlobalProxyAgent({ + environmentVariableNamespace: 'SHOPIFY_', + forceGlobalAgent: true, + socketConnectionTimeout: 60000, +}) + +// In some cases (for example when we boot the proxy server), when an exception is +// thrown, no 'exit' signal is sent to the process. We don't understand this fully. +// This means that any cleanup code that depends on "process.on('exit', ...)" will +// not be called. The tunnel plugin is an example of that. Here we make sure to print +// the error stack and manually call exit so that the cleanup code is called. This +// makes sure that there are no lingering tunnel processes. +// eslint-disable-next-line @typescript-eslint/no-misused-promises +process.on('uncaughtException', async (err) => { + try { + const {FatalError} = await import('@shopify/cli-kit/node/error') + if (err instanceof FatalError) { + const {renderFatalError} = await import('@shopify/cli-kit/node/ui') + renderFatalError(err) + } else { + fs.writeSync(process.stderr.fd, `${err.stack ?? err.message ?? err}\n`) + } + // eslint-disable-next-line no-catch-all/no-catch-all + } catch { + fs.writeSync(process.stderr.fd, `${err.stack ?? err.message ?? err}\n`) + } + process.exit(1) +}) +const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'] +signals.forEach((signal) => { + process.on(signal, () => { + process.exit(1) + }) +}) + +// Sometimes we want to specify a precise amount of stdout columns, for example in +// CI or on a cloud environment. +const columns = Number(process.env.SHOPIFY_CLI_COLUMNS) +if (!isNaN(columns)) { + process.stdout.columns = columns +} + +interface RunShopifyCLIOptions { + development: boolean +} + +async function runShopifyCLI({development}: RunShopifyCLIOptions) { + const {runCLI} = await import('@shopify/cli-kit/node/cli') + await runCLI({ + moduleURL: import.meta.url, + development, + }) +} + +export default runShopifyCLI diff --git a/packages/cli/src/commands/app/build.ts b/packages/cli/src/commands/app/build.ts new file mode 100644 index 00000000000..85a96259e9a --- /dev/null +++ b/packages/cli/src/commands/app/build.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:build'] as any diff --git a/packages/cli/src/commands/app/bulk/cancel.ts b/packages/cli/src/commands/app/bulk/cancel.ts new file mode 100644 index 00000000000..f46d3eb4c46 --- /dev/null +++ b/packages/cli/src/commands/app/bulk/cancel.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:bulk:cancel'] as any diff --git a/packages/cli/src/commands/app/bulk/execute.ts b/packages/cli/src/commands/app/bulk/execute.ts new file mode 100644 index 00000000000..e61cac09584 --- /dev/null +++ b/packages/cli/src/commands/app/bulk/execute.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:bulk:execute'] as any diff --git a/packages/cli/src/commands/app/bulk/status.ts b/packages/cli/src/commands/app/bulk/status.ts new file mode 100644 index 00000000000..7b3e4322439 --- /dev/null +++ b/packages/cli/src/commands/app/bulk/status.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:bulk:status'] as any diff --git a/packages/cli/src/commands/app/config/link.ts b/packages/cli/src/commands/app/config/link.ts new file mode 100644 index 00000000000..2ba5cedd1fb --- /dev/null +++ b/packages/cli/src/commands/app/config/link.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:config:link'] as any diff --git a/packages/cli/src/commands/app/config/pull.ts b/packages/cli/src/commands/app/config/pull.ts new file mode 100644 index 00000000000..b96b6e72605 --- /dev/null +++ b/packages/cli/src/commands/app/config/pull.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:config:pull'] as any diff --git a/packages/cli/src/commands/app/config/use.ts b/packages/cli/src/commands/app/config/use.ts new file mode 100644 index 00000000000..4333c5f49b4 --- /dev/null +++ b/packages/cli/src/commands/app/config/use.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:config:use'] as any diff --git a/packages/cli/src/commands/app/config/validate.ts b/packages/cli/src/commands/app/config/validate.ts new file mode 100644 index 00000000000..c99f2c1688f --- /dev/null +++ b/packages/cli/src/commands/app/config/validate.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:config:validate'] as any diff --git a/packages/cli/src/commands/app/deploy.ts b/packages/cli/src/commands/app/deploy.ts new file mode 100644 index 00000000000..0c3426c1ef1 --- /dev/null +++ b/packages/cli/src/commands/app/deploy.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:deploy'] as any diff --git a/packages/cli/src/commands/app/dev.ts b/packages/cli/src/commands/app/dev.ts new file mode 100644 index 00000000000..f8652c8cd28 --- /dev/null +++ b/packages/cli/src/commands/app/dev.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:dev'] as any diff --git a/packages/cli/src/commands/app/dev/clean.ts b/packages/cli/src/commands/app/dev/clean.ts new file mode 100644 index 00000000000..c8e67e51b27 --- /dev/null +++ b/packages/cli/src/commands/app/dev/clean.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:dev:clean'] as any diff --git a/packages/cli/src/commands/app/env/pull.ts b/packages/cli/src/commands/app/env/pull.ts new file mode 100644 index 00000000000..1f7faecd9ee --- /dev/null +++ b/packages/cli/src/commands/app/env/pull.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:env:pull'] as any diff --git a/packages/cli/src/commands/app/env/show.ts b/packages/cli/src/commands/app/env/show.ts new file mode 100644 index 00000000000..bcf017ff312 --- /dev/null +++ b/packages/cli/src/commands/app/env/show.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:env:show'] as any diff --git a/packages/cli/src/commands/app/execute.ts b/packages/cli/src/commands/app/execute.ts new file mode 100644 index 00000000000..f2c5fe1b71c --- /dev/null +++ b/packages/cli/src/commands/app/execute.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:execute'] as any diff --git a/packages/cli/src/commands/app/function/build.ts b/packages/cli/src/commands/app/function/build.ts new file mode 100644 index 00000000000..61b61ff7fb8 --- /dev/null +++ b/packages/cli/src/commands/app/function/build.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:function:build'] as any diff --git a/packages/cli/src/commands/app/function/info.ts b/packages/cli/src/commands/app/function/info.ts new file mode 100644 index 00000000000..b35ac11653e --- /dev/null +++ b/packages/cli/src/commands/app/function/info.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:function:info'] as any diff --git a/packages/cli/src/commands/app/function/replay.ts b/packages/cli/src/commands/app/function/replay.ts new file mode 100644 index 00000000000..29f35dd4085 --- /dev/null +++ b/packages/cli/src/commands/app/function/replay.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:function:replay'] as any diff --git a/packages/cli/src/commands/app/function/run.ts b/packages/cli/src/commands/app/function/run.ts new file mode 100644 index 00000000000..0206d88c12f --- /dev/null +++ b/packages/cli/src/commands/app/function/run.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:function:run'] as any diff --git a/packages/cli/src/commands/app/function/schema.ts b/packages/cli/src/commands/app/function/schema.ts new file mode 100644 index 00000000000..300d77a72f8 --- /dev/null +++ b/packages/cli/src/commands/app/function/schema.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:function:schema'] as any diff --git a/packages/cli/src/commands/app/function/typegen.ts b/packages/cli/src/commands/app/function/typegen.ts new file mode 100644 index 00000000000..f8920748bfc --- /dev/null +++ b/packages/cli/src/commands/app/function/typegen.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:function:typegen'] as any diff --git a/packages/cli/src/commands/app/generate/extension.ts b/packages/cli/src/commands/app/generate/extension.ts new file mode 100644 index 00000000000..be40a2112d7 --- /dev/null +++ b/packages/cli/src/commands/app/generate/extension.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:generate:extension'] as any diff --git a/packages/cli/src/commands/app/generate/schema.ts b/packages/cli/src/commands/app/generate/schema.ts new file mode 100644 index 00000000000..888458e2a65 --- /dev/null +++ b/packages/cli/src/commands/app/generate/schema.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:generate:schema'] as any diff --git a/packages/cli/src/commands/app/import-custom-data-definitions.ts b/packages/cli/src/commands/app/import-custom-data-definitions.ts new file mode 100644 index 00000000000..ed8c8f5e24c --- /dev/null +++ b/packages/cli/src/commands/app/import-custom-data-definitions.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:import-custom-data-definitions'] as any diff --git a/packages/cli/src/commands/app/import-extensions.ts b/packages/cli/src/commands/app/import-extensions.ts new file mode 100644 index 00000000000..523824d8fcb --- /dev/null +++ b/packages/cli/src/commands/app/import-extensions.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:import-extensions'] as any diff --git a/packages/cli/src/commands/app/info.ts b/packages/cli/src/commands/app/info.ts new file mode 100644 index 00000000000..cfd0da647f2 --- /dev/null +++ b/packages/cli/src/commands/app/info.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:info'] as any diff --git a/packages/cli/src/commands/app/init.ts b/packages/cli/src/commands/app/init.ts new file mode 100644 index 00000000000..d4c520eebab --- /dev/null +++ b/packages/cli/src/commands/app/init.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:init'] as any diff --git a/packages/cli/src/commands/app/logs.ts b/packages/cli/src/commands/app/logs.ts new file mode 100644 index 00000000000..cda70cb0391 --- /dev/null +++ b/packages/cli/src/commands/app/logs.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:logs'] as any diff --git a/packages/cli/src/commands/app/release.ts b/packages/cli/src/commands/app/release.ts new file mode 100644 index 00000000000..747e7d0f874 --- /dev/null +++ b/packages/cli/src/commands/app/release.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:release'] as any diff --git a/packages/cli/src/commands/app/versions/list.ts b/packages/cli/src/commands/app/versions/list.ts new file mode 100644 index 00000000000..5e1c5d579a9 --- /dev/null +++ b/packages/cli/src/commands/app/versions/list.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:versions:list'] as any diff --git a/packages/cli/src/commands/app/webhook/trigger.ts b/packages/cli/src/commands/app/webhook/trigger.ts new file mode 100644 index 00000000000..87b9e2ea01b --- /dev/null +++ b/packages/cli/src/commands/app/webhook/trigger.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['app:webhook:trigger'] as any diff --git a/packages/cli/src/commands/auth/login.ts b/packages/cli/src/commands/auth/login.ts new file mode 100644 index 00000000000..41eb78ac97c --- /dev/null +++ b/packages/cli/src/commands/auth/login.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/auth/login.js' diff --git a/packages/cli/src/commands/auth/logout.ts b/packages/cli/src/commands/auth/logout.ts new file mode 100644 index 00000000000..5b87c9b3c1d --- /dev/null +++ b/packages/cli/src/commands/auth/logout.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/auth/logout.js' diff --git a/packages/cli/src/commands/cache/clear.ts b/packages/cli/src/commands/cache/clear.ts new file mode 100644 index 00000000000..462f31864a5 --- /dev/null +++ b/packages/cli/src/commands/cache/clear.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/cache/clear.js' diff --git a/packages/cli/src/commands/commands.ts b/packages/cli/src/commands/commands.ts new file mode 100644 index 00000000000..4c1184201e8 --- /dev/null +++ b/packages/cli/src/commands/commands.ts @@ -0,0 +1,3 @@ +import {commands} from '@oclif/plugin-commands' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands.commands as any diff --git a/packages/cli/src/commands/config/autocorrect/off.ts b/packages/cli/src/commands/config/autocorrect/off.ts new file mode 100644 index 00000000000..dd51c36e9c5 --- /dev/null +++ b/packages/cli/src/commands/config/autocorrect/off.ts @@ -0,0 +1,3 @@ +import {DidYouMeanCommands} from '@shopify/plugin-did-you-mean' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default DidYouMeanCommands['config:autocorrect:off'] as any diff --git a/packages/cli/src/commands/config/autocorrect/on.ts b/packages/cli/src/commands/config/autocorrect/on.ts new file mode 100644 index 00000000000..5150fa4cb11 --- /dev/null +++ b/packages/cli/src/commands/config/autocorrect/on.ts @@ -0,0 +1,3 @@ +import {DidYouMeanCommands} from '@shopify/plugin-did-you-mean' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default DidYouMeanCommands['config:autocorrect:on'] as any diff --git a/packages/cli/src/commands/config/autocorrect/status.ts b/packages/cli/src/commands/config/autocorrect/status.ts new file mode 100644 index 00000000000..26bd11df173 --- /dev/null +++ b/packages/cli/src/commands/config/autocorrect/status.ts @@ -0,0 +1,3 @@ +import {DidYouMeanCommands} from '@shopify/plugin-did-you-mean' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default DidYouMeanCommands['config:autocorrect:status'] as any diff --git a/packages/cli/src/commands/debug/command-flags.ts b/packages/cli/src/commands/debug/command-flags.ts new file mode 100644 index 00000000000..3aeca2a450d --- /dev/null +++ b/packages/cli/src/commands/debug/command-flags.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/debug/command-flags.js' diff --git a/packages/cli/src/commands/demo/watcher.ts b/packages/cli/src/commands/demo/watcher.ts new file mode 100644 index 00000000000..1dc418c4a54 --- /dev/null +++ b/packages/cli/src/commands/demo/watcher.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['demo:watcher'] as any diff --git a/packages/cli/src/commands/docs/generate.ts b/packages/cli/src/commands/docs/generate.ts new file mode 100644 index 00000000000..5c6d88d942b --- /dev/null +++ b/packages/cli/src/commands/docs/generate.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/docs/generate.js' diff --git a/packages/cli/src/commands/doctor-release/index.ts b/packages/cli/src/commands/doctor-release/index.ts new file mode 100644 index 00000000000..e4ab490d69a --- /dev/null +++ b/packages/cli/src/commands/doctor-release/index.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/doctor-release/doctor-release.js' diff --git a/packages/cli/src/commands/doctor-release/theme/index.ts b/packages/cli/src/commands/doctor-release/theme/index.ts new file mode 100644 index 00000000000..1706c021b47 --- /dev/null +++ b/packages/cli/src/commands/doctor-release/theme/index.ts @@ -0,0 +1 @@ +export {default} from '../../../cli/commands/doctor-release/theme/index.js' diff --git a/packages/cli/src/commands/help.ts b/packages/cli/src/commands/help.ts new file mode 100644 index 00000000000..67a3eadf569 --- /dev/null +++ b/packages/cli/src/commands/help.ts @@ -0,0 +1 @@ +export {default} from '../cli/commands/help.js' diff --git a/packages/cli/src/commands/hydrogen/build.ts b/packages/cli/src/commands/hydrogen/build.ts new file mode 100644 index 00000000000..0d5c353b864 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/build.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:build'] as any diff --git a/packages/cli/src/commands/hydrogen/check.ts b/packages/cli/src/commands/hydrogen/check.ts new file mode 100644 index 00000000000..180a74006e7 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/check.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:check'] as any diff --git a/packages/cli/src/commands/hydrogen/codegen.ts b/packages/cli/src/commands/hydrogen/codegen.ts new file mode 100644 index 00000000000..29b12fb0030 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/codegen.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:codegen'] as any diff --git a/packages/cli/src/commands/hydrogen/customer-account-push.ts b/packages/cli/src/commands/hydrogen/customer-account-push.ts new file mode 100644 index 00000000000..e705bb31215 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/customer-account-push.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:customer-account-push'] as any diff --git a/packages/cli/src/commands/hydrogen/debug/cpu.ts b/packages/cli/src/commands/hydrogen/debug/cpu.ts new file mode 100644 index 00000000000..434c0fab513 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/debug/cpu.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:debug:cpu'] as any diff --git a/packages/cli/src/commands/hydrogen/deploy.ts b/packages/cli/src/commands/hydrogen/deploy.ts new file mode 100644 index 00000000000..4c70f3e90d6 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/deploy.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:deploy'] as any diff --git a/packages/cli/src/commands/hydrogen/dev.ts b/packages/cli/src/commands/hydrogen/dev.ts new file mode 100644 index 00000000000..a51ea4df587 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/dev.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:dev'] as any diff --git a/packages/cli/src/commands/hydrogen/env/list.ts b/packages/cli/src/commands/hydrogen/env/list.ts new file mode 100644 index 00000000000..270947f3658 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/env/list.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:env:list'] as any diff --git a/packages/cli/src/commands/hydrogen/env/pull.ts b/packages/cli/src/commands/hydrogen/env/pull.ts new file mode 100644 index 00000000000..7af967bd7ad --- /dev/null +++ b/packages/cli/src/commands/hydrogen/env/pull.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:env:pull'] as any diff --git a/packages/cli/src/commands/hydrogen/env/push.ts b/packages/cli/src/commands/hydrogen/env/push.ts new file mode 100644 index 00000000000..4b76e693128 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/env/push.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:env:push'] as any diff --git a/packages/cli/src/commands/hydrogen/g.ts b/packages/cli/src/commands/hydrogen/g.ts new file mode 100644 index 00000000000..11dd2233593 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/g.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:g'] as any diff --git a/packages/cli/src/commands/hydrogen/generate/route.ts b/packages/cli/src/commands/hydrogen/generate/route.ts new file mode 100644 index 00000000000..cb5820d4812 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/generate/route.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:generate:route'] as any diff --git a/packages/cli/src/commands/hydrogen/generate/routes.ts b/packages/cli/src/commands/hydrogen/generate/routes.ts new file mode 100644 index 00000000000..5384f405659 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/generate/routes.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:generate:routes'] as any diff --git a/packages/cli/src/commands/hydrogen/init.ts b/packages/cli/src/commands/hydrogen/init.ts new file mode 100644 index 00000000000..a8e03a9246a --- /dev/null +++ b/packages/cli/src/commands/hydrogen/init.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:init'] as any diff --git a/packages/cli/src/commands/hydrogen/link.ts b/packages/cli/src/commands/hydrogen/link.ts new file mode 100644 index 00000000000..51f6cffdcd9 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/link.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:link'] as any diff --git a/packages/cli/src/commands/hydrogen/list.ts b/packages/cli/src/commands/hydrogen/list.ts new file mode 100644 index 00000000000..3b21c49478e --- /dev/null +++ b/packages/cli/src/commands/hydrogen/list.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:list'] as any diff --git a/packages/cli/src/commands/hydrogen/login.ts b/packages/cli/src/commands/hydrogen/login.ts new file mode 100644 index 00000000000..482df78eb27 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/login.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:login'] as any diff --git a/packages/cli/src/commands/hydrogen/logout.ts b/packages/cli/src/commands/hydrogen/logout.ts new file mode 100644 index 00000000000..0709802fead --- /dev/null +++ b/packages/cli/src/commands/hydrogen/logout.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:logout'] as any diff --git a/packages/cli/src/commands/hydrogen/preview.ts b/packages/cli/src/commands/hydrogen/preview.ts new file mode 100644 index 00000000000..49cd5793d5d --- /dev/null +++ b/packages/cli/src/commands/hydrogen/preview.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:preview'] as any diff --git a/packages/cli/src/commands/hydrogen/setup.ts b/packages/cli/src/commands/hydrogen/setup.ts new file mode 100644 index 00000000000..e9e9a881ce0 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/setup.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:setup'] as any diff --git a/packages/cli/src/commands/hydrogen/setup/css.ts b/packages/cli/src/commands/hydrogen/setup/css.ts new file mode 100644 index 00000000000..7eba7731e2a --- /dev/null +++ b/packages/cli/src/commands/hydrogen/setup/css.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:setup:css'] as any diff --git a/packages/cli/src/commands/hydrogen/setup/markets.ts b/packages/cli/src/commands/hydrogen/setup/markets.ts new file mode 100644 index 00000000000..3fd02b8c59a --- /dev/null +++ b/packages/cli/src/commands/hydrogen/setup/markets.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:setup:markets'] as any diff --git a/packages/cli/src/commands/hydrogen/setup/vite.ts b/packages/cli/src/commands/hydrogen/setup/vite.ts new file mode 100644 index 00000000000..b1009187efd --- /dev/null +++ b/packages/cli/src/commands/hydrogen/setup/vite.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:setup:vite'] as any diff --git a/packages/cli/src/commands/hydrogen/shortcut.ts b/packages/cli/src/commands/hydrogen/shortcut.ts new file mode 100644 index 00000000000..f461c321db3 --- /dev/null +++ b/packages/cli/src/commands/hydrogen/shortcut.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:shortcut'] as any diff --git a/packages/cli/src/commands/hydrogen/unlink.ts b/packages/cli/src/commands/hydrogen/unlink.ts new file mode 100644 index 00000000000..e2ef17b812a --- /dev/null +++ b/packages/cli/src/commands/hydrogen/unlink.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:unlink'] as any diff --git a/packages/cli/src/commands/hydrogen/upgrade.ts b/packages/cli/src/commands/hydrogen/upgrade.ts new file mode 100644 index 00000000000..cfa7d29295f --- /dev/null +++ b/packages/cli/src/commands/hydrogen/upgrade.ts @@ -0,0 +1,3 @@ +import {COMMANDS} from '@shopify/cli-hydrogen' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default COMMANDS['hydrogen:upgrade'] as any diff --git a/packages/cli/src/commands/kitchen-sink/async.ts b/packages/cli/src/commands/kitchen-sink/async.ts new file mode 100644 index 00000000000..0dddc79d095 --- /dev/null +++ b/packages/cli/src/commands/kitchen-sink/async.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/kitchen-sink/async.js' diff --git a/packages/cli/src/commands/kitchen-sink/index.ts b/packages/cli/src/commands/kitchen-sink/index.ts new file mode 100644 index 00000000000..fb2d5d54b28 --- /dev/null +++ b/packages/cli/src/commands/kitchen-sink/index.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/kitchen-sink/index.js' diff --git a/packages/cli/src/commands/kitchen-sink/prompts.ts b/packages/cli/src/commands/kitchen-sink/prompts.ts new file mode 100644 index 00000000000..e2338493698 --- /dev/null +++ b/packages/cli/src/commands/kitchen-sink/prompts.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/kitchen-sink/prompts.js' diff --git a/packages/cli/src/commands/kitchen-sink/static.ts b/packages/cli/src/commands/kitchen-sink/static.ts new file mode 100644 index 00000000000..548f295e32d --- /dev/null +++ b/packages/cli/src/commands/kitchen-sink/static.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/kitchen-sink/static.js' diff --git a/packages/cli/src/commands/notifications/generate.ts b/packages/cli/src/commands/notifications/generate.ts new file mode 100644 index 00000000000..1a6e4b8a8ed --- /dev/null +++ b/packages/cli/src/commands/notifications/generate.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/notifications/generate.js' diff --git a/packages/cli/src/commands/notifications/list.ts b/packages/cli/src/commands/notifications/list.ts new file mode 100644 index 00000000000..206198af263 --- /dev/null +++ b/packages/cli/src/commands/notifications/list.ts @@ -0,0 +1 @@ +export {default} from '../../cli/commands/notifications/list.js' diff --git a/packages/cli/src/commands/organization/list.ts b/packages/cli/src/commands/organization/list.ts new file mode 100644 index 00000000000..9b16605fa97 --- /dev/null +++ b/packages/cli/src/commands/organization/list.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['organization:list'] as any diff --git a/packages/cli/src/commands/plugins/index.ts b/packages/cli/src/commands/plugins/index.ts new file mode 100644 index 00000000000..c09afe9ed38 --- /dev/null +++ b/packages/cli/src/commands/plugins/index.ts @@ -0,0 +1,5 @@ +import {commands} from '@oclif/plugin-plugins' +const cmd = commands.plugins +cmd.hidden = true +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default cmd as any diff --git a/packages/cli/src/commands/plugins/inspect.ts b/packages/cli/src/commands/plugins/inspect.ts new file mode 100644 index 00000000000..b6a0ebadefd --- /dev/null +++ b/packages/cli/src/commands/plugins/inspect.ts @@ -0,0 +1,3 @@ +import {commands} from '@oclif/plugin-plugins' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['plugins:inspect'] as any diff --git a/packages/cli/src/commands/plugins/install.ts b/packages/cli/src/commands/plugins/install.ts new file mode 100644 index 00000000000..9b9a416ecb3 --- /dev/null +++ b/packages/cli/src/commands/plugins/install.ts @@ -0,0 +1,5 @@ +import {commands} from '@oclif/plugin-plugins' +const cmd = commands['plugins:install'] +cmd.description = '' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default cmd as any diff --git a/packages/cli/src/commands/plugins/link.ts b/packages/cli/src/commands/plugins/link.ts new file mode 100644 index 00000000000..27d53a902d8 --- /dev/null +++ b/packages/cli/src/commands/plugins/link.ts @@ -0,0 +1,3 @@ +import {commands} from '@oclif/plugin-plugins' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['plugins:link'] as any diff --git a/packages/cli/src/commands/plugins/reset.ts b/packages/cli/src/commands/plugins/reset.ts new file mode 100644 index 00000000000..40205421664 --- /dev/null +++ b/packages/cli/src/commands/plugins/reset.ts @@ -0,0 +1,3 @@ +import {commands} from '@oclif/plugin-plugins' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['plugins:reset'] as any diff --git a/packages/cli/src/commands/plugins/uninstall.ts b/packages/cli/src/commands/plugins/uninstall.ts new file mode 100644 index 00000000000..117eae399cf --- /dev/null +++ b/packages/cli/src/commands/plugins/uninstall.ts @@ -0,0 +1,3 @@ +import {commands} from '@oclif/plugin-plugins' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['plugins:uninstall'] as any diff --git a/packages/cli/src/commands/plugins/update.ts b/packages/cli/src/commands/plugins/update.ts new file mode 100644 index 00000000000..3ba456b7ed2 --- /dev/null +++ b/packages/cli/src/commands/plugins/update.ts @@ -0,0 +1,3 @@ +import {commands} from '@oclif/plugin-plugins' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['plugins:update'] as any diff --git a/packages/cli/src/commands/search.ts b/packages/cli/src/commands/search.ts new file mode 100644 index 00000000000..2b5a0189cf6 --- /dev/null +++ b/packages/cli/src/commands/search.ts @@ -0,0 +1 @@ +export {default} from '../cli/commands/search.js' diff --git a/packages/cli/src/commands/theme/check.ts b/packages/cli/src/commands/theme/check.ts new file mode 100644 index 00000000000..a009841d151 --- /dev/null +++ b/packages/cli/src/commands/theme/check.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:check'] as any diff --git a/packages/cli/src/commands/theme/console.ts b/packages/cli/src/commands/theme/console.ts new file mode 100644 index 00000000000..ccbd714d60e --- /dev/null +++ b/packages/cli/src/commands/theme/console.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:console'] as any diff --git a/packages/cli/src/commands/theme/delete.ts b/packages/cli/src/commands/theme/delete.ts new file mode 100644 index 00000000000..ac6e3bdd222 --- /dev/null +++ b/packages/cli/src/commands/theme/delete.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:delete'] as any diff --git a/packages/cli/src/commands/theme/dev.ts b/packages/cli/src/commands/theme/dev.ts new file mode 100644 index 00000000000..993a66b5a2a --- /dev/null +++ b/packages/cli/src/commands/theme/dev.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:dev'] as any diff --git a/packages/cli/src/commands/theme/duplicate.ts b/packages/cli/src/commands/theme/duplicate.ts new file mode 100644 index 00000000000..c21b10d41e2 --- /dev/null +++ b/packages/cli/src/commands/theme/duplicate.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:duplicate'] as any diff --git a/packages/cli/src/commands/theme/info.ts b/packages/cli/src/commands/theme/info.ts new file mode 100644 index 00000000000..6816f5c4bb7 --- /dev/null +++ b/packages/cli/src/commands/theme/info.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:info'] as any diff --git a/packages/cli/src/commands/theme/init.ts b/packages/cli/src/commands/theme/init.ts new file mode 100644 index 00000000000..964b6418bc8 --- /dev/null +++ b/packages/cli/src/commands/theme/init.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:init'] as any diff --git a/packages/cli/src/commands/theme/language-server.ts b/packages/cli/src/commands/theme/language-server.ts new file mode 100644 index 00000000000..50a7928d362 --- /dev/null +++ b/packages/cli/src/commands/theme/language-server.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:language-server'] as any diff --git a/packages/cli/src/commands/theme/list.ts b/packages/cli/src/commands/theme/list.ts new file mode 100644 index 00000000000..8687cd54638 --- /dev/null +++ b/packages/cli/src/commands/theme/list.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:list'] as any diff --git a/packages/cli/src/commands/theme/metafields/pull.ts b/packages/cli/src/commands/theme/metafields/pull.ts new file mode 100644 index 00000000000..9587cdd6e6d --- /dev/null +++ b/packages/cli/src/commands/theme/metafields/pull.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:metafields:pull'] as any diff --git a/packages/cli/src/commands/theme/open.ts b/packages/cli/src/commands/theme/open.ts new file mode 100644 index 00000000000..60d27bbec8a --- /dev/null +++ b/packages/cli/src/commands/theme/open.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:open'] as any diff --git a/packages/cli/src/commands/theme/package.ts b/packages/cli/src/commands/theme/package.ts new file mode 100644 index 00000000000..d40c5b43883 --- /dev/null +++ b/packages/cli/src/commands/theme/package.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:package'] as any diff --git a/packages/cli/src/commands/theme/preview.ts b/packages/cli/src/commands/theme/preview.ts new file mode 100644 index 00000000000..d78deb10478 --- /dev/null +++ b/packages/cli/src/commands/theme/preview.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:preview'] as any diff --git a/packages/cli/src/commands/theme/profile.ts b/packages/cli/src/commands/theme/profile.ts new file mode 100644 index 00000000000..a87c9497dbc --- /dev/null +++ b/packages/cli/src/commands/theme/profile.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:profile'] as any diff --git a/packages/cli/src/commands/theme/publish.ts b/packages/cli/src/commands/theme/publish.ts new file mode 100644 index 00000000000..6bacdc0c062 --- /dev/null +++ b/packages/cli/src/commands/theme/publish.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:publish'] as any diff --git a/packages/cli/src/commands/theme/pull.ts b/packages/cli/src/commands/theme/pull.ts new file mode 100644 index 00000000000..162d1bc1dec --- /dev/null +++ b/packages/cli/src/commands/theme/pull.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:pull'] as any diff --git a/packages/cli/src/commands/theme/push.ts b/packages/cli/src/commands/theme/push.ts new file mode 100644 index 00000000000..34c0e55e791 --- /dev/null +++ b/packages/cli/src/commands/theme/push.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:push'] as any diff --git a/packages/cli/src/commands/theme/rename.ts b/packages/cli/src/commands/theme/rename.ts new file mode 100644 index 00000000000..c62edd22cee --- /dev/null +++ b/packages/cli/src/commands/theme/rename.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:rename'] as any diff --git a/packages/cli/src/commands/theme/serve.ts b/packages/cli/src/commands/theme/serve.ts new file mode 100644 index 00000000000..6c7fd2b16c2 --- /dev/null +++ b/packages/cli/src/commands/theme/serve.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:serve'] as any diff --git a/packages/cli/src/commands/theme/share.ts b/packages/cli/src/commands/theme/share.ts new file mode 100644 index 00000000000..9fd868f3b29 --- /dev/null +++ b/packages/cli/src/commands/theme/share.ts @@ -0,0 +1,3 @@ +import ThemeCommands from '@shopify/theme' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default ThemeCommands['theme:share'] as any diff --git a/packages/cli/src/commands/upgrade.ts b/packages/cli/src/commands/upgrade.ts new file mode 100644 index 00000000000..545d4d1b097 --- /dev/null +++ b/packages/cli/src/commands/upgrade.ts @@ -0,0 +1 @@ +export {default} from '../cli/commands/upgrade.js' diff --git a/packages/cli/src/commands/version.ts b/packages/cli/src/commands/version.ts new file mode 100644 index 00000000000..cc3ddd041f0 --- /dev/null +++ b/packages/cli/src/commands/version.ts @@ -0,0 +1 @@ +export {default} from '../cli/commands/version.js' diff --git a/packages/cli/src/commands/webhook/trigger.ts b/packages/cli/src/commands/webhook/trigger.ts new file mode 100644 index 00000000000..1d4935e5259 --- /dev/null +++ b/packages/cli/src/commands/webhook/trigger.ts @@ -0,0 +1,3 @@ +import {commands} from '@shopify/app' +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default commands['webhook:trigger'] as any diff --git a/packages/cli/src/hooks/app-init.ts b/packages/cli/src/hooks/app-init.ts new file mode 100644 index 00000000000..eb9fe976f30 --- /dev/null +++ b/packages/cli/src/hooks/app-init.ts @@ -0,0 +1,9 @@ +// Defer the @shopify/app init hook to avoid loading the heavy package at startup. +// The actual init logic (clearing command cache + setting COMMAND_RUN_ID) will run +// lazily when an app command is first executed, via the prerun hook. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const hook = async function (_this: any, _options: any) { + // no-op at startup — deferred to prerun +} + +export default hook diff --git a/packages/cli/src/hooks/app-public-metadata.ts b/packages/cli/src/hooks/app-public-metadata.ts new file mode 100644 index 00000000000..c72c50fef28 --- /dev/null +++ b/packages/cli/src/hooks/app-public-metadata.ts @@ -0,0 +1,7 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const hook = async function (this: any, options: any) { + const {AppPublicMetadataHook} = await import('@shopify/app') + return AppPublicMetadataHook.call(this, options) +} + +export default hook diff --git a/packages/cli/src/hooks/app-sensitive-metadata.ts b/packages/cli/src/hooks/app-sensitive-metadata.ts new file mode 100644 index 00000000000..3b80fd60752 --- /dev/null +++ b/packages/cli/src/hooks/app-sensitive-metadata.ts @@ -0,0 +1,7 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const hook = async function (this: any, options: any) { + const {AppSensitiveMetadataHook} = await import('@shopify/app') + return AppSensitiveMetadataHook.call(this, options) +} + +export default hook diff --git a/packages/cli/src/hooks/did-you-mean.ts b/packages/cli/src/hooks/did-you-mean.ts new file mode 100644 index 00000000000..d230f013355 --- /dev/null +++ b/packages/cli/src/hooks/did-you-mean.ts @@ -0,0 +1,7 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const hook = async function (this: any, options: any) { + const {DidYouMeanHook} = await import('@shopify/plugin-did-you-mean') + return DidYouMeanHook.call(this, options) +} + +export default hook diff --git a/packages/cli/src/hooks/hydrogen-init.ts b/packages/cli/src/hooks/hydrogen-init.ts new file mode 100644 index 00000000000..e57fa1a090f --- /dev/null +++ b/packages/cli/src/hooks/hydrogen-init.ts @@ -0,0 +1,8 @@ +// Defer the @shopify/cli-hydrogen init hook to avoid loading the heavy package at startup. +// The actual init logic will run lazily when a hydrogen command is first executed. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const hook = async function (_this: any, _options: any) { + // no-op at startup — deferred to prerun +} + +export default hook diff --git a/packages/cli/src/hooks/plugin-plugins.ts b/packages/cli/src/hooks/plugin-plugins.ts new file mode 100644 index 00000000000..2d6a0d9ccb8 --- /dev/null +++ b/packages/cli/src/hooks/plugin-plugins.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const hook = async function (this: any, options: any) { + const pluginPlugins = await import('@oclif/plugin-plugins') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (pluginPlugins.hooks as any).call(this, options) +} + +export default hook diff --git a/packages/cli/src/hooks/prerun.ts b/packages/cli/src/hooks/prerun.ts index c3283a474cb..e5c0b986df7 100644 --- a/packages/cli/src/hooks/prerun.ts +++ b/packages/cli/src/hooks/prerun.ts @@ -1 +1,58 @@ -export {hook as default} from '@shopify/cli-kit/node/hooks/prerun' +import {Hook} from '@oclif/core' + +// Map command ID prefixes to their source plugin names for analytics attribution. +const PLUGIN_NAME_MAP: Record = { + 'app:': '@shopify/app', + 'webhook:': '@shopify/app', + 'demo:': '@shopify/app', + 'organization:': '@shopify/app', + 'theme:': '@shopify/theme', + 'hydrogen:': '@shopify/cli-hydrogen', + 'commands': '@oclif/plugin-commands', + 'plugins': '@oclif/plugin-plugins', + 'config:autocorrect:': '@shopify/plugin-did-you-mean', +} + +function getPluginName(commandId: string): string | undefined { + for (const [prefix, pluginName] of Object.entries(PLUGIN_NAME_MAP)) { + if (commandId === prefix || commandId.startsWith(prefix)) { + return pluginName + } + } + return undefined +} + +// Track whether deferred init hooks have been run +let appInitDone = false +let hydrogenInitDone = false + +const prerunHook: Hook.Prerun = async function (this, options) { + // Set customPluginName for analytics attribution + const pluginName = getPluginName(options.Command.id) + if (pluginName) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ;(options.Command as any).customPluginName = pluginName + } + + // Run deferred init hooks lazily on first relevant command + const commandId = options.Command.id + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const initOpts: any = {id: commandId, argv: options.argv, config: options.config} + if (!appInitDone && (commandId.startsWith('app:') || commandId === 'webhook:trigger' || commandId === 'demo:watcher' || commandId === 'organization:list')) { + appInitDone = true + const {AppInitHook} = await import('@shopify/app') + await AppInitHook.call(this, initOpts) + } + + if (!hydrogenInitDone && commandId.startsWith('hydrogen:')) { + hydrogenInitDone = true + const {HOOKS} = await import('@shopify/cli-hydrogen') + await (HOOKS.init as Function).call(this, initOpts) + } + + // Delegate to the standard cli-kit prerun hook + const {hook} = await import('@shopify/cli-kit/node/hooks/prerun') + return hook.call(this, options) +} + +export default prerunHook diff --git a/packages/cli/src/hooks/tunnel-provider.ts b/packages/cli/src/hooks/tunnel-provider.ts new file mode 100644 index 00000000000..4ca955d1be8 --- /dev/null +++ b/packages/cli/src/hooks/tunnel-provider.ts @@ -0,0 +1,7 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const hook = async function (this: any, options: any) { + const providerHook = await import('@shopify/plugin-cloudflare/hooks/provider') + return providerHook.default.call(this, options) +} + +export default hook diff --git a/packages/cli/src/hooks/tunnel-start.ts b/packages/cli/src/hooks/tunnel-start.ts new file mode 100644 index 00000000000..9f99115648b --- /dev/null +++ b/packages/cli/src/hooks/tunnel-start.ts @@ -0,0 +1,7 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const hook = async function (this: any, options: any) { + const tunnelHook = await import('@shopify/plugin-cloudflare/hooks/tunnel') + return tunnelHook.default.call(this, options) +} + +export default hook diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 4e762b6067d..82dfd798977 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,156 +1,8 @@ -/* eslint-disable @shopify/cli/specific-imports-in-bootstrap-code */ -import VersionCommand from './cli/commands/version.js' -import Search from './cli/commands/search.js' -import Upgrade from './cli/commands/upgrade.js' -import Logout from './cli/commands/auth/logout.js' -import Login from './cli/commands/auth/login.js' -import CommandFlags from './cli/commands/debug/command-flags.js' -import KitchenSinkAsync from './cli/commands/kitchen-sink/async.js' -import KitchenSinkPrompts from './cli/commands/kitchen-sink/prompts.js' -import KitchenSinkStatic from './cli/commands/kitchen-sink/static.js' -import KitchenSink from './cli/commands/kitchen-sink/index.js' -import Doctor from './cli/commands/doctor-release/doctor-release.js' -import DoctorTheme from './cli/commands/doctor-release/theme/index.js' -import DocsGenerate from './cli/commands/docs/generate.js' -import HelpCommand from './cli/commands/help.js' -import List from './cli/commands/notifications/list.js' -import Generate from './cli/commands/notifications/generate.js' -import ClearCache from './cli/commands/cache/clear.js' -import {createGlobalProxyAgent} from 'global-agent' -import ThemeCommands from '@shopify/theme' -import {COMMANDS as HydrogenCommands, HOOKS as HydrogenHooks} from '@shopify/cli-hydrogen' -import {commands as AppCommands} from '@shopify/app' -import {commands as PluginCommandsCommands} from '@oclif/plugin-commands' -import {commands as PluginPluginsCommands} from '@oclif/plugin-plugins' -import {DidYouMeanCommands} from '@shopify/plugin-did-you-mean' -import {runCLI} from '@shopify/cli-kit/node/cli' -import {renderFatalError} from '@shopify/cli-kit/node/ui' -import {FatalError} from '@shopify/cli-kit/node/error' +// Public API barrel for the CLI package. +// +// Commands are now discovered by oclif's pattern strategy from the commands/ directory. +// The CLI entry point is bootstrap.ts (used by bin/run.js and bin/dev.js). +// +// This file only re-exports public utilities that external consumers may depend on. -import fs from 'fs' - -export {DidYouMeanHook} from '@shopify/plugin-did-you-mean' -export {default as TunnelStartHook} from '@shopify/plugin-cloudflare/hooks/tunnel' -export {default as TunnelProviderHook} from '@shopify/plugin-cloudflare/hooks/provider' -export {hooks as PluginHook} from '@oclif/plugin-plugins' -export {AppSensitiveMetadataHook, AppInitHook, AppPublicMetadataHook} from '@shopify/app' export {push, pull, fetchStoreThemes} from '@shopify/theme' - -export const HydrogenInitHook: unknown = HydrogenHooks.init - -// Setup global support for environment variable based proxy configuration. -createGlobalProxyAgent({ - environmentVariableNamespace: 'SHOPIFY_', - forceGlobalAgent: true, - socketConnectionTimeout: 60000, -}) - -// In some cases (for example when we boot the proxy server), when an exception is -// thrown, no 'exit' signal is sent to the process. We don't understand this fully. -// This means that any cleanup code that depends on "process.on('exit', ...)" will -// not be called. The tunnel plugin is an example of that. Here we make sure to print -// the error stack and manually call exit so that the cleanup code is called. This -// makes sure that there are no lingering tunnel processes. -process.on('uncaughtException', (err) => { - if (err instanceof FatalError) { - renderFatalError(err) - } else { - fs.writeSync(process.stderr.fd, `${err.stack ?? err.message ?? err}\n`) - } - process.exit(1) -}) -const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'] -signals.forEach((signal) => { - process.on(signal, () => { - process.exit(1) - }) -}) - -// Sometimes we want to specify a precise amount of stdout columns, for example in -// CI or on a cloud environment. -const columns = Number(process.env.SHOPIFY_CLI_COLUMNS) -if (!isNaN(columns)) { - process.stdout.columns = columns -} - -interface RunShopifyCLIOptions { - development: boolean -} - -async function runShopifyCLI({development}: RunShopifyCLIOptions) { - await runCLI({ - moduleURL: import.meta.url, - development, - }) -} - -// Hide plugins command -PluginPluginsCommands.plugins.hidden = true - -// Remove default description because it injects a path from the generating computer, making it fail on CI -PluginPluginsCommands['plugins:install'].description = '' - -const appCommands = Object.keys(AppCommands) as (keyof typeof AppCommands)[] -appCommands.forEach((command) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(AppCommands[command] as unknown as any).customPluginName = '@shopify/app' -}) - -const themeCommands = Object.keys(ThemeCommands) as (keyof typeof ThemeCommands)[] -themeCommands.forEach((command) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(ThemeCommands[command] as any).customPluginName = '@shopify/theme' -}) - -const hydrogenCommands = Object.keys(HydrogenCommands) as (keyof typeof HydrogenCommands)[] -hydrogenCommands.forEach((command) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(HydrogenCommands[command] as any).customPluginName = '@shopify/cli-hydrogen' -}) - -const pluginCommandsCommands = Object.keys(PluginCommandsCommands) as (keyof typeof PluginCommandsCommands)[] -pluginCommandsCommands.forEach((command) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(PluginCommandsCommands[command] as any).customPluginName = '@oclif/plugin-commands' -}) - -const didYouMeanCommands = Object.keys(DidYouMeanCommands) as (keyof typeof DidYouMeanCommands)[] -didYouMeanCommands.forEach((command) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(DidYouMeanCommands[command] as any).customPluginName = '@shopify/plugin-did-you-mean' -}) - -const pluginPluginsCommands = Object.keys(PluginPluginsCommands) as (keyof typeof PluginPluginsCommands)[] -pluginPluginsCommands.forEach((command) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(PluginPluginsCommands[command] as any).customPluginName = '@oclif/plugin-plugins' -}) - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const COMMANDS: any = { - ...AppCommands, - ...ThemeCommands, - ...PluginPluginsCommands, - ...DidYouMeanCommands, - ...PluginCommandsCommands, - ...HydrogenCommands, - search: Search, - upgrade: Upgrade, - version: VersionCommand, - help: HelpCommand, - 'auth:logout': Logout, - 'auth:login': Login, - 'debug:command-flags': CommandFlags, - 'kitchen-sink': KitchenSink, - 'kitchen-sink:async': KitchenSinkAsync, - 'kitchen-sink:prompts': KitchenSinkPrompts, - 'kitchen-sink:static': KitchenSinkStatic, - 'doctor-release': Doctor, - 'doctor-release:theme': DoctorTheme, - 'docs:generate': DocsGenerate, - 'notifications:list': List, - 'notifications:generate': Generate, - 'cache:clear': ClearCache, -} - -export default runShopifyCLI From 18f5823e6ae8f607d92b3f0853d958cd5c904f8e Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Tue, 14 Apr 2026 16:01:01 -0400 Subject: [PATCH 2/4] Add ShopifyConfig with non-blocking hooks for faster startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a minimal ShopifyConfig subclass to cli-kit that makes init, prerun, and postrun hooks fire-and-forget (non-blocking). This avoids blocking the command on analytics tracking, upgrade checks, and plugin init setup. Combined with the pattern strategy from the previous commit and process.exit(0) in bootstrap.ts, startup drops from 1.594s to 0.412s (-76.8% vs main). The ShopifyConfig only overrides runHook() — no runCommand() override or custom command registry. Commands are still loaded entirely by oclif's native pattern strategy, keeping the architecture idiomatic. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/lazy-loading-options.md | 6 +- .../cli-kit/src/public/node/cli-launcher.ts | 6 +- .../src/public/node/custom-oclif-loader.ts | 124 ++++++++++++++++++ packages/cli/src/bootstrap.ts | 3 + 4 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 packages/cli-kit/src/public/node/custom-oclif-loader.ts diff --git a/docs/lazy-loading-options.md b/docs/lazy-loading-options.md index eca843dbf28..060e00d165f 100644 --- a/docs/lazy-loading-options.md +++ b/docs/lazy-loading-options.md @@ -218,9 +218,9 @@ export {AppInitHook as default} from '@shopify/app' |---|---| | **Branch** | `lazy-loading-option-b` | | **Perf baseline (`main`)** | 1.778s ± 0.106s (range 1.647s … 2.010s) | -| **Perf result** | 1.594s ± 0.027s (range 1.556s … 1.646s) | -| **Delta** | **-10.3%** | -| **Note** | Heavy packages (`@shopify/app`, `@shopify/theme`, `@shopify/cli-hydrogen`) are NOT loaded for `version`. The remaining 1.6s is `@shopify/cli-kit` + oclif baseline cost. | +| **Perf result** | 0.412s ± 0.012s (range 0.397s … 0.445s) | +| **Delta** | **-76.8%** | +| **Note** | Uses ShopifyConfig with non-blocking init/prerun/postrun hooks + oclif pattern strategy for native lazy loading. Heavy packages NOT loaded for non-app/theme/hydrogen commands. | **Switch to oclif's native `pattern` discovery with a `commands/` directory where each file is a one-line re-export.** diff --git a/packages/cli-kit/src/public/node/cli-launcher.ts b/packages/cli-kit/src/public/node/cli-launcher.ts index 6d9b6b2725d..b96bc9bcea4 100644 --- a/packages/cli-kit/src/public/node/cli-launcher.ts +++ b/packages/cli-kit/src/public/node/cli-launcher.ts @@ -17,15 +17,17 @@ export async function launchCLI(options: Options): Promise { type OclifCore = typeof import('@oclif/core') const oclifModule = await import('@oclif/core') // esbuild wraps CJS dynamic imports under .default when bundling as ESM with code splitting - const {Config, run, flush, Errors, settings}: OclifCore = + const {run, flush, Errors, settings}: OclifCore = (oclifModule as OclifCore & {default?: OclifCore}).default ?? oclifModule + const {ShopifyConfig} = await import('./custom-oclif-loader.js') + if (isDevelopment()) { settings.debug = true } try { - const config = new Config({root: fileURLToPath(options.moduleURL)}) + const config = new ShopifyConfig({root: fileURLToPath(options.moduleURL)}) await config.load() await run(options.argv, config) diff --git a/packages/cli-kit/src/public/node/custom-oclif-loader.ts b/packages/cli-kit/src/public/node/custom-oclif-loader.ts new file mode 100644 index 00000000000..ab7991775a4 --- /dev/null +++ b/packages/cli-kit/src/public/node/custom-oclif-loader.ts @@ -0,0 +1,124 @@ +import {fileExistsSync} from './fs.js' +import {cwd, joinPath, sniffForPath} from './path.js' +import {isDevelopment} from './context/local.js' +import {execaSync} from 'execa' +import {Command, Config} from '@oclif/core' +import {Options} from '@oclif/core/interfaces' + +/** + * Custom oclif Config subclass for the Shopify CLI. + * + * This extends the stock oclif Config with two changes: + * 1. Hydrogen monorepo detection for dev mode (pre-existing, unrelated to lazy loading) + * 2. Non-blocking init hooks — the 'init' event fires in the background so the CLI + * doesn't wait for plugin init hooks (app-init, hydrogen-init) before running commands. + * These hooks do background setup (clearing caches, setting env vars) that doesn't + * need to complete before the target command executes. + */ +export class ShopifyConfig extends Config { + constructor(options: Options) { + if (isDevelopment()) { + // eslint-disable-next-line @shopify/cli/no-process-cwd + const currentPath = cwd() + + let path = sniffForPath() ?? currentPath + // Hydrogen CI uses `hydrogen/hydrogen` path, while local dev uses `shopify/hydrogen`. + const currentPathMightBeHydrogenMonorepo = /(shopify|hydrogen)\/hydrogen/i.test(currentPath) + const ignoreHydrogenMonorepo = process.env.IGNORE_HYDROGEN_MONOREPO + if (currentPathMightBeHydrogenMonorepo && !ignoreHydrogenMonorepo) { + path = execaSync('npm', ['prefix']).stdout.trim() + } + if (fileExistsSync(joinPath(path, 'package.json'))) { + options.pluginAdditions = { + core: ['@shopify/cli-hydrogen'], + path, + } + } + } + + super(options) + + if (isDevelopment()) { + // @ts-expect-error: This is a private method that we are overriding. OCLIF doesn't provide a way to extend it. + this.determinePriority = this.customPriority + } + } + + /** + * Override runHook to make init hooks non-blocking for faster startup. + * Init hooks (app-init, hydrogen-init) set up LocalStorage and check hydrogen — + * these are setup tasks that don't need to complete before commands run. + * + * @param event - The hook event name. + * @param opts - Options to pass to the hook. + * @param timeout - Optional timeout for the hook. + * @param captureErrors - Whether to capture errors instead of throwing. + * @returns The hook result with successes and failures arrays. + */ + // @ts-expect-error: overriding with looser types for hook interception + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async runHook(event: string, opts: any, timeout?: number, captureErrors?: boolean): Promise { + if (event === 'init' || event === 'prerun' || event === 'postrun') { + // Fire init, prerun, and postrun hooks in background — they don't need to block. + // - Init hooks: background setup (clearing caches, setting env vars) + // - Prerun hooks: analytics tracking, upgrade checks (best-effort) + // - Postrun hooks: analytics reporting (best-effort) + // eslint-disable-next-line no-void + void super.runHook(event, opts, timeout, captureErrors) + return {successes: [], failures: []} + } + return super.runHook(event, opts, timeout, captureErrors) + } + + /** + * Custom priority logic for plugin commands. + * In development mode, external cli-hydrogen commands take priority over bundled ones. + * + * @param commands - The commands to sort. + * @returns The highest priority command. + */ + customPriority(commands: Command.Loadable[]): Command.Loadable | undefined { + const oclifPlugins = this.pjson.oclif.plugins ?? [] + const commandPlugins = commands.sort((aCommand, bCommand) => { + // eslint-disable-next-line no-restricted-syntax + const pluginAliasA = aCommand.pluginAlias ?? 'A-Cannot-Find-This' + // eslint-disable-next-line no-restricted-syntax + const pluginAliasB = bCommand.pluginAlias ?? 'B-Cannot-Find-This' + const aIndex = oclifPlugins.indexOf(pluginAliasA) + const bIndex = oclifPlugins.indexOf(pluginAliasB) + + // If there is an external cli-hydrogen plugin, its commands should take priority over bundled ('core') commands + if (aCommand.pluginType === 'core' && bCommand.pluginAlias === '@shopify/cli-hydrogen') { + return 1 + } + + if (aCommand.pluginAlias === '@shopify/cli-hydrogen' && bCommand.pluginType === 'core') { + return -1 + } + + // All other cases are the default implementation from the private `determinePriority` method + if (aCommand.pluginType === 'core' && bCommand.pluginType === 'core') { + return aIndex - bIndex + } + + if (bCommand.pluginType === 'core' && aCommand.pluginType !== 'core') { + return 1 + } + + if (aCommand.pluginType === 'core' && bCommand.pluginType !== 'core') { + return -1 + } + + if (aCommand.pluginType === 'jit' && bCommand.pluginType !== 'jit') { + return 1 + } + + if (bCommand.pluginType === 'jit' && aCommand.pluginType !== 'jit') { + return -1 + } + + return 0 + }) + return commandPlugins[0] + } +} diff --git a/packages/cli/src/bootstrap.ts b/packages/cli/src/bootstrap.ts index f32dd900065..f543d4a143e 100644 --- a/packages/cli/src/bootstrap.ts +++ b/packages/cli/src/bootstrap.ts @@ -62,6 +62,9 @@ async function runShopifyCLI({development}: RunShopifyCLIOptions) { moduleURL: import.meta.url, development, }) + // Force exit after command completes. Background hooks (init, prerun analytics, + // postrun) are best-effort and shouldn't delay the user. + process.exit(0) } export default runShopifyCLI From 60b9fca7c9cc19381d9a4c1f99ecb92317847a44 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Tue, 14 Apr 2026 16:07:12 -0400 Subject: [PATCH 3/4] Rewrite lazy-loading-options.md to match actual branch contents Simplify code examples to high-level descriptions of the approach rather than full code listings. The branches are the source of truth for implementation details. Update performance numbers, evaluation sections, and comparison table to reflect the optimized results with ShopifyConfig non-blocking hooks. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/lazy-loading-options.md | 440 ++++++----------------------------- 1 file changed, 73 insertions(+), 367 deletions(-) diff --git a/docs/lazy-loading-options.md b/docs/lazy-loading-options.md index 060e00d165f..5ce442bbde6 100644 --- a/docs/lazy-loading-options.md +++ b/docs/lazy-loading-options.md @@ -6,7 +6,7 @@ The Shopify CLI uses [oclif](https://oclif.io/) as its command framework. The cu This document compares three approaches to fixing that, evaluated against these principles: -> **Implementation plan:** [`.claude/plans/lazy-loading-refactor.md`](lazy-loading-refactor-plan.md) +> **Implementation plan:** [`docs/lazy-loading-refactor-plan.md`](lazy-loading-refactor-plan.md) ### Principles @@ -19,7 +19,7 @@ This document compares three approaches to fixing that, evaluated against these --- -## Option A: Custom Command Registry (current branch) +## Option A: Custom Command Registry | | | |---|---| @@ -30,185 +30,23 @@ This document compares three approaches to fixing that, evaluated against these **Override oclif's `Config.runCommand()` with a hand-rolled registry that maps command IDs to dynamic imports.** -Keep the `explicit` strategy but bypass oclif's default loading by subclassing `Config` and intercepting command execution. A new `command-registry.ts` maps each command ID to a dynamic `import()` call. - -### Key files - -**`bootstrap.ts`** — new lightweight entry point (replaces `index.ts` as the startup file). It moves the global proxy agent setup, signal handlers, and `uncaughtException` handler out of `index.ts` so they run without importing any command modules: - -```ts -// bootstrap.ts — intentionally imports nothing heavy -import {loadCommand} from './command-registry.js' -import {createGlobalProxyAgent} from 'global-agent' -import {runCLI} from '@shopify/cli-kit/node/cli' -import fs from 'fs' - -createGlobalProxyAgent({ - environmentVariableNamespace: 'SHOPIFY_', - forceGlobalAgent: true, - socketConnectionTimeout: 60000, -}) - -process.on('uncaughtException', async (err) => { - try { - const {FatalError} = await import('@shopify/cli-kit/node/error') - if (err instanceof FatalError) { - const {renderFatalError} = await import('@shopify/cli-kit/node/ui') - renderFatalError(err) - } else { - fs.writeSync(process.stderr.fd, `${err.stack ?? err.message ?? err}\n`) - } - } catch { - fs.writeSync(process.stderr.fd, `${err.stack ?? err.message ?? err}\n`) - } - process.exit(1) -}) - -const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'] -signals.forEach((signal) => { - process.on(signal, () => { process.exit(1) }) -}) - -async function runShopifyCLI({development}: {development: boolean}) { - await runCLI({ - moduleURL: import.meta.url, - development, - lazyCommandLoader: loadCommand, - }) -} - -export default runShopifyCLI -``` - -**`command-registry.ts`** — maps command IDs to dynamic imports: - -```ts -const cliCommands: Record Promise> = { - version: () => import('./cli/commands/version.js'), - search: () => import('./cli/commands/search.js'), - 'auth:logout': () => import('./cli/commands/auth/logout.js'), - // ... every CLI-local command (17 total) -} - -const appCommandIds = ['app:build', 'app:dev', 'app:deploy', /* ... 33 total */] - -// Helper to find the default export or first class with a .run() method -function searchForDefault(module: any): any { - if (module.default?.run) return module.default - for (const value of Object.values(module)) { - if (typeof value === 'function' && typeof (value as any).run === 'function') return value - } - return undefined -} - -export async function loadCommand(id: string): Promise { - // CLI-local commands: import just that file - const cliLoader = cliCommands[id] - if (cliLoader) { - const module = await cliLoader() - return searchForDefault(module) - } - - // App commands: import the package, grab one command - if (appCommandIds.includes(id)) { - const {commands} = await import('@shopify/app') - return commands[id] - } - - // Theme commands - if (id.startsWith('theme:')) { - const themeModule = await import('@shopify/theme') - return themeModule.default?.[id] - } - - // Hydrogen commands - if (id.startsWith('hydrogen:')) { - const {COMMANDS} = await import('@shopify/cli-hydrogen') - return COMMANDS?.[id] - } - - // Plugin commands (oclif built-ins, did-you-mean) - if (id === 'commands') { - const {commands} = await import('@oclif/plugin-commands') - return commands[id] - } - if (id.startsWith('plugins')) { - const {commands} = await import('@oclif/plugin-plugins') - return commands[id] - } - if (id.startsWith('config:autocorrect')) { - const {DidYouMeanCommands} = await import('@shopify/plugin-did-you-mean') - return DidYouMeanCommands[id] - } - - return undefined -} -``` - -**`custom-oclif-loader.ts`** — subclass of oclif's `Config`. Note: `ShopifyConfig` already existed before lazy loading — it handles hydrogen monorepo detection in dev mode and a custom plugin priority method. The lazy loading additions are `lazyCommandLoader`, `setLazyCommandLoader`, and the `runCommand` override: - -```ts -import {Command, Config} from '@oclif/core' - -export class ShopifyConfig extends Config { - private lazyCommandLoader?: (id: string) => Promise - - constructor(options: Options) { - // Pre-existing: hydrogen monorepo detection and pluginAdditions for dev mode - // Pre-existing: custom determinePriority override for dev mode - super(options) - } - - setLazyCommandLoader(loader: LazyCommandLoader): void { - this.lazyCommandLoader = loader - } - - async runCommand(id: string, argv: string[] = [], cachedCommand?: Command.Loadable | null): Promise { - if (this.lazyCommandLoader) { - const cmd = cachedCommand ?? this.findCommand(id) - if (cmd) { - const commandClass = await this.lazyCommandLoader(id) - if (commandClass) { - commandClass.id = id - // Note: rootPlugin is a private oclif property accessed via cast - commandClass.plugin = cmd.plugin ?? (this as any).rootPlugin - await this.runHook('prerun', {argv, Command: commandClass}) - const result = await commandClass.run(argv, this) - await this.runHook('postrun', {argv, Command: commandClass, result}) - return result - } - } - } - return super.runCommand(id, argv, cachedCommand) - } -} -``` - -**`package.json`** hooks point to individual files instead of the barrel: - -```json -"hooks": { - "init": ["./dist/hooks/app-init.js", "./dist/hooks/hydrogen-init.js"], - "command_not_found": "./dist/hooks/did-you-mean.js", - "tunnel_start": "./dist/hooks/tunnel-start.js" -} -``` - -Each hook file is a one-liner re-export: - -```ts -// hooks/app-init.ts -export {AppInitHook as default} from '@shopify/app' -``` +Keep the `explicit` strategy but bypass oclif's default loading by subclassing `Config` and intercepting command execution. A `command-registry.ts` maps each command ID to a dynamic `import()` call. `ShopifyConfig` overrides both `runCommand()` (to use the registry instead of oclif's loading) and `runHook()` (to make init hooks non-blocking). + +### How it works + +1. `bootstrap.ts` replaces `index.ts` as the entry point — imports nothing heavy +2. `command-registry.ts` maps every command ID to a lazy `import()` — e.g. `'app:dev'` imports from `@shopify/app` only when that command runs +3. `ShopifyConfig.runCommand()` bypasses oclif's `cmd.load()` and calls the registry loader directly, then manually fires prerun/postrun hooks in the background +4. `ShopifyConfig.runHook('init')` fires init hooks in the background (non-blocking) ### Evaluation -- **Startup performance:** Good. Avoids importing `@shopify/app`, `@shopify/theme`, and `@shopify/cli-hydrogen` until the target command runs. Does not achieve per-command isolation within a package (running `app:dev` still loads all of `@shopify/app`). -- **Idiomatic code:** Low. Overrides `runCommand()` to bypass oclif's own command loading, manually calling `prerun`/`postrun` hooks and setting `.id`/`.plugin` on command classes. The `searchForDefault()` heuristic — scanning module exports for something with `.run()` — exists because the registry bypasses the normal contract where oclif knows what a command class looks like. This reimplements what oclif's `pattern` strategy does natively. -- **Ease of maintenance:** Low. `command-registry.ts` is a manual mapping that must be kept in sync with commands across 5+ packages. Adding `app:new-thing` in `@shopify/app` requires also updating the registry in `@shopify/cli` — this will drift. The `runCommand()` override reimplements oclif lifecycle internals; if oclif adds a new lifecycle step, this path silently skips it. `customPluginName` is not set on lazy-loaded commands, breaking analytics attribution — fixing it requires threading it through both the registry and the `runCommand()` override. -- **Testability:** Low. The `runCommand()` override, `searchForDefault()` heuristic, and command-registry mappings are all custom infrastructure that need their own tests. The registry correctness is hard to cover exhaustively since it must match every command export shape across all packages. -- **Codebase compatibility:** High. Works with all packages as they exist today — no structural changes required in `@shopify/app`, `@shopify/theme`, or the external `@shopify/cli-hydrogen`. Only requires that each package exports commands by name, which they already do. -- **Small refactor scope:** Medium. Adds `bootstrap.ts`, `command-registry.ts`, and hook files in `@shopify/cli`. Modifies `ShopifyConfig` in `cli-kit`. Does not touch plugin packages. Moderate diff, but the new files are net-new custom infrastructure. +- **Startup performance:** Good. Defers all heavy package imports until the target command runs. +- **Idiomatic code:** Low. Overrides `runCommand()` to bypass oclif's lifecycle, manually setting `.id`/`.plugin` on command classes and manually firing hooks. Reimplements what oclif's `pattern` strategy does natively. +- **Ease of maintenance:** Low. `command-registry.ts` is a manual mapping that must stay in sync with commands across 5+ packages. The `runCommand()` override reimplements oclif lifecycle internals — if oclif adds a new lifecycle step, this path silently skips it. +- **Testability:** Low. The `runCommand()` override, `searchForDefault()` heuristic, and command-registry mappings are all custom infrastructure that need their own tests. +- **Codebase compatibility:** High. Works with all packages as they exist today — no structural changes required. +- **Small refactor scope:** Medium. Adds `bootstrap.ts`, `command-registry.ts`, and hook files. Modifies `ShopifyConfig` in `cli-kit`. --- @@ -220,213 +58,81 @@ export {AppInitHook as default} from '@shopify/app' | **Perf baseline (`main`)** | 1.778s ± 0.106s (range 1.647s … 2.010s) | | **Perf result** | 0.412s ± 0.012s (range 0.397s … 0.445s) | | **Delta** | **-76.8%** | -| **Note** | Uses ShopifyConfig with non-blocking init/prerun/postrun hooks + oclif pattern strategy for native lazy loading. Heavy packages NOT loaded for non-app/theme/hydrogen commands. | **Switch to oclif's native `pattern` discovery with a `commands/` directory where each file is a one-line re-export.** -oclif's `pattern` strategy discovers commands by scanning a `commands/` directory. Each file's path becomes the command ID (e.g., `commands/app/dev.ts` → `app:dev`). oclif already lazy-loads each file individually when the command runs — no custom `Config` subclass needed. - -### Key files - -**`package.json`** — switch strategy: - -```json -"oclif": { - "commands": { - "strategy": "pattern", - "target": "./dist/commands" - } -} -``` - -**`commands/`** directory with thin re-exports — one file per command: - -``` -packages/cli/src/commands/ -├── version.ts ← local command (full implementation) -├── search.ts ← local command (full implementation) -├── upgrade.ts ← local command (full implementation) -├── auth/ -│ ├── login.ts ← local command -│ └── logout.ts ← local command -├── app/ -│ ├── dev.ts ← re-export from @shopify/app -│ ├── build.ts ← re-export from @shopify/app -│ ├── deploy.ts ← re-export from @shopify/app -│ ├── info.ts ← re-export from @shopify/app -│ ├── config/ -│ │ ├── link.ts -│ │ └── use.ts -│ └── ... -├── theme/ -│ ├── dev.ts ← re-export from @shopify/theme -│ ├── push.ts ← re-export from @shopify/theme -│ └── ... -└── hydrogen/ - ├── dev.ts ← re-export from @shopify/cli-hydrogen - └── ... -``` - -Each re-export file is minimal: - -```ts -// commands/app/dev.ts -import {commands} from '@shopify/app' -export default commands['app:dev'] -``` - -```ts -// commands/theme/push.ts -import ThemeCommands from '@shopify/theme' -export default ThemeCommands['theme:push'] -``` - -```ts -// commands/hydrogen/dev.ts -import {COMMANDS} from '@shopify/cli-hydrogen' -export default COMMANDS['hydrogen:dev'] -``` - -**`bootstrap.ts`** simplifies — no custom loader needed, but still needs the proxy agent setup, signal handlers, and `uncaughtException` handler (moved from `index.ts`): - -```ts -import {createGlobalProxyAgent} from 'global-agent' -import {runCLI} from '@shopify/cli-kit/node/cli' -import fs from 'fs' - -createGlobalProxyAgent({/* ... */}) -process.on('uncaughtException', async (err) => { /* ... */ }) -// ... signal handlers, column detection ... - -async function runShopifyCLI({development}: {development: boolean}) { - await runCLI({ - moduleURL: import.meta.url, - development, - // no lazyCommandLoader — oclif handles it - }) -} - -export default runShopifyCLI -``` - -**`cli-launcher.ts`** drops the lazy loader plumbing but still needs `ShopifyConfig` (not stock `Config`) because it contains hydrogen monorepo detection and custom plugin priority logic unrelated to lazy loading: - -```ts -import {ShopifyConfig} from './custom-oclif-loader.js' -import {run, flush, Errors, settings} from '@oclif/core' - -const config = new ShopifyConfig({root: fileURLToPath(options.moduleURL)}) -await config.load() -// no setLazyCommandLoader call — oclif handles lazy loading via pattern strategy -await run(options.argv, config) -``` +oclif's `pattern` strategy discovers commands by scanning a `commands/` directory. Each file's path becomes the command ID (e.g., `commands/app/dev.ts` → `app:dev`). oclif lazy-loads each file individually when the command runs — no `runCommand()` override needed. + +### How it works + +1. `bootstrap.ts` replaces `index.ts` as the entry point — imports nothing heavy, calls `process.exit(0)` after the command completes so background hooks don't delay the user +2. `package.json` switches from `strategy: "explicit"` to `strategy: "pattern"` pointing at `./dist/commands` +3. `commands/` directory contains ~108 thin re-export files — one per command. CLI-local commands (version, search, etc.) re-export from `../cli/commands/`. Package commands re-export from their source package (e.g. `import {commands} from '@shopify/app'; export default commands['app:dev']`) +4. Hooks are split into individual files with dynamic imports instead of referencing the barrel `index.ts`. Init hooks (`app-init.ts`, `hydrogen-init.ts`) are no-ops at startup — the real init logic runs lazily via the prerun hook on first relevant command +5. `ShopifyConfig` in `cli-kit` overrides `runHook()` to make `init`, `prerun`, and `postrun` hooks non-blocking (fire-and-forget). It does NOT override `runCommand()` — oclif's native pattern strategy handles command loading +6. The prerun hook assigns `customPluginName` for analytics attribution based on command ID prefix, and defers app/hydrogen init hooks until a relevant command runs ### Evaluation -- **Startup performance:** Good. Same as Option A — avoids importing `@shopify/app`, `@shopify/theme`, and `@shopify/cli-hydrogen` until the target command runs. Does not achieve per-command isolation within a package (running `app:dev` still loads all of `@shopify/app`). -- **Idiomatic code:** High. Uses oclif's native `pattern` strategy — the framework's built-in answer to lazy loading. No `runCommand()` override, no manual registry. The filesystem convention (`commands/app/dev.ts` → `app:dev`) is the same pattern used by Salesforce CLI, Heroku CLI, and other large oclif projects. `ShopifyConfig` is still required for hydrogen monorepo detection and plugin priority, but the lazy-loading parts are removed. -- **Ease of maintenance:** High. No manual registry to keep in sync — adding a command means adding a one-line file. The filesystem is the source of truth. Won't break on oclif upgrades since it uses the supported API. `customPluginName` needs a new home (each re-export or a hook), but the fix is trivial. The ~108 re-export files are one line each, grep-able, git-blame-able, and individually deletable. -- **Testability:** High. Removes the `runCommand()` override entirely — oclif handles command loading, so there is no custom loading machinery to test. Each re-export file is independently importable. `bootstrap.ts` simplifies because it no longer threads a `lazyCommandLoader` through the call stack. -- **Codebase compatibility:** High. Works with all packages as they exist today. Only requires that each package exports commands by name, which they already do. The external `@shopify/cli-hydrogen` needs no changes. -- **Small refactor scope:** Medium. Adds ~108 one-line re-export files in a `commands/` directory, switches `package.json` to `strategy: "pattern"`, and carries over the `bootstrap.ts` and hook-splitting work. No changes to plugin packages. The re-export files are trivial individually but the file count is high. +- **Startup performance:** Good. Equivalent to Option A — heavy packages are not imported for non-app/theme/hydrogen commands. +- **Idiomatic code:** High. Uses oclif's native `pattern` strategy — the framework's built-in answer to lazy loading. No `runCommand()` override, no manual registry. `ShopifyConfig` is minimal — only overrides `runHook()` for non-blocking hooks, plus pre-existing hydrogen dev-mode logic. +- **Ease of maintenance:** High. No manual registry to keep in sync — adding a command means adding a one-line file. The filesystem is the source of truth. The ~108 re-export files are one line each, grep-able, git-blame-able, and individually deletable. +- **Testability:** High. No custom command loading machinery to test. Each re-export file is independently importable. +- **Codebase compatibility:** High. Works with all packages as they exist today. The external `@shopify/cli-hydrogen` needs no changes. +- **Small refactor scope:** Medium. Adds ~108 one-line re-export files, switches `package.json` strategy, adds `bootstrap.ts`, splits hooks into individual files, adds `ShopifyConfig` to `cli-kit`. --- -## Option C: Proper oclif Plugin Architecture (with Wrapper Packages) +## Option C: Plugin Wrapper Architecture | | | |---|---| -| **Branch** | _TODO: add branch URL (based on Option B branch)_ | -| **Perf baseline (`main`)** | 1.778s ± 0.106s | -| **Perf result** | _TODO: `shopify version` median time_ | -| **Delta vs B** | _TODO_ | - -**Create thin wrapper plugin packages (`plugin-app`, `plugin-theme`, `plugin-hydrogen`) that re-export commands from the real packages, making each a proper oclif plugin without modifying the underlying packages.** - -oclif's plugin system is designed for exactly this — a CLI composed of multiple independent command packages. Each plugin declares its own commands, hooks, and manifest. The root CLI just lists which plugins to load, and oclif handles discovery and lazy loading per-plugin. - -### Key files - -**Root `packages/cli/package.json`** — declare plugins: - -```json -"oclif": { - "commands": { - "strategy": "pattern", - "target": "./dist/commands" - }, - "plugins": [ - "@shopify/app", - "@shopify/theme", - "@shopify/cli-hydrogen", - "@shopify/plugin-did-you-mean", - "@oclif/plugin-commands", - "@oclif/plugin-plugins" - ] -} -``` - -The root CLI only has its own local commands (`version`, `search`, `upgrade`, `auth:*`, etc.). - -**Each plugin package** gets its own oclif config. For example, `packages/app/package.json`: - -```json -"oclif": { - "commands": { - "strategy": "pattern", - "target": "./dist/commands" - }, - "hooks": { - "init": "./dist/hooks/init.js", - "sensitive_command_metadata": "./dist/hooks/sensitive-metadata.js", - "public_command_metadata": "./dist/hooks/public-metadata.js" - } -} -``` - -**Plugin command files** use oclif's filesystem convention: - -``` -packages/app/src/commands/ -├── app/ -│ ├── dev.ts ← full command implementation -│ ├── build.ts -│ ├── deploy.ts -│ └── config/ -│ ├── link.ts -│ └── use.ts -``` - -**No barrel files needed.** Each plugin is self-contained. The root CLI's `index.ts` shrinks to almost nothing: - -```ts -import {runCLI} from '@shopify/cli-kit/node/cli' - -async function runShopifyCLI({development}: {development: boolean}) { - await runCLI({moduleURL: import.meta.url, development}) -} - -export default runShopifyCLI -``` +| **Branch** | `lazy-loading-option-c` (based on `lazy-loading-option-b`) | +| **Perf baseline (`main`)** | 1.778s ± 0.106s (range 1.647s … 2.010s) | +| **Perf result** | 0.554s ± 0.025s (range 0.538s … 0.639s) | +| **Delta vs main** | **-68.8%** | +| **Delta vs B** | +34.4% slower (0.554s vs 0.412s) | + +**Create thin wrapper plugin packages (`plugin-app`, `plugin-theme`, `plugin-hydrogen`) that re-export commands from the real packages, registered as proper oclif plugins.** + +Extends Option B by moving re-export files out of `packages/cli/` into dedicated wrapper packages. The root CLI shrinks to only CLI-local commands (17 commands). Each wrapper is a proper oclif plugin with its own manifest and hooks. + +### How it works + +1. Three new packages: `packages/plugin-app/` (34 commands, 3 hooks), `packages/plugin-theme/` (20 commands), `packages/plugin-hydrogen/` (26 commands, 1 hook) +2. Each wrapper uses `strategy: "pattern"` with thin re-export files identical to Option B +3. The root CLI's `package.json` registers them via `oclif.plugins` array, alongside `@shopify/plugin-did-you-mean`, `@oclif/plugin-commands`, and `@oclif/plugin-plugins`. Plugins must be in `dependencies` (not `devDependencies`) for oclif to discover them +4. App/hydrogen init hooks and app metadata hooks move from the root CLI into `plugin-app` and `plugin-hydrogen` respectively — co-located with the commands they serve +5. The root CLI's hooks shrink to: `prerun`, `postrun`, `command_not_found`, `tunnel_start`, `tunnel_provider`, `update` +6. Same `ShopifyConfig` from Option B (non-blocking hooks) and same `process.exit(0)` in `bootstrap.ts` ### Evaluation -- **Startup performance:** Best. Full per-plugin isolation — oclif lazy-loads each plugin, so a plugin's code is only loaded when one of its commands runs. However, the marginal improvement over Option B is small because the expensive imports are cross-package, not intra-package. -- **Idiomatic code:** Highest. The textbook oclif architecture — how Salesforce CLI, Heroku CLI, and other large oclif CLIs are structured. Each plugin declares its own commands, hooks, and manifest. No barrel files, no manual registries, no `runCommand()` override. `customPluginName` becomes unnecessary since oclif tracks plugin ownership natively. -- **Ease of maintenance:** High. Each wrapper plugin is self-contained — adding a command means adding a one-line re-export in the appropriate wrapper. Hooks are co-located with their plugin. The wrapper approach avoids restructuring `@shopify/app`, `@shopify/theme`, or coordinating with the Hydrogen team. `customPluginName` becomes unnecessary since oclif tracks plugin ownership natively. -- **Testability:** Highest in theory. Each plugin is independently testable with its own manifest. No custom loading infrastructure to test. The wrapper packages are trivial (one-line re-exports) so there's little to break. -- **Codebase compatibility:** High. Wrapper packages (`plugin-app`, `plugin-theme`, `plugin-hydrogen`) live in the monorepo and depend on the real packages as regular npm dependencies. No changes required to `@shopify/app`, `@shopify/theme`, or the external `@shopify/cli-hydrogen`. -- **Small refactor scope:** Medium. Creates 3 new wrapper packages with one-line re-export files, moves hooks from root CLI into wrappers, and updates root CLI's plugin config. The re-export files from Option B are redistributed, not rewritten. No changes to existing plugin packages. +- **Startup performance:** Good. ~140ms slower than Option B due to oclif resolving 6 plugin packages at startup, but still 69% faster than main. +- **Idiomatic code:** Highest. Textbook oclif plugin architecture. Each plugin declares its own commands, hooks, and manifest. The root CLI is a thin orchestrator. +- **Ease of maintenance:** High. Each wrapper plugin is self-contained. Hooks are co-located with their plugin. Adding a command means adding a one-line file in the appropriate wrapper. +- **Testability:** High. Each plugin is independently testable with its own manifest. No custom loading infrastructure to test. +- **Codebase compatibility:** High. Wrapper packages live in the monorepo and depend on the real packages as regular dependencies. No changes required to `@shopify/app`, `@shopify/theme`, or `@shopify/cli-hydrogen`. +- **Small refactor scope:** Medium-large. Creates 3 new packages with build/lint/type-check targets, moves hooks from root CLI into wrappers, requires knip and nx configuration for the new packages. --- ## Comparison Summary -| Principle | A: Custom Registry | B: Pattern + Re-exports | C: Plugin Architecture | +| Principle | A: Custom Registry | B: Pattern + Re-exports | C: Plugin Wrappers | |---|---|---|---| -| **Startup performance** | Good — per-package isolation | Good — per-package isolation | Best — per-plugin isolation (marginal delta over B) | -| **Idiomatic code** | Low — overrides `runCommand()`, reimplements framework internals | High — uses oclif's native `pattern` strategy | Highest — textbook oclif plugin architecture | -| **Ease of maintenance** | Low — manual registry drifts, `runCommand()` override to keep current | High — filesystem is the source of truth, no custom loading code | Mixed — best structure long-term, but major cross-repo refactor to get there | -| **Testability** | Low — registry mappings, `searchForDefault()`, and `runCommand()` override all need tests | High — no custom loading infrastructure to test | Highest in theory, but migration creates regression risk | -| **Codebase compatibility** | High — works with all packages as-is | High — works with all packages as-is | Low — requires cross-team coordination and restructuring external `cli-hydrogen` | -| **Small refactor scope** | Medium — new files in `cli` and `cli-kit`, no plugin changes | Medium — ~108 one-line files, no plugin changes | Very large — touches every package, cross-repo coordination | +| **Startup performance** | 0.388s (-78%) | 0.412s (-77%) | 0.554s (-69%) | +| **Idiomatic code** | Low — overrides `runCommand()`, reimplements oclif internals | High — native `pattern` strategy, `ShopifyConfig` only overrides `runHook()` | Highest — textbook oclif plugin architecture | +| **Ease of maintenance** | Low — manual registry drifts from source of truth | High — filesystem is the source of truth | High — each wrapper is self-contained | +| **Testability** | Low — registry, `searchForDefault()`, and `runCommand()` all need tests | High — no custom loading infrastructure to test | High — each plugin testable independently | +| **Codebase compatibility** | High — works with all packages as-is | High — works with all packages as-is | High — wrapper packages, no changes to source packages | +| **Refactor scope** | Medium — new files in `cli` and `cli-kit` | Medium — ~108 re-export files, hook split, `ShopifyConfig` | Medium-large — 3 new packages, nx/knip config | + +### Shared infrastructure (Options B and C) + +Both Options B and C share these components that differ from Option A: + +- **`bootstrap.ts`** — lightweight entry point that avoids importing heavy packages at module load time. Calls `process.exit(0)` after the command completes so background hooks don't delay the user. +- **`ShopifyConfig`** in `cli-kit` — overrides `runHook()` to make `init`, `prerun`, and `postrun` hooks fire-and-forget (non-blocking). Does NOT override `runCommand()`. Also contains pre-existing hydrogen monorepo dev-mode detection and custom plugin priority logic. +- **Node.js compile cache** — `bin/run.js` calls `enableCompileCache()` for faster repeat startups. +- **`customPluginName`** — set in the prerun hook based on command ID prefix, replacing the eager assignment that was previously done in `index.ts` at import time. From 1a61fa9af6b515f503beadd64ad3bb79b78fda1f Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Tue, 14 Apr 2026 16:21:14 -0400 Subject: [PATCH 4/4] Remove lazy loading docs from repo (moved to Google Docs) Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/lazy-loading-options.md | 138 ------- docs/lazy-loading-refactor-plan.md | 616 ----------------------------- 2 files changed, 754 deletions(-) delete mode 100644 docs/lazy-loading-options.md delete mode 100644 docs/lazy-loading-refactor-plan.md diff --git a/docs/lazy-loading-options.md b/docs/lazy-loading-options.md deleted file mode 100644 index 5ce442bbde6..00000000000 --- a/docs/lazy-loading-options.md +++ /dev/null @@ -1,138 +0,0 @@ -# Lazy Command Loading: Architectural Options - -## Background - -The Shopify CLI uses [oclif](https://oclif.io/) as its command framework. The current architecture uses the **explicit** command discovery strategy, where all ~108 commands are exported from a single barrel file (`index.ts`). This means running `shopify version` eagerly imports `@shopify/app`, `@shopify/theme`, `@shopify/cli-hydrogen`, `@oclif/plugin-commands`, `@oclif/plugin-plugins`, and `@shopify/plugin-did-you-mean` — even though only one command runs. - -This document compares three approaches to fixing that, evaluated against these principles: - -> **Implementation plan:** [`docs/lazy-loading-refactor-plan.md`](lazy-loading-refactor-plan.md) - -### Principles - -1. **Startup performance** — The CLI must only pay the import cost of the command being run. Running `shopify version` should not import `@shopify/app`, `@shopify/theme`, or `@shopify/cli-hydrogen`. -2. **Idiomatic code** — Apply well-understood patterns appropriate for the tools and problems at hand. When using a framework like oclif, work *with* its conventions rather than around them. Avoid reimplementing framework internals or relying on undocumented behavior. -3. **Ease of maintenance** — The solution should be easy to evolve. Adding, removing, or renaming a command should be a small, obvious change. Manual registries that drift from the source of truth are a maintenance liability. The less custom machinery layered on top of the framework, the less there is to break on upgrades. -4. **Testability** — The solution should minimize the amount of custom infrastructure that needs its own tests. Framework-provided behavior doesn't need to be re-tested; hand-rolled replacements for framework behavior do. -5. **Codebase compatibility** — The solution must work with the codebase as it exists today. External packages (`@shopify/cli-hydrogen`) live outside this monorepo. Each plugin package has its own export conventions and build pipeline. The approach must be compatible with these realities without requiring coordinated changes across teams or repositories. -6. **Small refactor scope** *(least important)* — All else being equal, prefer the approach that requires fewer file moves, fewer package-level structural changes, and less CI/build pipeline rework to land. A smaller diff is easier to review, less likely to introduce regressions, and faster to ship. - ---- - -## Option A: Custom Command Registry - -| | | -|---|---| -| **Branch** | `faster-startup-4` | -| **Perf baseline (`main`)** | 1.778s ± 0.106s (range 1.647s … 2.010s) | -| **Perf result** | 0.388s ± 0.024s (range 0.362s … 0.441s) | -| **Delta** | **-78.2%** | - -**Override oclif's `Config.runCommand()` with a hand-rolled registry that maps command IDs to dynamic imports.** - -Keep the `explicit` strategy but bypass oclif's default loading by subclassing `Config` and intercepting command execution. A `command-registry.ts` maps each command ID to a dynamic `import()` call. `ShopifyConfig` overrides both `runCommand()` (to use the registry instead of oclif's loading) and `runHook()` (to make init hooks non-blocking). - -### How it works - -1. `bootstrap.ts` replaces `index.ts` as the entry point — imports nothing heavy -2. `command-registry.ts` maps every command ID to a lazy `import()` — e.g. `'app:dev'` imports from `@shopify/app` only when that command runs -3. `ShopifyConfig.runCommand()` bypasses oclif's `cmd.load()` and calls the registry loader directly, then manually fires prerun/postrun hooks in the background -4. `ShopifyConfig.runHook('init')` fires init hooks in the background (non-blocking) - -### Evaluation - -- **Startup performance:** Good. Defers all heavy package imports until the target command runs. -- **Idiomatic code:** Low. Overrides `runCommand()` to bypass oclif's lifecycle, manually setting `.id`/`.plugin` on command classes and manually firing hooks. Reimplements what oclif's `pattern` strategy does natively. -- **Ease of maintenance:** Low. `command-registry.ts` is a manual mapping that must stay in sync with commands across 5+ packages. The `runCommand()` override reimplements oclif lifecycle internals — if oclif adds a new lifecycle step, this path silently skips it. -- **Testability:** Low. The `runCommand()` override, `searchForDefault()` heuristic, and command-registry mappings are all custom infrastructure that need their own tests. -- **Codebase compatibility:** High. Works with all packages as they exist today — no structural changes required. -- **Small refactor scope:** Medium. Adds `bootstrap.ts`, `command-registry.ts`, and hook files. Modifies `ShopifyConfig` in `cli-kit`. - ---- - -## Option B: Pattern Strategy with Thin Re-export Files - -| | | -|---|---| -| **Branch** | `lazy-loading-option-b` | -| **Perf baseline (`main`)** | 1.778s ± 0.106s (range 1.647s … 2.010s) | -| **Perf result** | 0.412s ± 0.012s (range 0.397s … 0.445s) | -| **Delta** | **-76.8%** | - -**Switch to oclif's native `pattern` discovery with a `commands/` directory where each file is a one-line re-export.** - -oclif's `pattern` strategy discovers commands by scanning a `commands/` directory. Each file's path becomes the command ID (e.g., `commands/app/dev.ts` → `app:dev`). oclif lazy-loads each file individually when the command runs — no `runCommand()` override needed. - -### How it works - -1. `bootstrap.ts` replaces `index.ts` as the entry point — imports nothing heavy, calls `process.exit(0)` after the command completes so background hooks don't delay the user -2. `package.json` switches from `strategy: "explicit"` to `strategy: "pattern"` pointing at `./dist/commands` -3. `commands/` directory contains ~108 thin re-export files — one per command. CLI-local commands (version, search, etc.) re-export from `../cli/commands/`. Package commands re-export from their source package (e.g. `import {commands} from '@shopify/app'; export default commands['app:dev']`) -4. Hooks are split into individual files with dynamic imports instead of referencing the barrel `index.ts`. Init hooks (`app-init.ts`, `hydrogen-init.ts`) are no-ops at startup — the real init logic runs lazily via the prerun hook on first relevant command -5. `ShopifyConfig` in `cli-kit` overrides `runHook()` to make `init`, `prerun`, and `postrun` hooks non-blocking (fire-and-forget). It does NOT override `runCommand()` — oclif's native pattern strategy handles command loading -6. The prerun hook assigns `customPluginName` for analytics attribution based on command ID prefix, and defers app/hydrogen init hooks until a relevant command runs - -### Evaluation - -- **Startup performance:** Good. Equivalent to Option A — heavy packages are not imported for non-app/theme/hydrogen commands. -- **Idiomatic code:** High. Uses oclif's native `pattern` strategy — the framework's built-in answer to lazy loading. No `runCommand()` override, no manual registry. `ShopifyConfig` is minimal — only overrides `runHook()` for non-blocking hooks, plus pre-existing hydrogen dev-mode logic. -- **Ease of maintenance:** High. No manual registry to keep in sync — adding a command means adding a one-line file. The filesystem is the source of truth. The ~108 re-export files are one line each, grep-able, git-blame-able, and individually deletable. -- **Testability:** High. No custom command loading machinery to test. Each re-export file is independently importable. -- **Codebase compatibility:** High. Works with all packages as they exist today. The external `@shopify/cli-hydrogen` needs no changes. -- **Small refactor scope:** Medium. Adds ~108 one-line re-export files, switches `package.json` strategy, adds `bootstrap.ts`, splits hooks into individual files, adds `ShopifyConfig` to `cli-kit`. - ---- - -## Option C: Plugin Wrapper Architecture - -| | | -|---|---| -| **Branch** | `lazy-loading-option-c` (based on `lazy-loading-option-b`) | -| **Perf baseline (`main`)** | 1.778s ± 0.106s (range 1.647s … 2.010s) | -| **Perf result** | 0.554s ± 0.025s (range 0.538s … 0.639s) | -| **Delta vs main** | **-68.8%** | -| **Delta vs B** | +34.4% slower (0.554s vs 0.412s) | - -**Create thin wrapper plugin packages (`plugin-app`, `plugin-theme`, `plugin-hydrogen`) that re-export commands from the real packages, registered as proper oclif plugins.** - -Extends Option B by moving re-export files out of `packages/cli/` into dedicated wrapper packages. The root CLI shrinks to only CLI-local commands (17 commands). Each wrapper is a proper oclif plugin with its own manifest and hooks. - -### How it works - -1. Three new packages: `packages/plugin-app/` (34 commands, 3 hooks), `packages/plugin-theme/` (20 commands), `packages/plugin-hydrogen/` (26 commands, 1 hook) -2. Each wrapper uses `strategy: "pattern"` with thin re-export files identical to Option B -3. The root CLI's `package.json` registers them via `oclif.plugins` array, alongside `@shopify/plugin-did-you-mean`, `@oclif/plugin-commands`, and `@oclif/plugin-plugins`. Plugins must be in `dependencies` (not `devDependencies`) for oclif to discover them -4. App/hydrogen init hooks and app metadata hooks move from the root CLI into `plugin-app` and `plugin-hydrogen` respectively — co-located with the commands they serve -5. The root CLI's hooks shrink to: `prerun`, `postrun`, `command_not_found`, `tunnel_start`, `tunnel_provider`, `update` -6. Same `ShopifyConfig` from Option B (non-blocking hooks) and same `process.exit(0)` in `bootstrap.ts` - -### Evaluation - -- **Startup performance:** Good. ~140ms slower than Option B due to oclif resolving 6 plugin packages at startup, but still 69% faster than main. -- **Idiomatic code:** Highest. Textbook oclif plugin architecture. Each plugin declares its own commands, hooks, and manifest. The root CLI is a thin orchestrator. -- **Ease of maintenance:** High. Each wrapper plugin is self-contained. Hooks are co-located with their plugin. Adding a command means adding a one-line file in the appropriate wrapper. -- **Testability:** High. Each plugin is independently testable with its own manifest. No custom loading infrastructure to test. -- **Codebase compatibility:** High. Wrapper packages live in the monorepo and depend on the real packages as regular dependencies. No changes required to `@shopify/app`, `@shopify/theme`, or `@shopify/cli-hydrogen`. -- **Small refactor scope:** Medium-large. Creates 3 new packages with build/lint/type-check targets, moves hooks from root CLI into wrappers, requires knip and nx configuration for the new packages. - ---- - -## Comparison Summary - -| Principle | A: Custom Registry | B: Pattern + Re-exports | C: Plugin Wrappers | -|---|---|---|---| -| **Startup performance** | 0.388s (-78%) | 0.412s (-77%) | 0.554s (-69%) | -| **Idiomatic code** | Low — overrides `runCommand()`, reimplements oclif internals | High — native `pattern` strategy, `ShopifyConfig` only overrides `runHook()` | Highest — textbook oclif plugin architecture | -| **Ease of maintenance** | Low — manual registry drifts from source of truth | High — filesystem is the source of truth | High — each wrapper is self-contained | -| **Testability** | Low — registry, `searchForDefault()`, and `runCommand()` all need tests | High — no custom loading infrastructure to test | High — each plugin testable independently | -| **Codebase compatibility** | High — works with all packages as-is | High — works with all packages as-is | High — wrapper packages, no changes to source packages | -| **Refactor scope** | Medium — new files in `cli` and `cli-kit` | Medium — ~108 re-export files, hook split, `ShopifyConfig` | Medium-large — 3 new packages, nx/knip config | - -### Shared infrastructure (Options B and C) - -Both Options B and C share these components that differ from Option A: - -- **`bootstrap.ts`** — lightweight entry point that avoids importing heavy packages at module load time. Calls `process.exit(0)` after the command completes so background hooks don't delay the user. -- **`ShopifyConfig`** in `cli-kit` — overrides `runHook()` to make `init`, `prerun`, and `postrun` hooks fire-and-forget (non-blocking). Does NOT override `runCommand()`. Also contains pre-existing hydrogen monorepo dev-mode detection and custom plugin priority logic. -- **Node.js compile cache** — `bin/run.js` calls `enableCompileCache()` for faster repeat startups. -- **`customPluginName`** — set in the prerun hook based on command ID prefix, replacing the eager assignment that was previously done in `index.ts` at import time. diff --git a/docs/lazy-loading-refactor-plan.md b/docs/lazy-loading-refactor-plan.md deleted file mode 100644 index d861ed8a604..00000000000 --- a/docs/lazy-loading-refactor-plan.md +++ /dev/null @@ -1,616 +0,0 @@ -# Lazy Loading Refactor: Option B → Option C Implementation Plan - -> **Context document:** [`docs/lazy-loading-options.md`](lazy-loading-options.md) -> **Current branch:** `faster-startup` (has Option A implemented) -> **Goal:** Establish baseline measurements, implement Option B on a new branch, measure performance, then extend to Option C on a second branch. Both branches must pass CI (type-check, lint, bundle, tests, oclif-checks, knip). - ---- - -## Phase 0: Measure `main` baseline - -**Purpose:** Establish the baseline startup performance of `main` (no lazy loading). This is the number every other option is measured against. - -### Step 1: Build and measure - -```bash -git checkout main -pnpm build -hyperfine --warmup 3 --runs 20 'node packages/cli/bin/run.js version' -``` - -Record the full hyperfine output. This is the **worst-case baseline** — every command package (`@shopify/app`, `@shopify/theme`, `@shopify/cli-hydrogen`) is eagerly imported on every CLI invocation. - -### Step 2: Record baseline in `docs/lazy-loading-options.md` - -Add a baseline row at the top of each option's performance table. The `main` numbers will be reused in all subsequent phases — no need to re-measure. - -| Metric | `main` (no lazy loading) | -|--------|--------------------------| -| **Mean** | _Xs ± Ys_ | -| **Median** | _Xs_ | -| **Range** | _Xs … Xs_ | - ---- - -## Phase 1: Measure `faster-startup` branch (Option A) - -**Purpose:** Measure the current `faster-startup` branch (Option A — custom command registry) against the `main` baseline. - -### Step 1: Build and measure - -```bash -git checkout faster-startup -pnpm build -hyperfine --warmup 3 --runs 20 'node packages/cli/bin/run.js version' -``` - -### Step 2: Side-by-side comparison (preferred) - -If both branches are built in separate worktrees: - -```bash -hyperfine --warmup 3 --runs 20 \ - -n 'main (no lazy loading)' 'node /path/to/main/packages/cli/bin/run.js version' \ - -n 'faster-startup (Option A)' 'node /path/to/faster-startup/packages/cli/bin/run.js version' -``` - -### Step 3: Record in `docs/lazy-loading-options.md` - -Fill in the Option A performance table: - -| Metric | `main` (no lazy loading) | `faster-startup` (Option A) | Delta | -|--------|--------------------------|----------------------------|-------| -| **Mean** | _from Phase 0_ | _Xs ± Ys_ | _-X%_ | -| **Median** | _from Phase 0_ | _Xs_ | _-X%_ | -| **Range** | _from Phase 0_ | _Xs … Xs_ | | - ---- - -## Phase 2: Option B — Pattern Strategy with Thin Re-exports - -**Branch:** `lazy-loading-option-b` (based off `main`) - -### Step 1: Switch oclif strategy from `explicit` to `pattern` - -**File:** `packages/cli/package.json` - -Change: -```json -"commands": { - "strategy": "explicit", - "target": "./dist/index.js", - "identifier": "COMMANDS" -} -``` -To: -```json -"commands": { - "strategy": "pattern", - "target": "./dist/commands" -} -``` - -### Step 2: Create `packages/cli/src/commands/` directory with re-export files - -Create one file per command. Each file is a thin re-export that maps to the real command class. - -#### CLI-local commands (17 files) — move or re-export from `src/cli/commands/` - -These already live in the monorepo. Each file re-exports the default from its current location: - -``` -commands/version.ts → export {default} from '../cli/commands/version.js' -commands/search.ts → export {default} from '../cli/commands/search.js' -commands/upgrade.ts → export {default} from '../cli/commands/upgrade.js' -commands/help.ts → export {default} from '../cli/commands/help.js' -commands/auth/logout.ts → export {default} from '../../cli/commands/auth/logout.js' -commands/auth/login.ts → export {default} from '../../cli/commands/auth/login.js' -commands/debug/command-flags.ts -commands/kitchen-sink/index.ts -commands/kitchen-sink/async.ts -commands/kitchen-sink/prompts.ts -commands/kitchen-sink/static.ts -commands/doctor-release/doctor-release.ts ← NOTE: command ID is "doctor-release", file must match -commands/doctor-release/theme/index.ts -commands/docs/generate.ts -commands/notifications/list.ts -commands/notifications/generate.ts -commands/cache/clear.ts -``` - -**Important:** oclif's pattern strategy derives command IDs from file paths. `commands/app/dev.ts` → `app:dev`. The path must match the command ID exactly. - -**Edge case — `doctor-release` command:** The command ID is `doctor-release`, but the implementation file is `doctor-release.ts`. With pattern strategy, `commands/doctor-release.ts` would produce command ID `doctor-release`. If the implementation is at `commands/doctor-release/doctor-release.ts`, the ID would be `doctor-release:doctor-release`. Need to verify: does the current command have ID `doctor-release` or is it nested? Check the manifest. The manifest shows `"id": "doctor-release"` — so the file should be `commands/doctor-release.ts` (not in a subdirectory), OR we use `commands/doctor-release/index.ts` which oclif resolves to `doctor-release`. - -**Edge case — `webhook:trigger` (deprecated alias):** This command lives under app commands but its ID doesn't start with `app:`. It needs `commands/webhook/trigger.ts` re-exporting from `@shopify/app`. - -**Edge case — `demo:watcher` and `organization:list`:** Same situation — app commands with non-`app:` prefixes. Need `commands/demo/watcher.ts` and `commands/organization/list.ts`. - -#### App commands (34 files) — re-export from `@shopify/app` - -Each file imports the `commands` object from `@shopify/app` and re-exports the specific command: - -```ts -// commands/app/dev.ts -import {commands} from '@shopify/app' -export default commands['app:dev'] -``` - -Full list: -``` -commands/app/build.ts -commands/app/bulk/cancel.ts -commands/app/bulk/status.ts -commands/app/bulk/execute.ts -commands/app/config/link.ts -commands/app/config/use.ts -commands/app/config/pull.ts -commands/app/config/validate.ts -commands/app/deploy.ts -commands/app/dev.ts -commands/app/dev/clean.ts -commands/app/env/pull.ts -commands/app/env/show.ts -commands/app/execute.ts -commands/app/function/build.ts -commands/app/function/info.ts -commands/app/function/replay.ts -commands/app/function/run.ts -commands/app/function/schema.ts -commands/app/function/typegen.ts -commands/app/generate/extension.ts -commands/app/generate/schema.ts -commands/app/import-custom-data-definitions.ts -commands/app/import-extensions.ts -commands/app/info.ts -commands/app/init.ts -commands/app/logs.ts -commands/app/logs/sources.ts -commands/app/release.ts -commands/app/versions/list.ts -commands/app/webhook/trigger.ts -commands/webhook/trigger.ts ← deprecated alias, different path -commands/demo/watcher.ts ← app command with non-app prefix -commands/organization/list.ts ← app command with non-app prefix -``` - -#### Theme commands (20 files) — re-export from `@shopify/theme` - -```ts -// commands/theme/dev.ts -import ThemeCommands from '@shopify/theme' -export default ThemeCommands['theme:dev'] -``` - -Full list: -``` -commands/theme/check.ts -commands/theme/console.ts -commands/theme/delete.ts -commands/theme/dev.ts -commands/theme/duplicate.ts -commands/theme/info.ts -commands/theme/init.ts -commands/theme/language-server.ts -commands/theme/list.ts -commands/theme/metafields/pull.ts -commands/theme/open.ts -commands/theme/package.ts -commands/theme/preview.ts -commands/theme/profile.ts -commands/theme/publish.ts -commands/theme/pull.ts -commands/theme/push.ts -commands/theme/rename.ts -commands/theme/serve.ts -commands/theme/share.ts -``` - -#### Hydrogen commands (~25 files) — re-export from `@shopify/cli-hydrogen` - -```ts -// commands/hydrogen/dev.ts -import {COMMANDS} from '@shopify/cli-hydrogen' -export default COMMANDS['hydrogen:dev'] -``` - -Full list (verify against manifest — hydrogen commands are numerous): -``` -commands/hydrogen/build.ts -commands/hydrogen/check.ts -commands/hydrogen/codegen.ts -commands/hydrogen/customer-account-push.ts -commands/hydrogen/debug/cpu.ts -commands/hydrogen/deploy.ts -commands/hydrogen/dev.ts -commands/hydrogen/env/list.ts -commands/hydrogen/env/pull.ts -commands/hydrogen/env/push.ts -commands/hydrogen/g.ts -commands/hydrogen/generate/route.ts -commands/hydrogen/generate/routes.ts -commands/hydrogen/init.ts -commands/hydrogen/link.ts -commands/hydrogen/list.ts -commands/hydrogen/login.ts -commands/hydrogen/logout.ts -commands/hydrogen/preview.ts -commands/hydrogen/setup.ts -commands/hydrogen/setup/css.ts -commands/hydrogen/setup/markets.ts -commands/hydrogen/setup/vite.ts -commands/hydrogen/shortcut.ts -commands/hydrogen/unlink.ts -commands/hydrogen/upgrade.ts -``` - -#### Plugin commands — re-export from oclif plugins and did-you-mean - -``` -commands/commands.ts ← from @oclif/plugin-commands -commands/plugins/index.ts ← from @oclif/plugin-plugins -commands/plugins/inspect.ts -commands/plugins/install.ts -commands/plugins/link.ts -commands/plugins/reset.ts -commands/plugins/uninstall.ts -commands/plugins/update.ts -commands/config/autocorrect/off.ts ← from @shopify/plugin-did-you-mean -commands/config/autocorrect/on.ts -commands/config/autocorrect/status.ts -``` - -### Step 3: Handle `customPluginName` for analytics - -Currently `index.ts` sets `customPluginName` on every command class at import time (lines 92-126). This is used by `packages/cli-kit/src/private/node/analytics.ts` to attribute commands to their source package. - -**Two approaches:** - -**(a) Set it in each re-export file:** -```ts -// commands/app/dev.ts -import {commands} from '@shopify/app' -const cmd = commands['app:dev']! -;(cmd as any).customPluginName = '@shopify/app' -export default cmd -``` - -**(b) Set it in a prerun hook** — the `prerun` hook already runs before every command. Add `customPluginName` assignment there based on command ID prefix. This is cleaner and centralizes the logic. - -**Recommended: (b)** — add to existing `packages/cli/src/hooks/prerun.ts` or create a wrapper that assigns `customPluginName` based on command ID prefix mapping. - -### Step 4: Simplify `bootstrap.ts` - -Remove `lazyCommandLoader` — oclif handles lazy loading natively with `pattern` strategy: - -```ts -async function runShopifyCLI({development}: {development: boolean}) { - await runCLI({ - moduleURL: import.meta.url, - development, - // no lazyCommandLoader — oclif handles it via pattern strategy - }) -} -``` - -### Step 5: Simplify `cli-launcher.ts` and `ShopifyConfig` - -- Remove `setLazyCommandLoader()` method from `ShopifyConfig` -- Remove `runCommand()` override (the entire lazy loading override) -- Remove `LazyCommandLoader` type export -- Remove `lazyCommandLoader` parameter from `runCLI()` and `launchCLI()` -- Keep `ShopifyConfig` class — it still has `customPriority` logic for hydrogen monorepo dev mode - -### Step 6: Clean up dead code - -- Delete `packages/cli/src/command-registry.ts` -- `packages/cli/src/index.ts` — keep it but remove the `COMMANDS` export and heavy imports. It may still be needed for non-command exports (`push`, `pull`, `fetchStoreThemes`, hook re-exports). Check what else imports from `@shopify/cli` — if only oclif uses it for commands, the barrel can be gutted. If other packages import utilities from it, keep those exports. -- Check if `searchForDefault()` is used anywhere else — if not, remove it. - -### Step 7: Handle `index.ts` non-command exports - -`index.ts` currently also exports: -- `DidYouMeanHook`, `TunnelStartHook`, `TunnelProviderHook`, `PluginHook` — hook re-exports -- `AppSensitiveMetadataHook`, `AppInitHook`, `AppPublicMetadataHook` — app hook re-exports -- `HydrogenInitHook` — hydrogen hook re-export -- `push`, `pull`, `fetchStoreThemes` — theme utility re-exports - -**These are consumed by the hook files** (`packages/cli/src/hooks/*.ts`). But the hooks already import directly from their source packages (e.g., `hooks/app-init.ts` does `export {AppInitHook as default} from '@shopify/app'`). So the re-exports in `index.ts` are likely dead code. - -**Verify with knip** — run `pnpm knip` to check if these exports are used. If unused, remove them. If used by external consumers, keep a minimal `index.ts` with just those exports. - -### Step 8: Regenerate manifests and verify - -```bash -pnpm build -pnpm refresh-manifests -``` - -Verify the generated `oclif.manifest.json` contains all expected commands with correct IDs, flags, and descriptions. Diff against the current manifest to ensure no commands were dropped. - -### Step 9: Run CI checks locally - -```bash -pnpm nx run-many --all --target=build -pnpm nx run-many --all --target=type-check -pnpm nx run-many --all --target=lint -pnpm nx run-many --all --target=bundle -pnpm vitest run -pnpm refresh-manifests # verify no diff -pnpm knip -``` - -### Step 10: Measure performance - -**This step is mandatory — the whole point of this refactor is measurable startup improvement.** - -Measure startup time of `shopify version` (a lightweight command that exercises the full CLI bootstrap path without doing real work). - -**Reuse the `main` baseline from Phase 0 and Option A baseline from Phase 1** — no need to re-measure. - -#### 10a. Measure Option B branch - -```bash -git checkout lazy-loading-option-b -pnpm build -hyperfine --warmup 3 --runs 20 'node packages/cli/bin/run.js version' -``` - -#### 10b. Side-by-side comparison with baselines (preferred) - -If worktrees from Phase 0 and Phase 1 are still available: - -```bash -hyperfine --warmup 3 --runs 20 \ - -n 'main (no lazy loading)' 'node /path/to/main/packages/cli/bin/run.js version' \ - -n 'faster-startup (Option A)' 'node /path/to/option-a/packages/cli/bin/run.js version' \ - -n 'option-b' 'node packages/cli/bin/run.js version' -``` - -#### 10c. If `hyperfine` is not available - -```bash -# Install it -brew install hyperfine - -# Or fallback to manual timing (less precise) -for i in {1..10}; do - /usr/bin/time -p node packages/cli/bin/run.js version 2>&1 | grep real -done -``` - -#### 10d. What to record - -Fill in the table in `docs/lazy-loading-options.md` under Option B: - -| Metric | `main` (no lazy loading) | Option A (`faster-startup`) | Option B | Delta (B vs main) | -|--------|--------------------------|----------------------------|----------|-------------------| -| **Mean** | _from Phase 0_ | _from Phase 1_ | _Xs ± Ys_ | _-X%_ | -| **Median** | _from Phase 0_ | _from Phase 1_ | _Xs_ | _-X%_ | -| **Range** | _from Phase 0_ | _from Phase 1_ | _Xs … Xs_ | | - -### Step 11: Update `docs/lazy-loading-options.md` - -Fill in the Option B section with: -1. **Branch URL** — the GitHub compare or branch URL -2. **Performance table** — the metrics from Step 10, including Option A for comparison -3. Any notes on unexpected findings (e.g., if hook loading dominates startup, or if B is faster/slower than A) - ---- - -## Phase 3: Option C — Plugin Wrapper Architecture - -**Branch:** `lazy-loading-option-c` (based off `lazy-loading-option-b`) - -Option C extends Option B by extracting re-export files into separate plugin wrapper packages. The `commands/` directory in `packages/cli/` shrinks to only CLI-local commands. - -### Step 1: Create `packages/plugin-app/` wrapper package - -``` -packages/plugin-app/ -├── package.json -├── tsconfig.json -├── tsconfig.build.json -├── project.json -└── src/ - └── commands/ - └── app/ - ├── build.ts ← re-export from @shopify/app - ├── dev.ts - └── ... - └── webhook/ - └── trigger.ts ← the deprecated alias - └── demo/ - └── watcher.ts - └── organization/ - └── list.ts -``` - -**`package.json`:** -```json -{ - "name": "@shopify/plugin-app", - "version": "3.92.0", - "private": true, - "type": "module", - "main": "dist/index.js", - "dependencies": { - "@shopify/app": "3.92.0" - }, - "oclif": { - "commands": { - "strategy": "pattern", - "target": "./dist/commands" - }, - "hooks": { - "init": "./dist/hooks/init.js", - "sensitive_command_metadata": "./dist/hooks/sensitive-metadata.js", - "public_command_metadata": "./dist/hooks/public-metadata.js" - } - } -} -``` - -**Hooks** move from `packages/cli/src/hooks/` into the wrapper: -- `packages/plugin-app/src/hooks/init.ts` ← re-export `AppInitHook` from `@shopify/app` -- `packages/plugin-app/src/hooks/sensitive-metadata.ts` -- `packages/plugin-app/src/hooks/public-metadata.ts` - -### Step 2: Create `packages/plugin-theme/` wrapper package - -Same pattern. No hooks (theme has none). - -``` -packages/plugin-theme/ -├── package.json -├── src/ -│ └── commands/ -│ └── theme/ -│ ├── dev.ts -│ ├── push.ts -│ └── ... -``` - -### Step 3: Create `packages/plugin-hydrogen/` wrapper package - -Wraps the external `@shopify/cli-hydrogen` package. - -``` -packages/plugin-hydrogen/ -├── package.json -├── src/ -│ └── commands/ -│ └── hydrogen/ -│ ├── dev.ts -│ └── ... -│ └── hooks/ -│ └── init.ts ← re-export from @shopify/cli-hydrogen HOOKS.init -``` - -### Step 4: Register plugins in root CLI - -**`packages/cli/package.json`:** -```json -"oclif": { - "commands": { - "strategy": "pattern", - "target": "./dist/commands" - }, - "plugins": [ - "@shopify/plugin-app", - "@shopify/plugin-theme", - "@shopify/plugin-hydrogen", - "@shopify/plugin-did-you-mean", - "@oclif/plugin-commands", - "@oclif/plugin-plugins" - ] -} -``` - -Remove app/theme/hydrogen hooks from root CLI's hooks config — they now live in their respective plugins. - -Root CLI hooks config shrinks to: -```json -"hooks": { - "prerun": "./dist/hooks/prerun.js", - "postrun": "./dist/hooks/postrun.js", - "tunnel_start": "./dist/hooks/tunnel-start.js", - "tunnel_provider": "./dist/hooks/tunnel-provider.js", - "update": "./dist/hooks/plugin-plugins.js" -} -``` - -### Step 5: Shrink `packages/cli/src/commands/` - -Only CLI-local commands remain: -``` -commands/version.ts -commands/search.ts -commands/upgrade.ts -commands/help.ts -commands/auth/login.ts -commands/auth/logout.ts -commands/cache/clear.ts -commands/debug/command-flags.ts -commands/kitchen-sink/... -commands/doctor-release/... -commands/docs/generate.ts -commands/notifications/... -``` - -All app/theme/hydrogen/plugin re-exports are deleted — they now live in wrapper packages. - -### Step 6: Build pipeline for wrapper packages - -Each wrapper package needs: -- A `project.json` with build, type-check, and lint targets -- Entry in `pnpm-workspace.yaml` (already covered by `packages/*` glob) -- TypeScript config extending the root config -- Manifest generation (add to `refresh-manifests` script) - -### Step 7: Handle `customPluginName` - -With proper oclif plugins, `customPluginName` becomes unnecessary — oclif natively tracks which plugin owns each command via `Command.plugin`. The analytics code in `packages/cli-kit/src/private/node/analytics.ts` should be updated to prefer `commandClass.plugin?.name` and fall back to `customPluginName` only if needed. - -### Step 8: Run CI checks - -```bash -pnpm nx run-many --all --target=build -pnpm nx run-many --all --target=type-check -pnpm nx run-many --all --target=lint -pnpm nx run-many --all --target=bundle -pnpm vitest run -pnpm refresh-manifests # verify no diff -pnpm knip -``` - -### Step 9: Measure performance - -Same methodology as Phase 2 Step 10. Compare against **both** `main` and Option B: - -```bash -hyperfine --warmup 3 --runs 20 \ - -n 'main' 'node /path/to/main/packages/cli/bin/run.js version' \ - -n 'option-b' 'node /path/to/option-b/packages/cli/bin/run.js version' \ - -n 'option-c' 'node packages/cli/bin/run.js version' -``` - -Fill in the table in `docs/lazy-loading-options.md` under Option C: - -| Metric | `main` (baseline) | Option B | Option C | Delta (C vs main) | -|--------|-------------------|----------|----------|-------------------| -| **Mean** | _Xs ± Ys_ | _Xs ± Ys_ | _Xs ± Ys_ | _-X%_ | -| **Median** | _Xs_ | _Xs_ | _Xs_ | _-X%_ | -| **Range** | _Xs … Xs_ | _Xs … Xs_ | _Xs … Xs_ | | - -### Step 10: Update `docs/lazy-loading-options.md` - -Fill in the Option C section with: -1. **Branch URL** — the GitHub compare or branch URL -2. **Performance table** — the metrics from Step 9, including comparison to both `main` and Option B -3. Any notes on whether plugin isolation provided measurable improvement over Option B - ---- - -## Risk Checklist - -| Risk | Mitigation | -|------|-----------| -| Command ID mismatch (file path doesn't match expected ID) | Diff `oclif.manifest.json` before/after — every command must appear with correct ID | -| `customPluginName` breaks analytics | Add to re-export files or prerun hook; verify analytics tests pass | -| `knip` flags new re-export files as unused | May need knip config update for `commands/` directory | -| `plugins:install` description override lost | Move the `description = ''` and `hidden = true` assignments to re-export files | -| Hook loading order changes | Verify init hooks fire in same order (app-init before hydrogen-init) | -| `@shopify/cli-hydrogen` command list changes between versions | Generate hydrogen re-exports from the installed package's exports, not hardcoded | -| Manifest generation with pattern strategy produces different output | Compare field-by-field; `pluginAlias` and `pluginType` may differ | -| Option C: wrapper packages not discovered by oclif in dev mode | Test with `bin/dev.js` (development=true) — ShopifyConfig's `customPriority` may need adjustment | - ---- - -## Performance Expectations - -Both Option B and Option C achieve **per-package** lazy loading (not per-command). Running `shopify version` should NOT import `@shopify/app`, `@shopify/theme`, or `@shopify/cli-hydrogen`. - -The performance delta between B and C should be minimal — the difference is whether oclif loads one plugin (with many commands) or several plugins (with fewer commands each). The expensive part is the package import, which both defer equally. - -The main performance gain vs `main` (no lazy loading) is avoiding the import of ~6 heavy packages on every CLI invocation.