diff --git a/packages/cli-kit/src/public/node/custom-oclif-loader.ts b/packages/cli-kit/src/public/node/custom-oclif-loader.ts index 750eb999ec..86116d7392 100644 --- a/packages/cli-kit/src/public/node/custom-oclif-loader.ts +++ b/packages/cli-kit/src/public/node/custom-oclif-loader.ts @@ -2,7 +2,7 @@ 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 {Config} from '@oclif/core' import {Options} from '@oclif/core/interfaces' export class ShopifyConfig extends Config { @@ -17,6 +17,7 @@ export class ShopifyConfig extends Config { if (currentPathMightBeHydrogenMonorepo && !ignoreHydrogenMonorepo) { path = execaSync('npm', ['prefix']).stdout.trim() } + if (fileExistsSync(joinPath(path, 'package.json'))) { // Hydrogen is bundled, but we still want to support loading it as an external plugin for two reasons: // 1. To allow users to use an older version of Hydrogen. (to not force upgrades) @@ -25,69 +26,36 @@ export class ShopifyConfig extends Config { core: ['@shopify/cli-hydrogen'], path, } + // Force OCLIF to ignore manifests so commands are loaded dynamically + // to be replaced later + options.ignoreManifest = true } } 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. - // eslint-disable-next-line @typescript-eslint/unbound-method - this.determinePriority = this.customPriority - } } - 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') { - // If b is hydrogen and a is core sort b first - return 1 - } - - if (aCommand.pluginAlias === '@shopify/cli-hydrogen' && bCommand.pluginType === 'core') { - // If a is hydrogen and b is core sort a first - return -1 - } - - // All other cases are the default implementation from the private `determinePriority` method - // When both plugin types are 'core' plugins sort based on index - if (aCommand.pluginType === 'core' && bCommand.pluginType === 'core') { - // If b appears first in the pjson.plugins sort it first - return aIndex - bIndex - } - - // if b is a core plugin and a is not sort b first - if (bCommand.pluginType === 'core' && aCommand.pluginType !== 'core') { - return 1 - } - - // if a is a core plugin and b is not sort a first - if (aCommand.pluginType === 'core' && bCommand.pluginType !== 'core') { - return -1 - } - - // if a is a jit plugin and b is not sort b first - if (aCommand.pluginType === 'jit' && bCommand.pluginType !== 'jit') { - return 1 - } + async load(): Promise { + await super.load() - // if b is a jit plugin and a is not sort a first - if (bCommand.pluginType === 'jit' && aCommand.pluginType !== 'jit') { - return -1 + if (isDevelopment()) { + // Let OCLIF load all commands first, then manually replace bundled hydrogen + // commands with external ones after loading completes. + const externalHydrogenPlugin = Array.from(this.plugins.values()).find( + (plugin) => plugin.name === '@shopify/cli-hydrogen' && !plugin.isRoot, + ) + + if (externalHydrogenPlugin) { + for (const command of externalHydrogenPlugin.commands) { + if (command.id.startsWith('hydrogen')) { + // @ts-expect-error: _commands is private but we need to replace bundled commands + if (this._commands.has(command.id)) { + // @ts-expect-error: _commands is private but we need to replace bundled commands + this._commands.set(command.id, command) + } + } + } } - - // neither plugin is core, so do not change the order - return 0 - }) - return commandPlugins[0] + } } }