@shopify/cli-kit 3.92.0 → 3.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/api/graphql/admin/generated/find_development_theme_by_name.d.ts +16 -0
  3. package/dist/cli/api/graphql/admin/generated/find_development_theme_by_name.js +60 -0
  4. package/dist/cli/api/graphql/admin/generated/find_development_theme_by_name.js.map +1 -0
  5. package/dist/private/node/api/graphql.d.ts +10 -0
  6. package/dist/private/node/api/graphql.js +67 -5
  7. package/dist/private/node/api/graphql.js.map +1 -1
  8. package/dist/private/node/conf-store.d.ts +13 -0
  9. package/dist/private/node/conf-store.js +17 -3
  10. package/dist/private/node/conf-store.js.map +1 -1
  11. package/dist/private/node/constants.d.ts +3 -0
  12. package/dist/private/node/constants.js +3 -0
  13. package/dist/private/node/constants.js.map +1 -1
  14. package/dist/private/node/session/exchange.d.ts +22 -8
  15. package/dist/private/node/session/exchange.js +37 -14
  16. package/dist/private/node/session/exchange.js.map +1 -1
  17. package/dist/private/node/session/schema.d.ts +62 -62
  18. package/dist/private/node/session/scopes.js +3 -2
  19. package/dist/private/node/session/scopes.js.map +1 -1
  20. package/dist/private/node/session/store.d.ts +7 -0
  21. package/dist/private/node/session/store.js +17 -0
  22. package/dist/private/node/session/store.js.map +1 -1
  23. package/dist/private/node/session/validate.d.ts +5 -4
  24. package/dist/private/node/session/validate.js +34 -7
  25. package/dist/private/node/session/validate.js.map +1 -1
  26. package/dist/private/node/session.d.ts +1 -1
  27. package/dist/private/node/session.js +77 -32
  28. package/dist/private/node/session.js.map +1 -1
  29. package/dist/private/node/testing/ui.js +6 -4
  30. package/dist/private/node/testing/ui.js.map +1 -1
  31. package/dist/private/node/ui/components/AutocompletePrompt.test.js +5 -5
  32. package/dist/private/node/ui/components/AutocompletePrompt.test.js.map +1 -1
  33. package/dist/private/node/ui/components/ConcurrentOutput.js +5 -2
  34. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  35. package/dist/private/node/ui/components/ConcurrentOutput.test.js +2 -2
  36. package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
  37. package/dist/private/node/ui/components/SelectInput.test.js +10 -10
  38. package/dist/private/node/ui/components/SelectInput.test.js.map +1 -1
  39. package/dist/private/node/ui/components/Table/Table.js +2 -2
  40. package/dist/private/node/ui/components/Table/Table.js.map +1 -1
  41. package/dist/private/node/ui/components/Tasks.test.js +6 -6
  42. package/dist/private/node/ui/components/Tasks.test.js.map +1 -1
  43. package/dist/private/node/ui/components/TextAnimation.test.js +1 -1
  44. package/dist/private/node/ui/components/TextAnimation.test.js.map +1 -1
  45. package/dist/private/node/ui/components/TextInput.test.js +4 -4
  46. package/dist/private/node/ui/components/TextInput.test.js.map +1 -1
  47. package/dist/private/node/ui/components/TokenizedText.js +1 -0
  48. package/dist/private/node/ui/components/TokenizedText.js.map +1 -1
  49. package/dist/private/node/ui/hooks/use-select-state.js +1 -3
  50. package/dist/private/node/ui/hooks/use-select-state.js.map +1 -1
  51. package/dist/public/common/version.d.ts +1 -1
  52. package/dist/public/common/version.js +1 -1
  53. package/dist/public/common/version.js.map +1 -1
  54. package/dist/public/node/analytics.js +4 -4
  55. package/dist/public/node/analytics.js.map +1 -1
  56. package/dist/public/node/api/admin.js +1 -3
  57. package/dist/public/node/api/admin.js.map +1 -1
  58. package/dist/public/node/api/rest-api-throttler.d.ts +14 -0
  59. package/dist/public/node/api/rest-api-throttler.js +14 -87
  60. package/dist/public/node/api/rest-api-throttler.js.map +1 -1
  61. package/dist/public/node/archiver.js +6 -7
  62. package/dist/public/node/archiver.js.map +1 -1
  63. package/dist/public/node/cli-launcher.d.ts +1 -1
  64. package/dist/public/node/cli-launcher.js +9 -9
  65. package/dist/public/node/cli-launcher.js.map +1 -1
  66. package/dist/public/node/cli.js +4 -1
  67. package/dist/public/node/cli.js.map +1 -1
  68. package/dist/public/node/context/fqdn.js +1 -0
  69. package/dist/public/node/context/fqdn.js.map +1 -1
  70. package/dist/public/node/context/local.d.ts +7 -0
  71. package/dist/public/node/context/local.js +9 -0
  72. package/dist/public/node/context/local.js.map +1 -1
  73. package/dist/public/node/dot-env.js +1 -1
  74. package/dist/public/node/dot-env.js.map +1 -1
  75. package/dist/public/node/environment.d.ts +4 -9
  76. package/dist/public/node/environment.js +6 -12
  77. package/dist/public/node/environment.js.map +1 -1
  78. package/dist/public/node/environments.js +4 -3
  79. package/dist/public/node/environments.js.map +1 -1
  80. package/dist/public/node/error-handler.js +1 -1
  81. package/dist/public/node/error-handler.js.map +1 -1
  82. package/dist/public/node/error.js +1 -0
  83. package/dist/public/node/error.js.map +1 -1
  84. package/dist/public/node/fs.d.ts +9 -1
  85. package/dist/public/node/fs.js +16 -4
  86. package/dist/public/node/fs.js.map +1 -1
  87. package/dist/public/node/git.d.ts +10 -4
  88. package/dist/public/node/git.js +101 -80
  89. package/dist/public/node/git.js.map +1 -1
  90. package/dist/public/node/global-context.js +1 -3
  91. package/dist/public/node/global-context.js.map +1 -1
  92. package/dist/public/node/hooks/postrun.d.ts +7 -0
  93. package/dist/public/node/hooks/postrun.js +61 -1
  94. package/dist/public/node/hooks/postrun.js.map +1 -1
  95. package/dist/public/node/hooks/prerun.d.ts +3 -2
  96. package/dist/public/node/hooks/prerun.js +9 -22
  97. package/dist/public/node/hooks/prerun.js.map +1 -1
  98. package/dist/public/node/http.js +1 -1
  99. package/dist/public/node/http.js.map +1 -1
  100. package/dist/public/node/import-extractor.d.ts +17 -0
  101. package/dist/public/node/import-extractor.js +84 -23
  102. package/dist/public/node/import-extractor.js.map +1 -1
  103. package/dist/public/node/is-global.d.ts +9 -1
  104. package/dist/public/node/is-global.js +55 -12
  105. package/dist/public/node/is-global.js.map +1 -1
  106. package/dist/public/node/mimes.js +1 -1
  107. package/dist/public/node/mimes.js.map +1 -1
  108. package/dist/public/node/node-package-manager.d.ts +1 -1
  109. package/dist/public/node/node-package-manager.js +4 -1
  110. package/dist/public/node/node-package-manager.js.map +1 -1
  111. package/dist/public/node/os.js +1 -0
  112. package/dist/public/node/os.js.map +1 -1
  113. package/dist/public/node/output.js +3 -2
  114. package/dist/public/node/output.js.map +1 -1
  115. package/dist/public/node/path.d.ts +11 -0
  116. package/dist/public/node/path.js +30 -1
  117. package/dist/public/node/path.js.map +1 -1
  118. package/dist/public/node/result.js +1 -0
  119. package/dist/public/node/result.js.map +1 -1
  120. package/dist/public/node/serial-batch-processor.js +1 -3
  121. package/dist/public/node/serial-batch-processor.js.map +1 -1
  122. package/dist/public/node/session-prompt.js +10 -2
  123. package/dist/public/node/session-prompt.js.map +1 -1
  124. package/dist/public/node/session.js +6 -6
  125. package/dist/public/node/session.js.map +1 -1
  126. package/dist/public/node/themes/api.d.ts +1 -0
  127. package/dist/public/node/themes/api.js +26 -0
  128. package/dist/public/node/themes/api.js.map +1 -1
  129. package/dist/public/node/themes/conf.js +3 -5
  130. package/dist/public/node/themes/conf.js.map +1 -1
  131. package/dist/public/node/themes/factories.js +1 -0
  132. package/dist/public/node/themes/factories.js.map +1 -1
  133. package/dist/public/node/themes/theme-manager.d.ts +2 -2
  134. package/dist/public/node/themes/theme-manager.js +13 -8
  135. package/dist/public/node/themes/theme-manager.js.map +1 -1
  136. package/dist/public/node/{toml.d.ts → toml/codec.d.ts} +1 -1
  137. package/dist/public/node/{toml.js → toml/codec.js} +1 -1
  138. package/dist/public/node/toml/codec.js.map +1 -0
  139. package/dist/public/node/toml/index.d.ts +1 -0
  140. package/dist/public/node/toml/index.js +2 -0
  141. package/dist/public/node/toml/index.js.map +1 -0
  142. package/dist/public/node/toml/toml-file.d.ts +88 -0
  143. package/dist/public/node/toml/toml-file.js +159 -0
  144. package/dist/public/node/toml/toml-file.js.map +1 -0
  145. package/dist/public/node/tree-kill.js +1 -1
  146. package/dist/public/node/tree-kill.js.map +1 -1
  147. package/dist/public/node/ui.js +1 -0
  148. package/dist/public/node/ui.js.map +1 -1
  149. package/dist/public/node/upgrade.d.ts +28 -1
  150. package/dist/public/node/upgrade.js +184 -16
  151. package/dist/public/node/upgrade.js.map +1 -1
  152. package/dist/public/node/version.d.ts +9 -0
  153. package/dist/public/node/version.js +16 -1
  154. package/dist/public/node/version.js.map +1 -1
  155. package/dist/tsconfig.tsbuildinfo +1 -1
  156. package/package.json +12 -12
  157. package/dist/public/node/custom-oclif-loader.d.ts +0 -6
  158. package/dist/public/node/custom-oclif-loader.js +0 -79
  159. package/dist/public/node/custom-oclif-loader.js.map +0 -1
  160. package/dist/public/node/toml.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"prerun.js","sourceRoot":"","sources":["../../../../src/public/node/hooks/prerun.ts"],"names":[],"mappings":"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","sourcesContent":["import {CLI_KIT_VERSION} from '../../common/version.js'\nimport {checkForNewVersion, checkForCachedNewVersion} from '../node-package-manager.js'\nimport {startAnalytics} from '../../../private/node/analytics.js'\nimport {outputDebug, outputWarn} from '../output.js'\nimport {getOutputUpdateCLIReminder} from '../upgrade.js'\nimport Command from '../base-command.js'\nimport {runAtMinimumInterval} from '../../../private/node/conf-store.js'\nimport {fetchNotificationsInBackground} from '../notifications-system.js'\nimport {isPreReleaseVersion} from '../version.js'\nimport {Hook} from '@oclif/core'\n\nexport declare interface CommandContent {\n command: string\n topic?: string\n alias?: string\n}\n// This hook is called before each command run. More info: https://oclif.io/docs/hooks\nexport const hook: Hook.Prerun = async (options) => {\n const commandContent = parseCommandContent({\n id: options.Command.id,\n aliases: options.Command.aliases,\n pluginAlias: options.Command.plugin?.alias,\n })\n const args = options.argv\n await warnOnAvailableUpgrade()\n outputDebug(`Running command ${commandContent.command}`)\n await startAnalytics({commandContent, args, commandClass: options.Command as unknown as typeof Command})\n fetchNotificationsInBackground(options.Command.id)\n}\n\nexport function parseCommandContent(cmdInfo: {id: string; aliases: string[]; pluginAlias?: string}): CommandContent {\n let commandContent = parseCreateCommand(cmdInfo.pluginAlias)\n if (!commandContent) {\n commandContent = parseNormalCommand(cmdInfo.id, cmdInfo.aliases)\n }\n return commandContent\n}\n\nfunction parseNormalCommand(id: string, aliases: string[]): CommandContent {\n return {\n command: id.replace(/:/g, ' '),\n topic: parseTopic(id),\n alias: findAlias(aliases),\n }\n}\n\n/**\n * Create commands implement Init by default, so the name of the command must be extracted from\n * the plugin/module name. Neither alias or topic are supported\n *\n * @param commandClass - Oclif command configuration\n * @returns Command content with the name of the command or undefined otherwise\n */\nfunction parseCreateCommand(pluginAlias?: string): CommandContent | undefined {\n if (!pluginAlias?.startsWith('@shopify/create-')) {\n return undefined\n }\n\n return {command: pluginAlias.substring(pluginAlias.indexOf('/') + 1)}\n}\n\n/**\n * Commands use this pattern topic:subtopic1:...:subtopicN:command. This method extract the topic and subtopic\n * information replacing the ':' separator with one space\n *\n * @param cmd - Complete command string to extract the topic information\n * @returns The topic name or undefined otherwise\n */\nfunction parseTopic(cmd: string) {\n if (cmd.lastIndexOf(':') === -1) {\n return\n }\n return cmd.slice(0, cmd.lastIndexOf(':')).replace(/:/g, ' ')\n}\n\n/**\n * Identifies if the command was launched using an alias instead of the oficial command name\n *\n * @param aliases - List of possible alias a command has\n * @returns The alias used or undefined otherwise\n */\nfunction findAlias(aliases: string[]) {\n const existingAlias = aliases.find((alias) =>\n alias.split(':').every((aliasToken) => process.argv.includes(aliasToken)),\n )\n if (existingAlias) {\n return existingAlias.replace(/:/g, ' ')\n }\n}\n\n/**\n * Warns the user if there is a new version of the CLI available\n */\nexport async function warnOnAvailableUpgrade(): Promise<void> {\n const cliDependency = '@shopify/cli'\n const currentVersion = CLI_KIT_VERSION\n if (isPreReleaseVersion(currentVersion)) {\n // This is a nightly/snapshot/experimental version, so we don't want to check for updates\n return\n }\n\n // Check in the background, once daily\n // eslint-disable-next-line no-void\n void checkForNewVersion(cliDependency, currentVersion, {cacheExpiryInHours: 24})\n\n // Warn if we previously found a new version\n await runAtMinimumInterval('warn-on-available-upgrade', {days: 1}, async () => {\n const newerVersion = checkForCachedNewVersion(cliDependency, currentVersion)\n if (newerVersion) {\n outputWarn(getOutputUpdateCLIReminder(newerVersion))\n }\n })\n}\n"]}
