Lazy command loading: Option C — plugin wrapper architecture#7302
Draft
byrichardpowell wants to merge 1 commit intolazy-loading-option-bfrom
Draft
Lazy command loading: Option C — plugin wrapper architecture#7302byrichardpowell wants to merge 1 commit intolazy-loading-option-bfrom
byrichardpowell wants to merge 1 commit intolazy-loading-option-bfrom
Conversation
Extract app, theme, and hydrogen command re-exports from packages/cli into dedicated wrapper plugin packages (plugin-app, plugin-theme, plugin-hydrogen). Key changes: - Create packages/plugin-app/ with 34 command re-exports + app hooks - Create packages/plugin-theme/ with 20 command re-exports - Create packages/plugin-hydrogen/ with 26 command re-exports + init hook - Register all plugins in packages/cli oclif.plugins config - Move plugins to dependencies (required by oclif plugin loader) - Move init/metadata hooks from CLI to wrapper plugins - Remove app/theme/hydrogen/plugin commands from packages/cli/src/commands/ - Configure knip for new wrapper packages - Generate oclif manifests for all wrapper packages Performance: 3.765s mean (vs 1.778s main = +111.8% SLOWER) The plugin resolution overhead (loading 6 plugins, resolving packages, reading manifests) adds ~2s to startup. This approach provides the cleanest architecture but is the slowest option due to oclif's per-plugin loading cost. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
9 tasks
Contributor
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationspackages/cli-kit/dist/public/node/custom-oclif-loader.d.tsimport { 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 declare class ShopifyConfig extends Config {
constructor(options: Options);
/**
* 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.
*/
runHook(event: string, opts: any, timeout?: number, captureErrors?: boolean): Promise<any>;
/**
* 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;
}
Existing type declarationspackages/cli-kit/dist/public/common/version.d.ts@@ -1 +1 @@
-export declare const CLI_KIT_VERSION = "3.93.0";
\ No newline at end of file
+export declare const CLI_KIT_VERSION = "3.92.0";
\ No newline at end of file
packages/cli-kit/dist/private/node/conf-store.d.ts@@ -24,7 +24,6 @@ export interface ConfSchema {
devSessionStore?: string;
currentDevSessionId?: string;
cache?: Cache;
- autoUpgradeEnabled?: boolean;
}
/**
* Get session.
@@ -126,18 +125,6 @@ interface RunWithRateLimitOptions {
* @returns true, or undefined if the task was not run.
*/
export declare function runWithRateLimit(options: RunWithRateLimitOptions, config?: LocalStorage<ConfSchema>): Promise<boolean>;
-/**
- * Get auto-upgrade preference.
- *
- * @returns Whether auto-upgrade is enabled, or undefined if never set.
- */
-export declare function getAutoUpgradeEnabled(config?: LocalStorage<ConfSchema>): boolean | undefined;
-/**
- * Set auto-upgrade preference.
- *
- * @param enabled - Whether auto-upgrade should be enabled.
- */
-export declare function setAutoUpgradeEnabled(enabled: boolean, config?: LocalStorage<ConfSchema>): void;
export declare function getConfigStoreForPartnerStatus(): LocalStorage<Record<string, {
status: true;
checkedAt: string;
packages/cli-kit/dist/private/node/constants.d.ts@@ -33,8 +33,6 @@ export declare const environmentVariables: {
neverUsePartnersApi: string;
skipNetworkLevelRetry: string;
maxRequestTimeForNetworkCalls: string;
- disableImportScanning: string;
- hostedApps: string;
};
export declare const defaultThemeKitAccessDomain = "theme-kit-access.shopifyapps.com";
export declare const systemEnvironmentVariables: {
packages/cli-kit/dist/private/node/ui.d.ts import { Logger, LogLevel } from '../../public/node/output.js';
-import React from 'react';
import { Key, RenderOptions } from 'ink';
import { EventEmitter } from 'events';
-/**
- * Signal that the current Ink tree is done. Must be called within an
- * InkLifecycleRoot — throws if the provider is missing so lifecycle
- * bugs surface immediately instead of silently hanging.
- */
-export declare function useComplete(): (error?: Error) => void;
-/**
- * Root wrapper for Ink trees. Owns the single `exit()` call site — children
- * signal completion via `useComplete()`, which sets state here. The `useEffect`
- * fires post-render, guaranteeing all batched state updates have been flushed
- * before the tree is torn down.
- */
-export declare function InkLifecycleRoot({ children }: {
- children: React.ReactNode;
-}): React.JSX.Element;
interface RenderOnceOptions {
logLevel?: LogLevel;
logger?: Logger;
renderOptions?: RenderOptions;
}
export declare function renderOnce(element: JSX.Element, { logLevel, renderOptions }: RenderOnceOptions): string | undefined;
-export declare function render(element: JSX.Element, options?: RenderOptions): Promise<void>;
+export declare function render(element: JSX.Element, options?: RenderOptions): Promise<unknown>;
export declare class Stdout extends EventEmitter {
columns: number;
rows: number;
readonly frames: string[];
private _lastFrame?;
constructor(options: {
columns?: number;
rows?: number;
});
write: (frame: string) => void;
lastFrame: () => string | undefined;
}
export declare function handleCtrlC(input: string, key: Key, exit?: () => void): void;
export {};
packages/cli-kit/dist/public/node/fs.d.ts@@ -1,6 +1,6 @@
import { OverloadParameters } from '../../private/common/ts/overloaded-parameters.js';
import { RandomNameFamily } from '../common/string.js';
-import { findUp as internalFindUp, findUpSync as internalFindUpSync } from 'find-up';
+import { findUp as internalFindUp } from 'find-up';
import { ReadStream, WriteStream } from 'fs';
import type { Pattern, Options as GlobOptions } from 'fast-glob';
/**
@@ -335,14 +335,6 @@ export declare function defaultEOL(): EOL;
* @returns The first path found that matches or if none could be found.
*/
export declare function findPathUp(matcher: OverloadParameters<typeof internalFindUp>[0], options: OverloadParameters<typeof internalFindUp>[1]): ReturnType<typeof internalFindUp>;
-/**
- * Find a file by walking parent directories.
- *
- * @param matcher - A pattern or an array of patterns to match a file name.
- * @param options - Options for the search.
- * @returns The first path found that matches or if none could be found.
- */
-export declare function findPathUpSync(matcher: OverloadParameters<typeof internalFindUp>[0], options: OverloadParameters<typeof internalFindUp>[1]): ReturnType<typeof internalFindUpSync>;
export interface MatchGlobOptions {
matchBase: boolean;
noglobstar: boolean;
packages/cli-kit/dist/public/node/import-extractor.d.ts@@ -1,12 +1,6 @@
-/**
- * Clears all import-scanning caches (direct imports, recursive results, and filesystem stats).
- * Should be called when watched files change so that rescanning picks up updated imports.
- */
-export declare function clearImportPathsCache(): void;
/**
* Extracts import paths from a source file.
* Supports JavaScript, TypeScript, and Rust files.
- * Results are cached per file path to avoid redundant I/O.
*
* @param filePath - Path to the file to analyze.
* @returns Array of absolute paths to imported files.
@@ -23,17 +17,6 @@ export declare function extractImportPaths(filePath: string): string[];
* @throws If an unexpected error occurs while processing files (not including ENOENT file not found errors).
*/
export declare function extractImportPathsRecursively(filePath: string, visited?: Set<string>): string[];
-/**
- * Returns diagnostic information about the import scanning caches.
- * Useful for debugging performance issues with --verbose.
- *
- * @returns Cache size stats for directImports, fileExists, and isDir.
- */
-export declare function getImportScanningCacheStats(): {
- directImports: number;
- fileExists: number;
- isDir: number;
-};
/**
* Extracts import paths from a JavaScript content.
*
packages/cli-kit/dist/public/node/is-global.d.ts@@ -26,14 +26,6 @@ export declare function installGlobalCLIPrompt(): Promise<InstallGlobalCLIPrompt
* Infers the package manager used by the global CLI.
*
* @param argv - The arguments passed to the process.
- * @param env - The environment variables of the process.
* @returns The package manager used by the global CLI.
*/
-export declare function inferPackageManagerForGlobalCLI(argv?: string[], env?: NodeJS.ProcessEnv): PackageManager;
-/**
- * Returns the project directory for the given path.
- *
- * @param directory - The path to search upward from.
- * @returns The project root directory, or undefined if not found.
- */
-export declare function getProjectDir(directory: string): string | undefined;
\ No newline at end of file
+export declare function inferPackageManagerForGlobalCLI(argv?: string[]): PackageManager;
\ No newline at end of file
packages/cli-kit/dist/public/node/monorail.d.ts@@ -66,10 +66,6 @@ export interface Schemas {
cmd_app_linked_config_uses_cli_managed_urls?: Optional<boolean>;
cmd_app_warning_api_key_deprecation_displayed?: Optional<boolean>;
cmd_app_deployment_mode?: Optional<string>;
- cmd_app_validate_json?: Optional<boolean>;
- cmd_app_validate_valid?: Optional<boolean>;
- cmd_app_validate_issue_count?: Optional<number>;
- cmd_app_validate_file_count?: Optional<number>;
cmd_dev_tunnel_type?: Optional<string>;
cmd_dev_tunnel_custom_hash?: Optional<string>;
cmd_dev_urls_updated?: Optional<boolean>;
packages/cli-kit/dist/public/node/node-package-manager.d.ts@@ -25,7 +25,7 @@ export type DependencyType = 'dev' | 'prod' | 'peer';
/**
* A union that represents the package managers available.
*/
-export declare const packageManager: readonly ["yarn", "npm", "pnpm", "bun", "homebrew", "unknown"];
+export declare const packageManager: readonly ["yarn", "npm", "pnpm", "bun", "unknown"];
export type PackageManager = (typeof packageManager)[number];
/**
* Returns an abort error that's thrown when the package manager can't be determined.
@@ -60,22 +60,10 @@ export declare class FindUpAndReadPackageJsonNotFoundError extends BugError {
export declare function packageManagerFromUserAgent(env?: NodeJS.ProcessEnv): PackageManager;
/**
* Returns the dependency manager used in a directory.
- * Walks upward from so workspace packages (e.g. )
- * still resolve to the repo root lockfile ().
- * If no lockfile is found, it falls back to the package manager from the user agent.
- * If the package manager from the user agent is unknown, it returns 'npm'.
* @param fromDirectory - The starting directory
* @returns The dependency manager
*/
export declare function getPackageManager(fromDirectory: string): Promise<PackageManager>;
-/**
- * Builds the command and argv needed to execute a local binary using the package manager
- * detected from the provided directory or its ancestors.
- */
-export declare function packageManagerBinaryCommandForDirectory(fromDirectory: string, binary: string, ...binaryArgs: string[]): Promise<{
- command: string;
- args: string[];
-}>;
interface InstallNPMDependenciesRecursivelyOptions {
/**
* The dependency manager to use to install the dependencies.
packages/cli-kit/dist/public/node/path.d.ts import type { URL } from 'url';
/**
* Joins a list of paths together.
*
* @param paths - Paths to join.
* @returns Joined path.
*/
export declare function joinPath(...paths: string[]): string;
/**
* Normalizes a path.
*
* @param path - Path to normalize.
* @returns Normalized path.
*/
export declare function normalizePath(path: string): string;
/**
* Resolves a list of paths together.
*
* @param paths - Paths to resolve.
* @returns Resolved path.
*/
export declare function resolvePath(...paths: string[]): string;
/**
* Returns the relative path from one path to another.
*
* @param from - Path to resolve from.
* @param to - Path to resolve to.
* @returns Relative path.
*/
export declare function relativePath(from: string, to: string): string;
/**
* Returns whether the path is absolute.
*
* @param path - Path to check.
* @returns Whether the path is absolute.
*/
export declare function isAbsolutePath(path: string): boolean;
/**
* Returns the directory name of a path.
*
* @param path - Path to get the directory name of.
* @returns Directory name.
*/
export declare function dirname(path: string): string;
/**
* Returns the base name of a path.
*
* @param path - Path to get the base name of.
* @param ext - Optional extension to remove from the result.
* @returns Base name.
*/
export declare function basename(path: string, ext?: string): string;
/**
* Returns the extension of the path.
*
* @param path - Path to get the extension of.
* @returns Extension.
*/
export declare function extname(path: string): string;
/**
* Parses a path into its components (root, dir, base, ext, name).
*
* @param path - Path to parse.
* @returns Parsed path object.
*/
export declare function parsePath(path: string): {
root: string;
dir: string;
base: string;
ext: string;
name: string;
};
/**
- * Returns the longest common parent directory of two absolute paths.
- *
- * @param first - First absolute path.
- * @param second - Second absolute path.
- * @returns The common parent directory, or '/' if they share only the root.
- */
-export declare function commonParentDirectory(first: string, second: string): string;
-/**
* Given an absolute filesystem path, it makes it relative to
* the current working directory. This is useful when logging paths
* to allow the users to click on the file and let the OS open it
* in the editor of choice.
*
* @param path - Path to relativize.
* @param dir - Current working directory.
* @returns Relativized path.
*/
export declare function relativizePath(path: string, dir?: string): string;
/**
* Given 2 paths, it returns whether the second path is a subpath of the first path.
*
* @param mainPath - The main path.
* @param subpath - The subpath.
* @returns Whether the subpath is a subpath of the main path.
*/
export declare function isSubpath(mainPath: string, subpath: string): boolean;
/**
* Given a module's import.meta.url it returns the directory containing the module.
*
* @param moduleURL - The value of import.meta.url in the context of the caller module.
* @returns The path to the directory containing the caller module.
*/
export declare function moduleDirectory(moduleURL: string | URL): string;
/**
* When running a script using `npm run`, something interesting happens. If the current
* folder does not have a `package.json` or a `node_modules` folder, npm will traverse
* the directory tree upwards until it finds one. Then it will run the script and set
* `process.cwd()` to that folder, while the actual path is stored in the INIT_CWD
* environment variable (see here: https://docs.npmjs.com/cli/v9/commands/npm-run-script#description).
*
* @returns The path to the current working directory.
*/
export declare function cwd(): string;
/**
* Tries to get the value of the `--path` argument, if provided.
*
* @param argv - The arguments to search for the `--path` argument.
* @returns The value of the `--path` argument, if provided.
*/
export declare function sniffForPath(argv?: string[]): string | undefined;
/**
* Returns whether the `--json` or `-j` flags are present in the arguments.
*
* @param argv - The arguments to search for the `--json` and `-j` flags.
* @returns Whether the `--json` or `-j` flag is present in the arguments.
*/
export declare function sniffForJson(argv?: string[]): boolean;
/**
* Removes any `..` traversal segments from a relative path and calls `warn`
* if any were stripped. Normal `..` that cancel out within the path (e.g.
* `foo/../bar` → `bar`) are collapsed but never allowed to escape the root.
* Both `/` and `\` are treated as separators for cross-platform safety.
*
* @param input - The relative path to sanitize.
* @param warn - Called with a human-readable warning when traversal segments are removed.
* @returns The sanitized path (may be an empty string if all segments were traversal).
*/
export declare function sanitizeRelativePath(input: string, warn: (msg: string) => void): string;
packages/cli-kit/dist/public/node/ui.d.ts@@ -34,7 +34,7 @@ export interface RenderConcurrentOptions extends PartialBy<ConcurrentOutputProps
* 00:00:00 │ frontend │ third frontend message
*
*/
-export declare function renderConcurrent({ renderOptions, ...props }: RenderConcurrentOptions): Promise<void>;
+export declare function renderConcurrent({ renderOptions, ...props }: RenderConcurrentOptions): Promise<unknown>;
export type AlertCustomSection = CustomSection;
export type RenderAlertOptions = Omit<AlertOptions, 'type'>;
/**
packages/cli-kit/dist/public/node/upgrade.d.ts@@ -4,41 +4,11 @@
*
* @returns A string with the command to run.
*/
-export declare function cliInstallCommand(): string | undefined;
-/**
- * Runs the CLI upgrade using the appropriate package manager.
- * Determines the install command and executes it.
- *
- * @throws AbortError if the package manager or command cannot be determined.
- */
-export declare function runCLIUpgrade(): Promise<void>;
-/**
- * Returns the version to auto-upgrade to, or undefined if auto-upgrade should be skipped.
- * Auto-upgrade is disabled by default and must be enabled via .
- * Also skips for CI, pre-release versions, or when no newer version is available.
- *
- * @returns The version string to upgrade to, or undefined if no upgrade should happen.
- */
-export declare function versionToAutoUpgrade(): string | undefined;
-/**
- * Shows a daily upgrade-available warning for users who have not enabled auto-upgrade.
- * Skipped in CI and for pre-release versions. When auto-upgrade is enabled this is a no-op
- * because the postrun hook will handle the upgrade directly.
- */
-export declare function warnIfUpgradeAvailable(): Promise<void>;
+export declare function cliInstallCommand(): string;
/**
* Generates a message to remind the user to update the CLI.
- * For major version bumps, appends a link to the GitHub release notes so users
- * can review breaking changes before deciding to upgrade.
*
* @param version - The version to update to.
- * @param isMajor - Whether the version bump is a major version change.
* @returns The message to remind the user to update the CLI.
*/
-export declare function getOutputUpdateCLIReminder(version: string, isMajor?: boolean): string;
-/**
- * Prompts the user to enable or disable automatic upgrades, then persists their choice.
- *
- * @returns Whether the user chose to enable auto-upgrade.
- */
-export declare function promptAutoUpgrade(): Promise<boolean>;
\ No newline at end of file
+export declare function getOutputUpdateCLIReminder(version: string): string;
\ No newline at end of file
packages/cli-kit/dist/public/node/version.d.ts@@ -18,13 +18,4 @@ export declare function globalCLIVersion(): Promise<string | undefined>;
* @param version - The version to check.
* @returns True if the version is a pre-release version.
*/
-export declare function isPreReleaseVersion(version: string): boolean;
-/**
- * Checks if there is a major version change between two versions.
- * Pre-release versions (0.0.0-*) are treated as not having a major version change.
- *
- * @param currentVersion - The current version.
- * @param newerVersion - The newer version to compare against.
- * @returns True if there is a major version change.
- */
-export declare function isMajorVersionChange(currentVersion: string, newerVersion: string): boolean;
\ No newline at end of file
+export declare function isPreReleaseVersion(version: string): boolean;
\ No newline at end of file
packages/cli-kit/dist/private/node/api/graphql.d.ts@@ -1,14 +1,4 @@
import { Variables } from 'graphql-request';
export declare function debugLogRequestInfo(api: string, query: string, url: string, variables?: Variables, headers?: Record<string, string>): void;
export declare function sanitizeVariables(variables: Variables): string;
-/**
- * Extracts human-readable error messages from a GraphQL errors array.
- *
- * Some APIs (e.g. App Management) return structured errors nested inside
- * . When those are present, we extract
- * them so the CLI displays a clean message instead of a raw JSON dump.
- * Falls back to each error's top-level field, and ultimately to
- * the full JSON representation if no messages can be extracted.
- */
-export declare function extractGraphQLErrorMessages(errors: any[] | undefined): string | undefined;
export declare function errorHandler(api: string): (error: unknown, requestId?: string) => unknown;
\ No newline at end of file
packages/cli-kit/dist/public/node/context/local.d.ts@@ -25,13 +25,6 @@ export declare function isDevelopment(env?: NodeJS.ProcessEnv): boolean;
* @returns True if SHOPIFY_FLAG_VERBOSE is truthy or the flag --verbose has been passed.
*/
export declare function isVerbose(env?: NodeJS.ProcessEnv): boolean;
-/**
- * Returns true if the hosted apps mode is enabled.
- *
- * @param env - The environment variables from the environment of the current process.
- * @returns True if HOSTED_APPS is truthy.
- */
-export declare function isHostedAppsMode(env?: NodeJS.ProcessEnv): boolean;
/**
* Returns true if the environment in which the CLI is running is either
* a local environment (where dev is present).
packages/cli-kit/dist/public/node/hooks/postrun.d.ts@@ -5,11 +5,4 @@ import { Hook } from '@oclif/core';
* @returns Whether post run hook has completed.
*/
export declare function postRunHookHasCompleted(): boolean;
-export declare const hook: Hook.Postrun;
-/**
- * Auto-upgrades the CLI after a command completes, if a newer version is available.
- * The entire flow is rate-limited to once per day unless forced via SHOPIFY_CLI_FORCE_AUTO_UPGRADE.
- *
- * @returns Resolves when the upgrade attempt (or fallback warning) is complete.
- */
-export declare function autoUpgradeIfNeeded(): Promise<void>;
\ No newline at end of file
+export declare const hook: Hook.Postrun;
\ No newline at end of file
packages/cli-kit/dist/public/node/hooks/prerun.d.ts@@ -11,7 +11,6 @@ export declare function parseCommandContent(cmdInfo: {
pluginAlias?: string;
}): CommandContent;
/**
- * Triggers a background check for a newer CLI version (non-blocking).
- * The result is cached and consumed by the postrun hook for auto-upgrade.
+ * Warns the user if there is a new version of the CLI available
*/
-export declare function checkForNewVersionInBackground(): void;
\ No newline at end of file
+export declare function warnOnAvailableUpgrade(): Promise<void>;
\ No newline at end of file
packages/cli-kit/dist/public/node/toml/toml-file.d.ts import { JsonMapType } from './codec.js';
/**
- * An error on a TOML file — missing or malformed.
- * Extends Error so it can be thrown. Carries path and a clean message suitable for JSON output.
+ * Thrown when a TOML file does not exist at the expected path.
*/
-export declare class TomlFileError extends Error {
+export declare class TomlFileNotFoundError extends Error {
readonly path: string;
- constructor(path: string, message: string);
+ constructor(path: string);
}
/**
+ * Thrown when a TOML file cannot be parsed. Includes the file path for context.
+ */
+export declare class TomlParseError extends Error {
+ readonly path: string;
+ constructor(path: string, cause: Error);
+}
+/**
* General-purpose TOML file abstraction.
*
* Provides a unified interface for reading, patching, removing keys from, and replacing
* the content of TOML files on disk.
*
* - `read` populates content from disk
* - `patch` does surgical WASM-based edits (preserves comments and formatting)
* - `remove` deletes a key by dotted path (preserves comments and formatting)
* - `replace` does a full re-serialization (comments and formatting are NOT preserved).
* - `transformRaw` applies a function to the raw TOML string on disk.
*/
export declare class TomlFile {
/**
- * Read and parse a TOML file from disk. Throws {@link TomlFileError} if the file
- * doesn't exist or contains invalid TOML.
+ * Read and parse a TOML file from disk. Throws if the file doesn't exist or contains invalid TOML.
+ * Parse errors are wrapped in {@link TomlParseError} with the file path for context.
*
* @param path - Absolute path to the TOML file.
* @returns A TomlFile instance with parsed content.
*/
static read(path: string): Promise<TomlFile>;
readonly path: string;
content: JsonMapType;
- readonly errors: TomlFileError[];
constructor(path: string, content: JsonMapType);
/**
* Surgically patch values in the TOML file, preserving comments and formatting.
*
* Accepts a nested object whose leaf values are set in the TOML. Intermediate tables are
* created automatically. Setting a leaf to `undefined` removes it (use `remove()` for a
* clearer API when deleting keys).
*
* @example
* ```ts
* await file.patch({build: {dev_store_url: 'my-store.myshopify.com'}})
* await file.patch({application_url: 'https://example.com', auth: {redirect_urls: ['...']}})
* ```
*/
patch(changes: {
[key: string]: unknown;
}): Promise<void>;
/**
* Remove a key from the TOML file by dotted path, preserving comments and formatting.
*
* @param keyPath - Dotted key path to remove (e.g. 'build.include_config_on_deploy').
* @example
* ```ts
* await file.remove('build.include_config_on_deploy')
* ```
*/
remove(keyPath: string): Promise<void>;
/**
* Replace the entire file content. The file is fully re-serialized — comments and formatting
* are NOT preserved.
*
* @param content - The new content to write.
* @example
* ```ts
* await file.replace({client_id: 'abc', name: 'My App'})
* ```
*/
replace(content: JsonMapType): Promise<void>;
/**
* Transform the raw TOML string on disk. Reads the file, applies the transform function
* to the raw text, writes back, and re-parses to keep `content` in sync.
*
* Use this for text-level operations that can't be expressed as structured edits —
* e.g. Injecting comments or positional insertion of keys in arrays-of-tables.
* Subsequent `patch()` calls will preserve any comments added this way.
*
* @param transform - A function that receives the raw TOML string and returns the modified string.
* @example
* ```ts
* await file.transformRaw((raw) => `# Header comment\n${raw}`)
* ```
*/
transformRaw(transform: (raw: string) => string): Promise<void>;
private decode;
}
packages/cli-kit/dist/private/node/ui/components/DangerousConfirmationPrompt.d.ts@@ -1,4 +1,3 @@
-import { InlineToken, TokenItem } from './TokenizedText.js';
import { InfoTableProps } from './Prompts/InfoTable.js';
import { AbortSignal } from '../../../../public/node/abort.js';
import { FunctionComponent } from 'react';
@@ -6,7 +5,6 @@ export interface DangerousConfirmationPromptProps {
message: string;
confirmation: string;
infoTable?: InfoTableProps['table'];
- warningItem?: TokenItem<InlineToken>;
onSubmit: (value: boolean) => void;
abortSignal?: AbortSignal;
}
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Extends Option B by extracting app, theme, and hydrogen command re-exports into dedicated wrapper plugin packages (
plugin-app,plugin-theme,plugin-hydrogen). Each wrapper is a proper oclif plugin with its own manifest and hooks. The root CLI shrinks to only 17 CLI-local commands.This is the textbook oclif plugin architecture — each plugin declares its own commands and hooks, and the root CLI is a thin orchestrator. Achieves ~69% faster startup vs main, though ~35% slower than Option B due to oclif plugin resolution overhead.
Approach
packages/plugin-app/(34 commands, 3 hooks),packages/plugin-theme/(20 commands),packages/plugin-hydrogen/(26 commands, 1 hook)package.jsonregisters them viaoclif.pluginsarray alongside@shopify/plugin-did-you-mean,@oclif/plugin-commands,@oclif/plugin-pluginsShopifyConfig(non-blocking hooks) andbootstrap.tsfrom Option Bdependencies(notdevDependencies) for oclif to discover themPerformance
mainmain(baseline)The ~140ms overhead vs Option B comes from oclif resolving 6 plugin packages at startup (reading each
package.jsonandoclif.manifest.json).Design doc
See Lazy Command Loading: Architectural Options for the full comparison of approaches and evaluation against design principles.
Test plan
pnpm buildpassespnpm nx run cli:type-checkpassespnpm nx run cli:lintpassespnpm vitest runinpackages/clipasses (all 20 tests)pnpm nx run cli:bundlepassespnpm knippassesnode packages/cli/bin/run.js versionoutputs version correctlynode packages/cli/bin/run.js helpshows all topics (app, theme, hydrogen, auth, config)hyperfine --warmup 5 --runs 20 'node packages/cli/bin/run.js version'shows ~0.55s🤖 Generated with Claude Code