1
+ {"version":3,"file":"prerun.js","sourceRoot":"","sources":["../../../../src/public/node/hooks/prerun.ts"],"names":[],"mappings":"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","sourcesContent":["import {CLI_KIT_VERSION} from '../../common/version.js'\nimport {isPreReleaseVersion} from '../version.js'\nimport {checkForNewVersion} from '../node-package-manager.js'\nimport {startAnalytics} from '../../../private/node/analytics.js'\nimport {outputDebug} from '../output.js'\nimport Command from '../base-command.js'\nimport {fetchNotificationsInBackground} from '../notifications-system.js'\nimport {Hook} from '@oclif/core'\n\nexport declare interface CommandContent {\n command: string\n topic?: string\n alias?: string\n}\n// This hook is called before each command run. More info: https://oclif.io/docs/hooks\nexport const hook: Hook.Prerun = async (options) => {\n const commandContent = parseCommandContent({\n id: options.Command.id,\n aliases: options.Command.aliases,\n pluginAlias: options.Command.plugin?.alias,\n })\n const args = options.argv\n checkForNewVersionInBackground()\n outputDebug(`Running command ${commandContent.command}`)\n await startAnalytics({commandContent, args, commandClass: options.Command as unknown as typeof Command})\n fetchNotificationsInBackground(options.Command.id)\n}\n\nexport function parseCommandContent(cmdInfo: {id: string; aliases: string[]; pluginAlias?: string}): CommandContent {\n let commandContent = parseCreateCommand(cmdInfo.pluginAlias)\n commandContent ??= parseNormalCommand(cmdInfo.id, cmdInfo.aliases)\n return commandContent\n}\n\nfunction parseNormalCommand(id: string, aliases: string[]): CommandContent {\n return {\n command: id.replace(/:/g, ' '),\n topic: parseTopic(id),\n alias: findAlias(aliases),\n }\n}\n\n/**\n * Create commands implement Init by default, so the name of the command must be extracted from\n * the plugin/module name. Neither alias or topic are supported\n *\n * @param commandClass - Oclif command configuration\n * @returns Command content with the name of the command or undefined otherwise\n */\nfunction parseCreateCommand(pluginAlias?: string): CommandContent | undefined {\n if (!pluginAlias?.startsWith('@shopify/create-')) {\n return undefined\n }\n\n return {command: pluginAlias.substring(pluginAlias.indexOf('/') + 1)}\n}\n\n/**\n * Commands use this pattern topic:subtopic1:...:subtopicN:command. This method extract the topic and subtopic\n * information replacing the ':' separator with one space\n *\n * @param cmd - Complete command string to extract the topic information\n * @returns The topic name or undefined otherwise\n */\nfunction parseTopic(cmd: string) {\n if (cmd.lastIndexOf(':') === -1) {\n return\n }\n return cmd.slice(0, cmd.lastIndexOf(':')).replace(/:/g, ' ')\n}\n\n/**\n * Identifies if the command was launched using an alias instead of the oficial command name\n *\n * @param aliases - List of possible alias a command has\n * @returns The alias used or undefined otherwise\n */\nfunction findAlias(aliases: string[]) {\n const existingAlias = aliases.find((alias) =>\n alias.split(':').every((aliasToken) => process.argv.includes(aliasToken)),\n )\n if (existingAlias) {\n return existingAlias.replace(/:/g, ' ')\n }\n}\n\n/**\n * Triggers a background check for a newer CLI version (non-blocking).\n * The result is cached and consumed by the postrun hook for auto-upgrade.\n */\nexport function checkForNewVersionInBackground(): void {\n const currentVersion = CLI_KIT_VERSION\n if (isPreReleaseVersion(currentVersion)) {\n return\n }\n // eslint-disable-next-line no-void\n void checkForNewVersion('@shopify/cli', currentVersion, {cacheExpiryInHours: 24})\n}\n"]}
@@ -200,7 +200,7 @@ export function downloadFile(url, to) {
200
200
  })
201
201
  .catch((err) => {
202
202
  tryToRemoveFile();
203
- reject(err);
203
+ reject(err instanceof Error ? err : new Error(String(err)));
204
204
  });
205
205
  });
206
206
  });
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/public/node/http.ts"],"names":[],"mappings":"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","sourcesContent":["import {dirname} from './path.js'\nimport {createFileWriteStream, fileExistsSync, mkdirSync, unlinkFileSync} from './fs.js'\nimport {runWithTimer} from './metadata.js'\nimport {maxRequestTimeForNetworkCallsMs, skipNetworkLevelRetry} from './environment.js'\nimport {outputContent, outputDebug, outputToken} from './output.js'\nimport {sanitizeURL} from '../../private/node/api/urls.js'\nimport {httpsAgent, sanitizedHeadersOutput} from '../../private/node/api/headers.js'\nimport {NetworkRetryBehaviour, simpleRequestWithDebugLog} from '../../private/node/api.js'\nimport {DEFAULT_MAX_TIME_MS} from '../../private/node/sleep-with-backoff.js'\n\nimport FormData from 'form-data'\nimport nodeFetch, {RequestInfo, RequestInit, Response} from 'node-fetch'\n\nexport {FetchError, Request, Response} from 'node-fetch'\n\n/**\n * Create a new FormData object.\n *\n * @returns A FormData object.\n */\nexport function formData(): FormData {\n return new FormData()\n}\n\ntype AbortSignal = RequestInit['signal']\n\ntype PresetFetchBehaviour = 'default' | 'non-blocking' | 'slow-request'\n\ntype AutomaticCancellationBehaviour =\n | {\n useAbortSignal: true\n timeoutMs: number\n }\n | {\n useAbortSignal: false\n }\n | {\n useAbortSignal: AbortSignal | (() => AbortSignal)\n }\n\nexport type RequestBehaviour = NetworkRetryBehaviour & AutomaticCancellationBehaviour\n\nexport type RequestModeInput = PresetFetchBehaviour | RequestBehaviour\n\n/**\n * Specify the behaviour of a network request.\n *\n * - default: Requests are automatically retried, and are subject to automatic cancellation if they're taking too long.\n * This is generally desirable.\n * - non-blocking: Requests are not retried if they fail with a network error, and are automatically cancelled if\n * they're taking too long. This is good for throwaway requests, like polling or tracking.\n * - slow-request: Requests are not retried if they fail with a network error, and are not automatically cancelled.\n * This is good for slow requests that should be give the chance to complete, and are unlikely to be safe to retry.\n *\n * Some request behaviours may be de-activated by the environment, and this function takes care of that concern. You\n * can also provide a customised request behaviour.\n *\n * @param preset - The preset to use.\n * @param env - Process environment variables.\n * @returns A request behaviour object.\n */\nexport function requestMode(\n preset: RequestModeInput = 'default',\n env: NodeJS.ProcessEnv = process.env,\n): RequestBehaviour {\n const networkLevelRetryIsSupported = !skipNetworkLevelRetry(env)\n switch (preset) {\n case 'default':\n return {\n useNetworkLevelRetry: networkLevelRetryIsSupported,\n maxRetryTimeMs: DEFAULT_MAX_TIME_MS,\n useAbortSignal: true,\n timeoutMs: maxRequestTimeForNetworkCallsMs(env),\n }\n case 'non-blocking':\n return {\n useNetworkLevelRetry: false,\n useAbortSignal: true,\n timeoutMs: maxRequestTimeForNetworkCallsMs(env),\n }\n case 'slow-request':\n return {\n useNetworkLevelRetry: false,\n useAbortSignal: false,\n }\n }\n return {\n ...preset,\n useNetworkLevelRetry: networkLevelRetryIsSupported && preset.useNetworkLevelRetry,\n } as RequestBehaviour\n}\n\ninterface FetchOptions {\n url: RequestInfo\n behaviour: RequestBehaviour\n init?: RequestInit\n logRequest: boolean\n useHttpsAgent: boolean\n}\n\n/**\n * Create an AbortSignal for automatic request cancellation, from a request behaviour.\n *\n * @param behaviour - The request behaviour.\n * @returns An AbortSignal.\n */\nexport function abortSignalFromRequestBehaviour(behaviour: RequestBehaviour): AbortSignal {\n let signal: AbortSignal\n if (behaviour.useAbortSignal === true) {\n signal = AbortSignal.timeout(behaviour.timeoutMs)\n } else if (behaviour.useAbortSignal && typeof behaviour.useAbortSignal === 'function') {\n signal = behaviour.useAbortSignal()\n } else if (behaviour.useAbortSignal) {\n signal = behaviour.useAbortSignal\n }\n return signal\n}\n\nasync function innerFetch({url, behaviour, init, logRequest, useHttpsAgent}: FetchOptions): Promise<Response> {\n if (logRequest) {\n outputDebug(outputContent`Sending ${init?.method ?? 'GET'} request to URL ${sanitizeURL(url.toString())}\nWith request headers:\n${sanitizedHeadersOutput((init?.headers ?? {}) as Record<string, string>)}\n`)\n }\n\n let agent: RequestInit['agent']\n if (useHttpsAgent) {\n agent = await httpsAgent()\n }\n\n const request = async () => {\n // each time we make the request, we need to potentially reset the abort signal, as the request logic may make\n // the same request multiple times.\n let signal = abortSignalFromRequestBehaviour(behaviour)\n\n // it's possible to provide a signal through the request's init structure.\n if (init?.signal) {\n signal = init.signal\n }\n\n return nodeFetch(url, {...init, agent, signal})\n }\n\n return runWithTimer('cmd_all_timing_network_ms')(async () => {\n return simpleRequestWithDebugLog({\n url: url.toString(),\n request,\n ...behaviour,\n })\n })\n}\n\n/**\n * An interface that abstracts way node-fetch. When Node has built-in\n * support for \"fetch\" in the standard library, we can drop the node-fetch\n * dependency from here.\n * Note that we are exposing types from \"node-fetch\". The reason being is that\n * they are consistent with the Web API so if we drop node-fetch in the future\n * it won't require changes from the callers.\n *\n * The CLI's fetch function supports special behaviours, like automatic retries. These are disabled by default through\n * this function.\n *\n * @param url - This defines the resource that you wish to fetch.\n * @param init - An object containing any custom settings that you want to apply to the request.\n * @param preferredBehaviour - A request behaviour object that overrides the default behaviour.\n * @returns A promise that resolves with the response.\n */\nexport async function fetch(\n url: RequestInfo,\n init?: RequestInit,\n preferredBehaviour?: RequestModeInput,\n): Promise<Response> {\n const options = {\n url,\n init,\n logRequest: false,\n useHttpsAgent: false,\n // all special behaviours are disabled by default\n behaviour: preferredBehaviour ? requestMode(preferredBehaviour) : requestMode('non-blocking'),\n } as const\n\n return innerFetch(options)\n}\n\n/**\n * A fetch function to use with Shopify services. The function ensures the right\n * TLS configuragion is used based on the environment in which the service is running\n * (e.g. Local). NB: headers/auth are the responsibility of the caller.\n *\n * By default, the CLI's fetch function's special behaviours, like automatic retries, are enabled.\n *\n * @param url - This defines the resource that you wish to fetch.\n * @param init - An object containing any custom settings that you want to apply to the request.\n * @param preferredBehaviour - A request behaviour object that overrides the default behaviour.\n * @returns A promise that resolves with the response.\n */\nexport async function shopifyFetch(\n url: RequestInfo,\n init?: RequestInit,\n preferredBehaviour?: RequestModeInput,\n): Promise<Response> {\n const options = {\n url,\n init,\n logRequest: true,\n useHttpsAgent: true,\n // special behaviours enabled by default\n behaviour: preferredBehaviour ? requestMode(preferredBehaviour) : requestMode(),\n }\n\n return innerFetch(options)\n}\n\n/**\n * Download a file from a URL to a local path.\n *\n * @param url - The URL to download from.\n * @param to - The local path to download to.\n * @returns - A promise that resolves with the local path.\n */\nexport function downloadFile(url: string, to: string): Promise<string> {\n const sanitizedUrl = sanitizeURL(url)\n outputDebug(`Downloading ${sanitizedUrl} to ${to}`)\n\n return runWithTimer('cmd_all_timing_network_ms')(() => {\n return new Promise<string>((resolve, reject) => {\n if (!fileExistsSync(dirname(to))) {\n mkdirSync(dirname(to))\n }\n\n const file = createFileWriteStream(to)\n\n // if we can't remove the file for some reason (seen on windows), that's ok -- it's in a temporary directory\n const tryToRemoveFile = () => {\n try {\n unlinkFileSync(to)\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (err: unknown) {\n outputDebug(outputContent`Failed to remove file ${outputToken.path(to)}: ${outputToken.raw(String(err))}`)\n }\n }\n\n file.on('finish', () => {\n file.close()\n resolve(to)\n })\n\n file.on('error', (err) => {\n tryToRemoveFile()\n reject(err)\n })\n\n nodeFetch(url, {redirect: 'follow'})\n .then((res) => {\n res.body?.pipe(file)\n })\n .catch((err) => {\n tryToRemoveFile()\n reject(err)\n })\n })\n })\n}\n"]}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/public/node/http.ts"],"names":[],"mappings":"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","sourcesContent":["import {dirname} from './path.js'\nimport {createFileWriteStream, fileExistsSync, mkdirSync, unlinkFileSync} from './fs.js'\nimport {runWithTimer} from './metadata.js'\nimport {maxRequestTimeForNetworkCallsMs, skipNetworkLevelRetry} from './environment.js'\nimport {outputContent, outputDebug, outputToken} from './output.js'\nimport {sanitizeURL} from '../../private/node/api/urls.js'\nimport {httpsAgent, sanitizedHeadersOutput} from '../../private/node/api/headers.js'\nimport {NetworkRetryBehaviour, simpleRequestWithDebugLog} from '../../private/node/api.js'\nimport {DEFAULT_MAX_TIME_MS} from '../../private/node/sleep-with-backoff.js'\n\nimport FormData from 'form-data'\nimport nodeFetch, {RequestInfo, RequestInit, Response} from 'node-fetch'\n\nexport {FetchError, Request, Response} from 'node-fetch'\n\n/**\n * Create a new FormData object.\n *\n * @returns A FormData object.\n */\nexport function formData(): FormData {\n return new FormData()\n}\n\ntype AbortSignal = RequestInit['signal']\n\ntype PresetFetchBehaviour = 'default' | 'non-blocking' | 'slow-request'\n\ntype AutomaticCancellationBehaviour =\n | {\n useAbortSignal: true\n timeoutMs: number\n }\n | {\n useAbortSignal: false\n }\n | {\n useAbortSignal: AbortSignal | (() => AbortSignal)\n }\n\nexport type RequestBehaviour = NetworkRetryBehaviour & AutomaticCancellationBehaviour\n\nexport type RequestModeInput = PresetFetchBehaviour | RequestBehaviour\n\n/**\n * Specify the behaviour of a network request.\n *\n * - default: Requests are automatically retried, and are subject to automatic cancellation if they're taking too long.\n * This is generally desirable.\n * - non-blocking: Requests are not retried if they fail with a network error, and are automatically cancelled if\n * they're taking too long. This is good for throwaway requests, like polling or tracking.\n * - slow-request: Requests are not retried if they fail with a network error, and are not automatically cancelled.\n * This is good for slow requests that should be give the chance to complete, and are unlikely to be safe to retry.\n *\n * Some request behaviours may be de-activated by the environment, and this function takes care of that concern. You\n * can also provide a customised request behaviour.\n *\n * @param preset - The preset to use.\n * @param env - Process environment variables.\n * @returns A request behaviour object.\n */\nexport function requestMode(\n preset: RequestModeInput = 'default',\n env: NodeJS.ProcessEnv = process.env,\n): RequestBehaviour {\n const networkLevelRetryIsSupported = !skipNetworkLevelRetry(env)\n switch (preset) {\n case 'default':\n return {\n useNetworkLevelRetry: networkLevelRetryIsSupported,\n maxRetryTimeMs: DEFAULT_MAX_TIME_MS,\n useAbortSignal: true,\n timeoutMs: maxRequestTimeForNetworkCallsMs(env),\n }\n case 'non-blocking':\n return {\n useNetworkLevelRetry: false,\n useAbortSignal: true,\n timeoutMs: maxRequestTimeForNetworkCallsMs(env),\n }\n case 'slow-request':\n return {\n useNetworkLevelRetry: false,\n useAbortSignal: false,\n }\n }\n return {\n ...preset,\n useNetworkLevelRetry: networkLevelRetryIsSupported && preset.useNetworkLevelRetry,\n } as RequestBehaviour\n}\n\ninterface FetchOptions {\n url: RequestInfo\n behaviour: RequestBehaviour\n init?: RequestInit\n logRequest: boolean\n useHttpsAgent: boolean\n}\n\n/**\n * Create an AbortSignal for automatic request cancellation, from a request behaviour.\n *\n * @param behaviour - The request behaviour.\n * @returns An AbortSignal.\n */\nexport function abortSignalFromRequestBehaviour(behaviour: RequestBehaviour): AbortSignal {\n let signal: AbortSignal\n if (behaviour.useAbortSignal === true) {\n signal = AbortSignal.timeout(behaviour.timeoutMs)\n } else if (behaviour.useAbortSignal && typeof behaviour.useAbortSignal === 'function') {\n signal = behaviour.useAbortSignal()\n } else if (behaviour.useAbortSignal) {\n signal = behaviour.useAbortSignal\n }\n return signal\n}\n\nasync function innerFetch({url, behaviour, init, logRequest, useHttpsAgent}: FetchOptions): Promise<Response> {\n if (logRequest) {\n outputDebug(outputContent`Sending ${init?.method ?? 'GET'} request to URL ${sanitizeURL(url.toString())}\nWith request headers:\n${sanitizedHeadersOutput((init?.headers ?? {}) as Record<string, string>)}\n`)\n }\n\n let agent: RequestInit['agent']\n if (useHttpsAgent) {\n agent = await httpsAgent()\n }\n\n const request = async () => {\n // each time we make the request, we need to potentially reset the abort signal, as the request logic may make\n // the same request multiple times.\n let signal = abortSignalFromRequestBehaviour(behaviour)\n\n // it's possible to provide a signal through the request's init structure.\n if (init?.signal) {\n signal = init.signal\n }\n\n return nodeFetch(url, {...init, agent, signal})\n }\n\n return runWithTimer('cmd_all_timing_network_ms')(async () => {\n return simpleRequestWithDebugLog({\n url: url.toString(),\n request,\n ...behaviour,\n })\n })\n}\n\n/**\n * An interface that abstracts way node-fetch. When Node has built-in\n * support for \"fetch\" in the standard library, we can drop the node-fetch\n * dependency from here.\n * Note that we are exposing types from \"node-fetch\". The reason being is that\n * they are consistent with the Web API so if we drop node-fetch in the future\n * it won't require changes from the callers.\n *\n * The CLI's fetch function supports special behaviours, like automatic retries. These are disabled by default through\n * this function.\n *\n * @param url - This defines the resource that you wish to fetch.\n * @param init - An object containing any custom settings that you want to apply to the request.\n * @param preferredBehaviour - A request behaviour object that overrides the default behaviour.\n * @returns A promise that resolves with the response.\n */\nexport async function fetch(\n url: RequestInfo,\n init?: RequestInit,\n preferredBehaviour?: RequestModeInput,\n): Promise<Response> {\n const options = {\n url,\n init,\n logRequest: false,\n useHttpsAgent: false,\n // all special behaviours are disabled by default\n behaviour: preferredBehaviour ? requestMode(preferredBehaviour) : requestMode('non-blocking'),\n } as const\n\n return innerFetch(options)\n}\n\n/**\n * A fetch function to use with Shopify services. The function ensures the right\n * TLS configuragion is used based on the environment in which the service is running\n * (e.g. Local). NB: headers/auth are the responsibility of the caller.\n *\n * By default, the CLI's fetch function's special behaviours, like automatic retries, are enabled.\n *\n * @param url - This defines the resource that you wish to fetch.\n * @param init - An object containing any custom settings that you want to apply to the request.\n * @param preferredBehaviour - A request behaviour object that overrides the default behaviour.\n * @returns A promise that resolves with the response.\n */\nexport async function shopifyFetch(\n url: RequestInfo,\n init?: RequestInit,\n preferredBehaviour?: RequestModeInput,\n): Promise<Response> {\n const options = {\n url,\n init,\n logRequest: true,\n useHttpsAgent: true,\n // special behaviours enabled by default\n behaviour: preferredBehaviour ? requestMode(preferredBehaviour) : requestMode(),\n }\n\n return innerFetch(options)\n}\n\n/**\n * Download a file from a URL to a local path.\n *\n * @param url - The URL to download from.\n * @param to - The local path to download to.\n * @returns - A promise that resolves with the local path.\n */\nexport function downloadFile(url: string, to: string): Promise<string> {\n const sanitizedUrl = sanitizeURL(url)\n outputDebug(`Downloading ${sanitizedUrl} to ${to}`)\n\n return runWithTimer('cmd_all_timing_network_ms')(() => {\n return new Promise<string>((resolve, reject) => {\n if (!fileExistsSync(dirname(to))) {\n mkdirSync(dirname(to))\n }\n\n const file = createFileWriteStream(to)\n\n // if we can't remove the file for some reason (seen on windows), that's ok -- it's in a temporary directory\n const tryToRemoveFile = () => {\n try {\n unlinkFileSync(to)\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (err: unknown) {\n outputDebug(outputContent`Failed to remove file ${outputToken.path(to)}: ${outputToken.raw(String(err))}`)\n }\n }\n\n file.on('finish', () => {\n file.close()\n resolve(to)\n })\n\n file.on('error', (err) => {\n tryToRemoveFile()\n reject(err)\n })\n\n nodeFetch(url, {redirect: 'follow'})\n .then((res) => {\n res.body?.pipe(file)\n })\n .catch((err) => {\n tryToRemoveFile()\n reject(err instanceof Error ? err : new Error(String(err)))\n })\n })\n })\n}\n"]}
@@ -1,6 +1,12 @@
1
+ /**
2
+ * Clears all import-scanning caches (direct imports, recursive results, and filesystem stats).
3
+ * Should be called when watched files change so that rescanning picks up updated imports.
4
+ */
5
+ export declare function clearImportPathsCache(): void;
1
6
  /**
2
7
  * Extracts import paths from a source file.
3
8
  * Supports JavaScript, TypeScript, and Rust files.
9
+ * Results are cached per file path to avoid redundant I/O.
4
10
  *
5
11
  * @param filePath - Path to the file to analyze.
6
12
  * @returns Array of absolute paths to imported files.
@@ -17,6 +23,17 @@ export declare function extractImportPaths(filePath: string): string[];
17
23
  * @throws If an unexpected error occurs while processing files (not including ENOENT file not found errors).
18
24
  */
19
25
  export declare function extractImportPathsRecursively(filePath: string, visited?: Set<string>): string[];
26
+ /**
27
+ * Returns diagnostic information about the import scanning caches.
28
+ * Useful for debugging performance issues with --verbose.
29
+ *
30
+ * @returns Cache size stats for directImports, fileExists, and isDir.
31
+ */
32
+ export declare function getImportScanningCacheStats(): {
33
+ directImports: number;
34
+ fileExists: number;
35
+ isDir: number;
36
+ };
20
37
  /**
21
38
  * Extracts import paths from a JavaScript content.
22
39
  *
@@ -1,15 +1,70 @@
1
- import { readFileSync, fileExistsSync, isDirectorySync } from './fs.js';
1
+ import { fileExistsSync, isDirectorySync } from './fs.js';
2
2
  import { dirname, joinPath } from './path.js';
3
+ import { openSync, readSync, closeSync } from 'fs';
4
+ // Only read the first 128KB of each file for import scanning. This covers
5
+ // ~3,000+ lines which is more than enough to capture all static imports.
6
+ // Generated type files (e.g. graphql-codegen) can be tens of megabytes and
7
+ // reading them fully takes several seconds on some machines.
8
+ const MAX_READ_SIZE = 128 * 1024;
9
+ // Caches direct import results per file path to avoid redundant file reads and parsing
10
+ // when multiple extensions import the same shared code.
11
+ const directImportsCache = new Map();
12
+ // Caches filesystem stat results to avoid redundant synchronous I/O.
13
+ // Each stat call also triggers outputDebug overhead, so caching here
14
+ // avoids both the kernel round-trip and the debug string construction.
15
+ const fileExistsCache = new Map();
16
+ const isDirCache = new Map();
17
+ function cachedFileExists(path) {
18
+ const cached = fileExistsCache.get(path);
19
+ if (cached !== undefined)
20
+ return cached;
21
+ const result = fileExistsSync(path);
22
+ fileExistsCache.set(path, result);
23
+ return result;
24
+ }
25
+ function cachedIsDir(path) {
26
+ const cached = isDirCache.get(path);
27
+ if (cached !== undefined)
28
+ return cached;
29
+ const result = isDirectorySync(path);
30
+ isDirCache.set(path, result);
31
+ return result;
32
+ }
33
+ function readFileContent(filePath) {
34
+ const fd = openSync(filePath, 'r');
35
+ try {
36
+ const buffer = Buffer.alloc(MAX_READ_SIZE);
37
+ const bytesRead = readSync(fd, buffer, 0, MAX_READ_SIZE, 0);
38
+ return buffer.subarray(0, bytesRead).toString();
39
+ }
40
+ finally {
41
+ closeSync(fd);
42
+ }
43
+ }
44
+ /**
45
+ * Clears all import-scanning caches (direct imports, recursive results, and filesystem stats).
46
+ * Should be called when watched files change so that rescanning picks up updated imports.
47
+ */
48
+ export function clearImportPathsCache() {
49
+ directImportsCache.clear();
50
+ fileExistsCache.clear();
51
+ isDirCache.clear();
52
+ }
3
53
  /**
4
54
  * Extracts import paths from a source file.
5
55
  * Supports JavaScript, TypeScript, and Rust files.
56
+ * Results are cached per file path to avoid redundant I/O.
6
57
  *
7
58
  * @param filePath - Path to the file to analyze.
8
59
  * @returns Array of absolute paths to imported files.
9
60
  */
10
61
  export function extractImportPaths(filePath) {
11
- const content = readFileSync(filePath).toString();
62
+ const cached = directImportsCache.get(filePath);
63
+ if (cached)
64
+ return cached;
65
+ const content = readFileContent(filePath);
12
66
  const ext = filePath.substring(filePath.lastIndexOf('.'));
67
+ let result;
13
68
  switch (ext) {
14
69
  case '.js':
15
70
  case '.mjs':
@@ -17,12 +72,16 @@ export function extractImportPaths(filePath) {
17
72
  case '.ts':
18
73
  case '.tsx':
19
74
  case '.jsx':
20
- return extractJSLikeImports(content, filePath);
75
+ result = extractJSLikeImports(content, filePath);
76
+ break;
21
77
  case '.rs':
22
- return extractRustImports(content, filePath);
78
+ result = extractRustImports(content, filePath);
79
+ break;
23
80
  default:
24
- return [];
81
+ result = [];
25
82
  }
83
+ directImportsCache.set(filePath, result);
84
+ return result;
26
85
  }
27
86
  /**
28
87
  * Recursively extracts import paths from a source file and all its dependencies.
@@ -35,36 +94,41 @@ export function extractImportPaths(filePath) {
35
94
  * @throws If an unexpected error occurs while processing files (not including ENOENT file not found errors).
36
95
  */
37
96
  export function extractImportPathsRecursively(filePath, visited = new Set()) {
38
- // Avoid processing the same file twice (handles circular dependencies)
39
97
  if (visited.has(filePath)) {
40
98
  return [];
41
99
  }
42
100
  visited.add(filePath);
43
- // Get direct imports from this file
44
101
  const directImports = extractImportPaths(filePath);
45
102
  const allImports = [filePath, ...directImports];
46
- // Recursively process each imported file
47
103
  for (const importedFile of directImports) {
48
104
  try {
49
- // Only process files that exist and are not directories
50
- // Note: resolveJSImport already resolves directory imports to their index files
51
- if (fileExistsSync(importedFile) && !isDirectorySync(importedFile)) {
105
+ if (cachedFileExists(importedFile) && !cachedIsDir(importedFile)) {
52
106
  const nestedImports = extractImportPathsRecursively(importedFile, visited);
53
107
  allImports.push(...nestedImports);
54
108
  }
55
109
  }
56
110
  catch (error) {
57
- // Rethrow unexpected errors after checking for expected file read errors
58
111
  if (error instanceof Error && error.message.includes('ENOENT')) {
59
- // Skip files that don't exist or can't be read
60
112
  continue;
61
113
  }
62
114
  throw error;
63
115
  }
64
116
  }
65
- // Return unique list of imports
66
117
  return [...new Set(allImports)];
67
118
  }
119
+ /**
120
+ * Returns diagnostic information about the import scanning caches.
121
+ * Useful for debugging performance issues with --verbose.
122
+ *
123
+ * @returns Cache size stats for directImports, fileExists, and isDir.
124
+ */
125
+ export function getImportScanningCacheStats() {
126
+ return {
127
+ directImports: directImportsCache.size,
128
+ fileExists: fileExistsCache.size,
129
+ isDir: isDirCache.size,
130
+ };
131
+ }
68
132
  /**
69
133
  * Extracts import paths from a JavaScript content.
70
134
  *
@@ -124,7 +188,7 @@ function extractRustImports(content, filePath) {
124
188
  const pathValue = match[1];
125
189
  if (pathValue) {
126
190
  const resolvedPath = joinPath(dirname(filePath), pathValue);
127
- if (fileExistsSync(resolvedPath)) {
191
+ if (cachedFileExists(resolvedPath)) {
128
192
  imports.push(resolvedPath);
129
193
  }
130
194
  }
@@ -132,10 +196,9 @@ function extractRustImports(content, filePath) {
132
196
  return [...new Set(imports)];
133
197
  }
134
198
  function resolveJSImport(importPath, fromFile) {
135
- const basePath = fileExistsSync(fromFile) && isDirectorySync(fromFile) ? fromFile : dirname(fromFile);
199
+ const basePath = cachedFileExists(fromFile) && cachedIsDir(fromFile) ? fromFile : dirname(fromFile);
136
200
  const resolvedPath = joinPath(basePath, importPath);
137
- // If the import path resolves to a directory, look for index files
138
- if (fileExistsSync(resolvedPath) && isDirectorySync(resolvedPath)) {
201
+ if (cachedFileExists(resolvedPath) && cachedIsDir(resolvedPath)) {
139
202
  const indexPaths = [
140
203
  joinPath(resolvedPath, 'index.js'),
141
204
  joinPath(resolvedPath, 'index.ts'),
@@ -143,14 +206,12 @@ function resolveJSImport(importPath, fromFile) {
143
206
  joinPath(resolvedPath, 'index.jsx'),
144
207
  ];
145
208
  for (const indexPath of indexPaths) {
146
- if (fileExistsSync(indexPath) && !isDirectorySync(indexPath)) {
209
+ if (cachedFileExists(indexPath) && !cachedIsDir(indexPath)) {
147
210
  return indexPath;
148
211
  }
149
212
  }
150
- // If no index file found, don't return the directory
151
213
  return null;
152
214
  }
153
- // Check for file with extensions
154
215
  const possiblePaths = [
155
216
  resolvedPath,
156
217
  `${resolvedPath}.js`,
@@ -159,7 +220,7 @@ function resolveJSImport(importPath, fromFile) {
159
220
  `${resolvedPath}.jsx`,
160
221
  ];
161
222
  for (const path of possiblePaths) {
162
- if (fileExistsSync(path) && !isDirectorySync(path)) {
223
+ if (cachedFileExists(path) && !cachedIsDir(path)) {
163
224
  return path;
164
225
  }
165
226
  }
@@ -169,7 +230,7 @@ function resolveRustModule(modName, fromFile) {
169
230
  const basePath = dirname(fromFile);
170
231
  const possiblePaths = [joinPath(basePath, `${modName}.rs`), joinPath(basePath, modName, 'mod.rs')];
171
232
  for (const path of possiblePaths) {
172
- if (fileExistsSync(path)) {
233
+ if (cachedFileExists(path)) {
173
234
  return path;
174
235
  }
175
236
  }
@@ -1 +1 @@
1
- {"version":3,"file":"import-extractor.js","sourceRoot":"","sources":["../../../src/public/node/import-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["import {readFileSync, fileExistsSync, isDirectorySync} from './fs.js'\nimport {dirname, joinPath} from './path.js'\n\n/**\n * Extracts import paths from a source file.\n * Supports JavaScript, TypeScript, and Rust files.\n *\n * @param filePath - Path to the file to analyze.\n * @returns Array of absolute paths to imported files.\n */\nexport function extractImportPaths(filePath: string): string[] {\n const content = readFileSync(filePath).toString()\n const ext = filePath.substring(filePath.lastIndexOf('.'))\n\n switch (ext) {\n case '.js':\n case '.mjs':\n case '.cjs':\n case '.ts':\n case '.tsx':\n case '.jsx':\n return extractJSLikeImports(content, filePath)\n case '.rs':\n return extractRustImports(content, filePath)\n default:\n return []\n }\n}\n\n/**\n * Recursively extracts import paths from a source file and all its dependencies.\n * Supports JavaScript, TypeScript, and Rust files.\n * Handles circular dependencies by tracking visited files.\n *\n * @param filePath - Path to the file to analyze.\n * @param visited - Set of already visited files to prevent infinite recursion.\n * @returns Array of absolute paths to the provided file and all imported files there (including nested imports).\n * @throws If an unexpected error occurs while processing files (not including ENOENT file not found errors).\n */\nexport function extractImportPathsRecursively(filePath: string, visited: Set<string> = new Set<string>()): string[] {\n // Avoid processing the same file twice (handles circular dependencies)\n if (visited.has(filePath)) {\n return []\n }\n\n visited.add(filePath)\n\n // Get direct imports from this file\n const directImports = extractImportPaths(filePath)\n const allImports = [filePath, ...directImports]\n\n // Recursively process each imported file\n for (const importedFile of directImports) {\n try {\n // Only process files that exist and are not directories\n // Note: resolveJSImport already resolves directory imports to their index files\n if (fileExistsSync(importedFile) && !isDirectorySync(importedFile)) {\n const nestedImports = extractImportPathsRecursively(importedFile, visited)\n allImports.push(...nestedImports)\n }\n } catch (error) {\n // Rethrow unexpected errors after checking for expected file read errors\n if (error instanceof Error && error.message.includes('ENOENT')) {\n // Skip files that don't exist or can't be read\n continue\n }\n throw error\n }\n }\n\n // Return unique list of imports\n return [...new Set(allImports)]\n}\n\n/**\n * Extracts import paths from a JavaScript content.\n *\n * @param content - The content to extract imports from.\n * @param filePath - The path to the file to extract imports from.\n * @returns Array of absolute paths to imported files.\n */\nexport function extractJSImports(content: string, filePath: string): string[] {\n return extractJSLikeImports(content, filePath)\n}\n\nfunction extractJSLikeImports(content: string, filePath: string): string[] {\n const imports: string[] = []\n\n // Regular expressions for different import types\n const patterns = [\n // ES6 imports: import ... from './path'\n /import\\s+(?:[\\s\\S]*?)\\s+from\\s+['\"](\\.\\.?\\/[^'\"]+)['\"]/gm,\n // ES6 side-effect imports: import './path'\n /import\\s+['\"](\\.\\.?\\/[^'\"]+)['\"]/g,\n // ES6 exports: export ... from './path'\n /export\\s+(?:[\\s\\S]*?)\\s+from\\s+['\"](\\.\\.?\\/[^'\"]+)['\"]/gm,\n // Dynamic imports: import('./path')\n /import\\s*\\(\\s*['\"](\\.\\.?\\/[^'\"]+)['\"]\\s*\\)/g,\n // CommonJS requires: require('./path')\n /require\\s*\\(\\s*['\"](\\.\\.?\\/[^'\"]+)['\"]\\s*\\)/g,\n ]\n\n for (const pattern of patterns) {\n let match\n while ((match = pattern.exec(content)) !== null) {\n const importPath = match[1]\n if (importPath && importPath.startsWith('.')) {\n const resolvedPath = resolveJSImport(importPath, filePath)\n if (resolvedPath) {\n imports.push(resolvedPath)\n }\n }\n }\n }\n\n return [...new Set(imports)]\n}\n\nfunction extractRustImports(content: string, filePath: string): string[] {\n const imports: string[] = []\n\n // Basic Rust mod declarations: mod module_name;\n const modPattern = /^\\s*(?:pub\\s+)?mod\\s+([a-z_][a-z0-9_]*)\\s*;/gm\n\n let match\n while ((match = modPattern.exec(content)) !== null) {\n const modName = match[1]\n if (modName) {\n const modPath = resolveRustModule(modName, filePath)\n if (modPath) {\n imports.push(modPath)\n }\n }\n }\n\n // Handle #[path = \"...\"] attributes\n const pathPattern = /#\\[path\\s*=\\s*\"([^\"]+)\"\\]/g\n while ((match = pathPattern.exec(content)) !== null) {\n const pathValue = match[1]\n if (pathValue) {\n const resolvedPath = joinPath(dirname(filePath), pathValue)\n if (fileExistsSync(resolvedPath)) {\n imports.push(resolvedPath)\n }\n }\n }\n\n return [...new Set(imports)]\n}\n\nfunction resolveJSImport(importPath: string, fromFile: string): string | null {\n const basePath = fileExistsSync(fromFile) && isDirectorySync(fromFile) ? fromFile : dirname(fromFile)\n const resolvedPath = joinPath(basePath, importPath)\n\n // If the import path resolves to a directory, look for index files\n if (fileExistsSync(resolvedPath) && isDirectorySync(resolvedPath)) {\n const indexPaths = [\n joinPath(resolvedPath, 'index.js'),\n joinPath(resolvedPath, 'index.ts'),\n joinPath(resolvedPath, 'index.tsx'),\n joinPath(resolvedPath, 'index.jsx'),\n ]\n\n for (const indexPath of indexPaths) {\n if (fileExistsSync(indexPath) && !isDirectorySync(indexPath)) {\n return indexPath\n }\n }\n // If no index file found, don't return the directory\n return null\n }\n\n // Check for file with extensions\n const possiblePaths = [\n resolvedPath,\n `${resolvedPath}.js`,\n `${resolvedPath}.ts`,\n `${resolvedPath}.tsx`,\n `${resolvedPath}.jsx`,\n ]\n\n for (const path of possiblePaths) {\n if (fileExistsSync(path) && !isDirectorySync(path)) {\n return path\n }\n }\n\n return null\n}\n\nfunction resolveRustModule(modName: string, fromFile: string): string | null {\n const basePath = dirname(fromFile)\n const possiblePaths = [joinPath(basePath, `${modName}.rs`), joinPath(basePath, modName, 'mod.rs')]\n\n for (const path of possiblePaths) {\n if (fileExistsSync(path)) {\n return path\n }\n }\n\n return null\n}\n"]}
1
+ {"version":3,"file":"import-extractor.js","sourceRoot":"","sources":["../../../src/public/node/import-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["import {fileExistsSync, isDirectorySync} from './fs.js'\nimport {dirname, joinPath} from './path.js'\nimport {openSync, readSync, closeSync} from 'fs'\n\n// Only read the first 128KB of each file for import scanning. This covers\n// ~3,000+ lines which is more than enough to capture all static imports.\n// Generated type files (e.g. graphql-codegen) can be tens of megabytes and\n// reading them fully takes several seconds on some machines.\nconst MAX_READ_SIZE = 128 * 1024\n\n// Caches direct import results per file path to avoid redundant file reads and parsing\n// when multiple extensions import the same shared code.\nconst directImportsCache = new Map<string, string[]>()\n\n// Caches filesystem stat results to avoid redundant synchronous I/O.\n// Each stat call also triggers outputDebug overhead, so caching here\n// avoids both the kernel round-trip and the debug string construction.\nconst fileExistsCache = new Map<string, boolean>()\nconst isDirCache = new Map<string, boolean>()\n\nfunction cachedFileExists(path: string): boolean {\n const cached = fileExistsCache.get(path)\n if (cached !== undefined) return cached\n const result = fileExistsSync(path)\n fileExistsCache.set(path, result)\n return result\n}\n\nfunction cachedIsDir(path: string): boolean {\n const cached = isDirCache.get(path)\n if (cached !== undefined) return cached\n const result = isDirectorySync(path)\n isDirCache.set(path, result)\n return result\n}\n\nfunction readFileContent(filePath: string): string {\n const fd = openSync(filePath, 'r')\n try {\n const buffer = Buffer.alloc(MAX_READ_SIZE)\n const bytesRead = readSync(fd, buffer, 0, MAX_READ_SIZE, 0)\n return buffer.subarray(0, bytesRead).toString()\n } finally {\n closeSync(fd)\n }\n}\n\n/**\n * Clears all import-scanning caches (direct imports, recursive results, and filesystem stats).\n * Should be called when watched files change so that rescanning picks up updated imports.\n */\nexport function clearImportPathsCache(): void {\n directImportsCache.clear()\n fileExistsCache.clear()\n isDirCache.clear()\n}\n\n/**\n * Extracts import paths from a source file.\n * Supports JavaScript, TypeScript, and Rust files.\n * Results are cached per file path to avoid redundant I/O.\n *\n * @param filePath - Path to the file to analyze.\n * @returns Array of absolute paths to imported files.\n */\nexport function extractImportPaths(filePath: string): string[] {\n const cached = directImportsCache.get(filePath)\n if (cached) return cached\n\n const content = readFileContent(filePath)\n const ext = filePath.substring(filePath.lastIndexOf('.'))\n\n let result: string[]\n switch (ext) {\n case '.js':\n case '.mjs':\n case '.cjs':\n case '.ts':\n case '.tsx':\n case '.jsx':\n result = extractJSLikeImports(content, filePath)\n break\n case '.rs':\n result = extractRustImports(content, filePath)\n break\n default:\n result = []\n }\n\n directImportsCache.set(filePath, result)\n return result\n}\n\n/**\n * Recursively extracts import paths from a source file and all its dependencies.\n * Supports JavaScript, TypeScript, and Rust files.\n * Handles circular dependencies by tracking visited files.\n *\n * @param filePath - Path to the file to analyze.\n * @param visited - Set of already visited files to prevent infinite recursion.\n * @returns Array of absolute paths to the provided file and all imported files there (including nested imports).\n * @throws If an unexpected error occurs while processing files (not including ENOENT file not found errors).\n */\nexport function extractImportPathsRecursively(filePath: string, visited: Set<string> = new Set<string>()): string[] {\n if (visited.has(filePath)) {\n return []\n }\n\n visited.add(filePath)\n\n const directImports = extractImportPaths(filePath)\n const allImports = [filePath, ...directImports]\n\n for (const importedFile of directImports) {\n try {\n if (cachedFileExists(importedFile) && !cachedIsDir(importedFile)) {\n const nestedImports = extractImportPathsRecursively(importedFile, visited)\n allImports.push(...nestedImports)\n }\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n continue\n }\n throw error\n }\n }\n\n return [...new Set(allImports)]\n}\n\n/**\n * Returns diagnostic information about the import scanning caches.\n * Useful for debugging performance issues with --verbose.\n *\n * @returns Cache size stats for directImports, fileExists, and isDir.\n */\nexport function getImportScanningCacheStats(): {directImports: number; fileExists: number; isDir: number} {\n return {\n directImports: directImportsCache.size,\n fileExists: fileExistsCache.size,\n isDir: isDirCache.size,\n }\n}\n\n/**\n * Extracts import paths from a JavaScript content.\n *\n * @param content - The content to extract imports from.\n * @param filePath - The path to the file to extract imports from.\n * @returns Array of absolute paths to imported files.\n */\nexport function extractJSImports(content: string, filePath: string): string[] {\n return extractJSLikeImports(content, filePath)\n}\n\nfunction extractJSLikeImports(content: string, filePath: string): string[] {\n const imports: string[] = []\n\n // Regular expressions for different import types\n const patterns = [\n // ES6 imports: import ... from './path'\n /import\\s+(?:[\\s\\S]*?)\\s+from\\s+['\"](\\.\\.?\\/[^'\"]+)['\"]/gm,\n // ES6 side-effect imports: import './path'\n /import\\s+['\"](\\.\\.?\\/[^'\"]+)['\"]/g,\n // ES6 exports: export ... from './path'\n /export\\s+(?:[\\s\\S]*?)\\s+from\\s+['\"](\\.\\.?\\/[^'\"]+)['\"]/gm,\n // Dynamic imports: import('./path')\n /import\\s*\\(\\s*['\"](\\.\\.?\\/[^'\"]+)['\"]\\s*\\)/g,\n // CommonJS requires: require('./path')\n /require\\s*\\(\\s*['\"](\\.\\.?\\/[^'\"]+)['\"]\\s*\\)/g,\n ]\n\n for (const pattern of patterns) {\n let match\n while ((match = pattern.exec(content)) !== null) {\n const importPath = match[1]\n if (importPath && importPath.startsWith('.')) {\n const resolvedPath = resolveJSImport(importPath, filePath)\n if (resolvedPath) {\n imports.push(resolvedPath)\n }\n }\n }\n }\n\n return [...new Set(imports)]\n}\n\nfunction extractRustImports(content: string, filePath: string): string[] {\n const imports: string[] = []\n\n // Basic Rust mod declarations: mod module_name;\n const modPattern = /^\\s*(?:pub\\s+)?mod\\s+([a-z_][a-z0-9_]*)\\s*;/gm\n\n let match\n while ((match = modPattern.exec(content)) !== null) {\n const modName = match[1]\n if (modName) {\n const modPath = resolveRustModule(modName, filePath)\n if (modPath) {\n imports.push(modPath)\n }\n }\n }\n\n // Handle #[path = \"...\"] attributes\n const pathPattern = /#\\[path\\s*=\\s*\"([^\"]+)\"\\]/g\n while ((match = pathPattern.exec(content)) !== null) {\n const pathValue = match[1]\n if (pathValue) {\n const resolvedPath = joinPath(dirname(filePath), pathValue)\n if (cachedFileExists(resolvedPath)) {\n imports.push(resolvedPath)\n }\n }\n }\n\n return [...new Set(imports)]\n}\n\nfunction resolveJSImport(importPath: string, fromFile: string): string | null {\n const basePath = cachedFileExists(fromFile) && cachedIsDir(fromFile) ? fromFile : dirname(fromFile)\n const resolvedPath = joinPath(basePath, importPath)\n\n if (cachedFileExists(resolvedPath) && cachedIsDir(resolvedPath)) {\n const indexPaths = [\n joinPath(resolvedPath, 'index.js'),\n joinPath(resolvedPath, 'index.ts'),\n joinPath(resolvedPath, 'index.tsx'),\n joinPath(resolvedPath, 'index.jsx'),\n ]\n\n for (const indexPath of indexPaths) {\n if (cachedFileExists(indexPath) && !cachedIsDir(indexPath)) {\n return indexPath\n }\n }\n return null\n }\n\n const possiblePaths = [\n resolvedPath,\n `${resolvedPath}.js`,\n `${resolvedPath}.ts`,\n `${resolvedPath}.tsx`,\n `${resolvedPath}.jsx`,\n ]\n\n for (const path of possiblePaths) {\n if (cachedFileExists(path) && !cachedIsDir(path)) {\n return path\n }\n }\n\n return null\n}\n\nfunction resolveRustModule(modName: string, fromFile: string): string | null {\n const basePath = dirname(fromFile)\n const possiblePaths = [joinPath(basePath, `${modName}.rs`), joinPath(basePath, modName, 'mod.rs')]\n\n for (const path of possiblePaths) {\n if (cachedFileExists(path)) {\n return path\n }\n }\n\n return null\n}\n"]}
@@ -26,6 +26,14 @@ export declare function installGlobalCLIPrompt(): Promise<InstallGlobalCLIPrompt
26
26
  * Infers the package manager used by the global CLI.
27
27
  *
28
28
  * @param argv - The arguments passed to the process.
29
+ * @param env - The environment variables of the process.
29
30
  * @returns The package manager used by the global CLI.
30
31
  */
31
- export declare function inferPackageManagerForGlobalCLI(argv?: string[]): PackageManager;
32
+ export declare function inferPackageManagerForGlobalCLI(argv?: string[], env?: NodeJS.ProcessEnv): PackageManager;
33
+ /**
34
+ * Returns the project directory for the given path.
35
+ *
36
+ * @param directory - The path to search upward from.
37
+ * @returns The project root directory, or undefined if not found.
38
+ */
39
+ export declare function getProjectDir(directory: string): string | undefined;
@@ -1,10 +1,11 @@
1
1
  import { outputInfo } from './output.js';
2
- import { cwd, sniffForPath } from './path.js';
2
+ import { cwd, dirname, joinPath, sniffForPath } from './path.js';
3
3
  import { exec, terminalSupportsPrompting } from './system.js';
4
4
  import { renderSelectPrompt } from './ui.js';
5
5
  import { globalCLIVersion } from './version.js';
6
6
  import { isUnitTest } from './context/local.js';
7
- import { execaSync } from 'execa';
7
+ import { findPathUpSync, globSync } from './fs.js';
8
+ import { realpathSync } from 'fs';
8
9
  let _isGlobal;
9
10
  /**
10
11
  * Returns true if the current process is running in a global context.
@@ -19,13 +20,14 @@ export function currentProcessIsGlobal(argv = process.argv) {
19
20
  return _isGlobal;
20
21
  // Path where the current project is (app/hydrogen)
21
22
  const path = sniffForPath() ?? cwd();
22
- // Closest parent directory to contain a package.json file or node_modules directory
23
- // https://docs.npmjs.com/cli/v8/commands/npm-prefix#description
24
- const npmPrefix = execaSync('npm', ['prefix'], { cwd: path }).stdout.trim();
23
+ const projectDir = getProjectDir(path);
24
+ if (!projectDir) {
25
+ return true;
26
+ }
25
27
  // From node docs: "The second element [of the array] will be the path to the JavaScript file being executed"
26
28
  const binDir = argv[1] ?? '';
27
- // If binDir starts with npmPrefix, then we are running a local CLI
28
- const isLocal = binDir.startsWith(npmPrefix.trim());
29
+ // If binDir starts with projectDir, then we are running a local CLI
30
+ const isLocal = binDir.startsWith(projectDir.trim());
29
31
  _isGlobal = !isLocal;
30
32
  return _isGlobal;
31
33
  // eslint-disable-next-line no-catch-all/no-catch-all
@@ -68,19 +70,60 @@ export async function installGlobalCLIPrompt() {
68
70
  * Infers the package manager used by the global CLI.
69
71
  *
70
72
  * @param argv - The arguments passed to the process.
73
+ * @param env - The environment variables of the process.
71
74
  * @returns The package manager used by the global CLI.
72
75
  */
73
- export function inferPackageManagerForGlobalCLI(argv = process.argv) {
76
+ export function inferPackageManagerForGlobalCLI(argv = process.argv, env = process.env) {
74
77
  if (!currentProcessIsGlobal(argv))
75
78
  return 'unknown';
76
- // argv[1] contains the path of the executed binary
79
+ // Check for Homebrew first (most reliable via environment variable)
80
+ if (env.SHOPIFY_HOMEBREW_FORMULA) {
81
+ return 'homebrew';
82
+ }
77
83
  const processArgv = argv[1] ?? '';
78
- if (processArgv.includes('yarn'))
84
+ // Resolve symlinks to get the real path of the binary.
85
+ let realPath = processArgv.toLowerCase();
86
+ try {
87
+ realPath = realpathSync(processArgv).toLowerCase();
88
+ // eslint-disable-next-line no-catch-all/no-catch-all
89
+ }
90
+ catch (error) {
91
+ // fall back to using the original path for detection
92
+ }
93
+ if (realPath.includes('yarn'))
79
94
  return 'yarn';
80
- if (processArgv.includes('pnpm'))
95
+ if (realPath.includes('pnpm'))
81
96
  return 'pnpm';
82
- if (processArgv.includes('bun'))
97
+ if (realPath.includes('bun'))
83
98
  return 'bun';
99
+ // Check for Homebrew via Cellar path (resolved symlink)
100
+ if (realPath.includes('/cellar/'))
101
+ return 'homebrew';
84
102
  return 'npm';
85
103
  }
104
+ /**
105
+ * Returns the project directory for the given path.
106
+ *
107
+ * @param directory - The path to search upward from.
108
+ * @returns The project root directory, or undefined if not found.
109
+ */
110
+ export function getProjectDir(directory) {
111
+ const configFiles = ['shopify.app{,.*}.toml', 'hydrogen.config.js', 'hydrogen.config.ts'];
112
+ const existsConfigFile = (directory) => {
113
+ const configPaths = globSync(configFiles.map((file) => joinPath(directory, file)));
114
+ return configPaths.length > 0 ? configPaths[0] : undefined;
115
+ };
116
+ try {
117
+ const configFile = findPathUpSync(existsConfigFile, {
118
+ cwd: directory,
119
+ type: 'file',
120
+ });
121
+ if (configFile)
122
+ return dirname(configFile);
123
+ // eslint-disable-next-line no-catch-all/no-catch-all
124
+ }
125
+ catch (error) {
126
+ return undefined;
127
+ }
128
+ }
86
129
  //# sourceMappingURL=is-global.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"is-global.js","sourceRoot":"","sources":["../../../src/public/node/is-global.ts"],"names":[],"mappings":"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","sourcesContent":["import {PackageManager} from './node-package-manager.js'\nimport {outputInfo} from './output.js'\nimport {cwd, sniffForPath} from './path.js'\nimport {exec, terminalSupportsPrompting} from './system.js'\nimport {renderSelectPrompt} from './ui.js'\nimport {globalCLIVersion} from './version.js'\nimport {isUnitTest} from './context/local.js'\nimport {execaSync} from 'execa'\n\nlet _isGlobal: boolean | undefined\n\n/**\n * Returns true if the current process is running in a global context.\n *\n * @param argv - The arguments passed to the process.\n * @returns `true` if the current process is running in a global context.\n */\nexport function currentProcessIsGlobal(argv = process.argv): boolean {\n // If we are running tests, we need to disable the cache\n try {\n if (_isGlobal !== undefined && !isUnitTest()) return _isGlobal\n\n // Path where the current project is (app/hydrogen)\n const path = sniffForPath() ?? cwd()\n\n // Closest parent directory to contain a package.json file or node_modules directory\n // https://docs.npmjs.com/cli/v8/commands/npm-prefix#description\n const npmPrefix = execaSync('npm', ['prefix'], {cwd: path}).stdout.trim()\n\n // From node docs: \"The second element [of the array] will be the path to the JavaScript file being executed\"\n const binDir = argv[1] ?? ''\n\n // If binDir starts with npmPrefix, then we are running a local CLI\n const isLocal = binDir.startsWith(npmPrefix.trim())\n\n _isGlobal = !isLocal\n return _isGlobal\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return false\n }\n}\n\n/**\n * Installs the global Shopify CLI, using the provided package manager.\n *\n * @param packageManager - The package manager to use.\n */\nexport async function installGlobalShopifyCLI(packageManager: PackageManager): Promise<void> {\n const args =\n packageManager === 'yarn' ? ['global', 'add', '@shopify/cli@latest'] : ['install', '-g', '@shopify/cli@latest']\n outputInfo(`Running ${packageManager} ${args.join(' ')}...`)\n await exec(packageManager, args, {stdio: 'inherit'})\n}\n\nexport interface InstallGlobalCLIPromptResult {\n install: boolean\n alreadyInstalled: boolean\n}\n/**\n * Prompts the user to install the global CLI.\n *\n * @returns `true` if the user has installed the global CLI.\n */\nexport async function installGlobalCLIPrompt(): Promise<InstallGlobalCLIPromptResult> {\n if (!terminalSupportsPrompting()) return {install: false, alreadyInstalled: false}\n if (await globalCLIVersion()) {\n return {install: false, alreadyInstalled: true}\n }\n const result = await renderSelectPrompt({\n message: 'We recommend installing Shopify CLI globally in your system. Would you like to install it now?',\n choices: [\n {value: 'yes', label: 'Yes'},\n {value: 'no', label: 'No, just for this project'},\n ],\n })\n\n return {install: result === 'yes', alreadyInstalled: false}\n}\n\n/**\n * Infers the package manager used by the global CLI.\n *\n * @param argv - The arguments passed to the process.\n * @returns The package manager used by the global CLI.\n */\nexport function inferPackageManagerForGlobalCLI(argv = process.argv): PackageManager {\n if (!currentProcessIsGlobal(argv)) return 'unknown'\n\n // argv[1] contains the path of the executed binary\n const processArgv = argv[1] ?? ''\n if (processArgv.includes('yarn')) return 'yarn'\n if (processArgv.includes('pnpm')) return 'pnpm'\n if (processArgv.includes('bun')) return 'bun'\n return 'npm'\n}\n"]}
1
+ {"version":3,"file":"is-global.js","sourceRoot":"","sources":["../../../src/public/node/is-global.ts"],"names":[],"mappings":"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","sourcesContent":["import {PackageManager} from './node-package-manager.js'\nimport {outputInfo} from './output.js'\nimport {cwd, dirname, joinPath, sniffForPath} from './path.js'\nimport {exec, terminalSupportsPrompting} from './system.js'\nimport {renderSelectPrompt} from './ui.js'\nimport {globalCLIVersion} from './version.js'\nimport {isUnitTest} from './context/local.js'\nimport {findPathUpSync, globSync} from './fs.js'\nimport {realpathSync} from 'fs'\n\nlet _isGlobal: boolean | undefined\n\n/**\n * Returns true if the current process is running in a global context.\n *\n * @param argv - The arguments passed to the process.\n * @returns `true` if the current process is running in a global context.\n */\nexport function currentProcessIsGlobal(argv = process.argv): boolean {\n // If we are running tests, we need to disable the cache\n try {\n if (_isGlobal !== undefined && !isUnitTest()) return _isGlobal\n\n // Path where the current project is (app/hydrogen)\n const path = sniffForPath() ?? cwd()\n\n const projectDir = getProjectDir(path)\n if (!projectDir) {\n return true\n }\n\n // From node docs: \"The second element [of the array] will be the path to the JavaScript file being executed\"\n const binDir = argv[1] ?? ''\n\n // If binDir starts with projectDir, then we are running a local CLI\n const isLocal = binDir.startsWith(projectDir.trim())\n\n _isGlobal = !isLocal\n return _isGlobal\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return false\n }\n}\n\n/**\n * Installs the global Shopify CLI, using the provided package manager.\n *\n * @param packageManager - The package manager to use.\n */\nexport async function installGlobalShopifyCLI(packageManager: PackageManager): Promise<void> {\n const args =\n packageManager === 'yarn' ? ['global', 'add', '@shopify/cli@latest'] : ['install', '-g', '@shopify/cli@latest']\n outputInfo(`Running ${packageManager} ${args.join(' ')}...`)\n await exec(packageManager, args, {stdio: 'inherit'})\n}\n\nexport interface InstallGlobalCLIPromptResult {\n install: boolean\n alreadyInstalled: boolean\n}\n/**\n * Prompts the user to install the global CLI.\n *\n * @returns `true` if the user has installed the global CLI.\n */\nexport async function installGlobalCLIPrompt(): Promise<InstallGlobalCLIPromptResult> {\n if (!terminalSupportsPrompting()) return {install: false, alreadyInstalled: false}\n if (await globalCLIVersion()) {\n return {install: false, alreadyInstalled: true}\n }\n const result = await renderSelectPrompt({\n message: 'We recommend installing Shopify CLI globally in your system. Would you like to install it now?',\n choices: [\n {value: 'yes', label: 'Yes'},\n {value: 'no', label: 'No, just for this project'},\n ],\n })\n\n return {install: result === 'yes', alreadyInstalled: false}\n}\n\n/**\n * Infers the package manager used by the global CLI.\n *\n * @param argv - The arguments passed to the process.\n * @param env - The environment variables of the process.\n * @returns The package manager used by the global CLI.\n */\nexport function inferPackageManagerForGlobalCLI(argv = process.argv, env = process.env): PackageManager {\n if (!currentProcessIsGlobal(argv)) return 'unknown'\n\n // Check for Homebrew first (most reliable via environment variable)\n if (env.SHOPIFY_HOMEBREW_FORMULA) {\n return 'homebrew'\n }\n\n const processArgv = argv[1] ?? ''\n\n // Resolve symlinks to get the real path of the binary.\n let realPath = processArgv.toLowerCase()\n try {\n realPath = realpathSync(processArgv).toLowerCase()\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n // fall back to using the original path for detection\n }\n\n if (realPath.includes('yarn')) return 'yarn'\n if (realPath.includes('pnpm')) return 'pnpm'\n if (realPath.includes('bun')) return 'bun'\n\n // Check for Homebrew via Cellar path (resolved symlink)\n if (realPath.includes('/cellar/')) return 'homebrew'\n\n return 'npm'\n}\n\n/**\n * Returns the project directory for the given path.\n *\n * @param directory - The path to search upward from.\n * @returns The project root directory, or undefined if not found.\n */\nexport function getProjectDir(directory: string): string | undefined {\n const configFiles = ['shopify.app{,.*}.toml', 'hydrogen.config.js', 'hydrogen.config.ts']\n const existsConfigFile = (directory: string) => {\n const configPaths = globSync(configFiles.map((file) => joinPath(directory, file)))\n return configPaths.length > 0 ? configPaths[0] : undefined\n }\n try {\n const configFile = findPathUpSync(existsConfigFile, {\n cwd: directory,\n type: 'file',\n })\n if (configFile) return dirname(configFile)\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return undefined\n }\n}\n"]}
@@ -7,7 +7,7 @@ import { lookup, mimes } from 'mrmime';
7
7
  * @returns The mime type.
8
8
  */
9
9
  export function lookupMimeType(fileName) {
10
- return lookup(fileName) || 'application/octet-stream';
10
+ return lookup(fileName) ?? 'application/octet-stream';
11
11
  }
12
12
  /**
13
13
  * Adds MIME type(s) to the dictionary.
@@ -1 +1 @@
1
- {"version":3,"file":"mimes.js","sourceRoot":"","sources":["../../../src/public/node/mimes.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAC,MAAM,EAAE,KAAK,EAAC,MAAM,QAAQ,CAAA;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,0BAA0B,CAAA;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgC;IAC3D,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,EAAE;QACzD,KAAK,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["// import * as mimeTypes from 'mrmime'\nimport {lookup, mimes} from 'mrmime'\n\n/**\n * Returns the MIME type for a filename.\n *\n * @param fileName - Filename.\n * @returns The mime type.\n */\nexport function lookupMimeType(fileName: string): string {\n return lookup(fileName) || 'application/octet-stream'\n}\n\n/**\n * Adds MIME type(s) to the dictionary.\n *\n * @param newTypes - Object of key-values where key is extension and value is mime type.\n */\nexport function setMimeTypes(newTypes: Record<string, string>): void {\n Object.entries(newTypes).forEach(([extension, mimeType]) => {\n mimes[extension] = mimeType\n })\n}\n"]}
1
+ {"version":3,"file":"mimes.js","sourceRoot":"","sources":["../../../src/public/node/mimes.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAC,MAAM,EAAE,KAAK,EAAC,MAAM,QAAQ,CAAA;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,0BAA0B,CAAA;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgC;IAC3D,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,EAAE;QACzD,KAAK,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["// import * as mimeTypes from 'mrmime'\nimport {lookup, mimes} from 'mrmime'\n\n/**\n * Returns the MIME type for a filename.\n *\n * @param fileName - Filename.\n * @returns The mime type.\n */\nexport function lookupMimeType(fileName: string): string {\n return lookup(fileName) ?? 'application/octet-stream'\n}\n\n/**\n * Adds MIME type(s) to the dictionary.\n *\n * @param newTypes - Object of key-values where key is extension and value is mime type.\n */\nexport function setMimeTypes(newTypes: Record<string, string>): void {\n Object.entries(newTypes).forEach(([extension, mimeType]) => {\n mimes[extension] = mimeType\n })\n}\n"]}
@@ -25,7 +25,7 @@ export type DependencyType = 'dev' | 'prod' | 'peer';
25
25
  /**
26
26
  * A union that represents the package managers available.
27
27
  */
28
- export declare const packageManager: readonly ["yarn", "npm", "pnpm", "bun", "unknown"];
28
+ export declare const packageManager: readonly ["yarn", "npm", "pnpm", "bun", "homebrew", "unknown"];
29
29
  export type PackageManager = (typeof packageManager)[number];
30
30
  /**
31
31
  * Returns an abort error that's thrown when the package manager can't be determined.
@@ -27,12 +27,13 @@ export const lockfilesByManager = {
27
27
  npm: npmLockfile,
28
28
  pnpm: pnpmLockfile,
29
29
  bun: bunLockfile,
30
+ homebrew: undefined,
30
31
  unknown: undefined,
31
32
  };
32
33
  /**
33
34
  * A union that represents the package managers available.
34
35
  */
35
- export const packageManager = ['yarn', 'npm', 'pnpm', 'bun', 'unknown'];
36
+ export const packageManager = ['yarn', 'npm', 'pnpm', 'bun', 'homebrew', 'unknown'];
36
37
  /**
37
38
  * Returns an abort error that's thrown when the package manager can't be determined.
38
39
  * @returns An abort error.
@@ -325,6 +326,8 @@ export async function addNPMDependencies(dependencies, options) {
325
326
  await installDependencies(options, argumentsToAddDependenciesWithBun(dependenciesWithVersion, options.type));
326
327
  await installDependencies(options, ['install']);
327
328
  break;
329
+ case 'homebrew':
330
+ throw new AbortError("Homebrew can't be used to install project dependencies. Use npm, yarn, pnpm, or bun.");
328
331
  case 'unknown':
329
332
  throw new UnknownPackageManagerError();
330
333
  }