@shopify/cli-hydrogen 7.1.2 → 8.0.1

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 (145) hide show
  1. package/dist/commands/hydrogen/build-vite.js +19 -10
  2. package/dist/commands/hydrogen/build.js +10 -2
  3. package/dist/commands/hydrogen/check.js +1 -0
  4. package/dist/commands/hydrogen/codegen.js +1 -0
  5. package/dist/commands/hydrogen/customer-account/push.js +170 -0
  6. package/dist/commands/hydrogen/debug/cpu.js +10 -1
  7. package/dist/commands/hydrogen/deploy.js +110 -36
  8. package/dist/commands/hydrogen/dev-vite.js +128 -59
  9. package/dist/commands/hydrogen/dev.js +108 -51
  10. package/dist/commands/hydrogen/env/list.js +20 -28
  11. package/dist/commands/hydrogen/env/pull.js +29 -19
  12. package/dist/commands/hydrogen/env/{push__unstable.js → push.js} +34 -68
  13. package/dist/commands/hydrogen/generate/route.js +1 -0
  14. package/dist/commands/hydrogen/init.js +39 -21
  15. package/dist/commands/hydrogen/link.js +25 -6
  16. package/dist/commands/hydrogen/list.js +1 -0
  17. package/dist/commands/hydrogen/login.js +1 -0
  18. package/dist/commands/hydrogen/logout.js +1 -0
  19. package/dist/commands/hydrogen/preview.js +31 -16
  20. package/dist/commands/hydrogen/setup/css.js +8 -1
  21. package/dist/commands/hydrogen/setup/markets.js +1 -0
  22. package/dist/commands/hydrogen/setup/vite.js +244 -138
  23. package/dist/commands/hydrogen/setup.js +21 -23
  24. package/dist/commands/hydrogen/shortcut.js +10 -0
  25. package/dist/commands/hydrogen/unlink.js +1 -0
  26. package/dist/commands/hydrogen/upgrade.js +2 -1
  27. package/dist/generator-templates/assets/vite/package.json +3 -4
  28. package/dist/generator-templates/assets/vite/vite.config.js +15 -2
  29. package/dist/generator-templates/starter/CHANGELOG.md +129 -0
  30. package/dist/generator-templates/starter/README.md +3 -44
  31. package/dist/generator-templates/starter/app/components/Footer.tsx +1 -1
  32. package/dist/generator-templates/starter/app/components/Header.tsx +1 -1
  33. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -0
  34. package/dist/generator-templates/starter/app/lib/fragments.ts +2 -0
  35. package/dist/generator-templates/starter/app/lib/root-data.ts +11 -0
  36. package/dist/generator-templates/starter/app/root.tsx +4 -20
  37. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +1 -1
  38. package/dist/generator-templates/starter/app/routes/account.tsx +1 -1
  39. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +3 -3
  40. package/dist/generator-templates/starter/app/routes/cart.tsx +1 -1
  41. package/dist/generator-templates/starter/app/routes/collections.all.tsx +160 -0
  42. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +1 -2
  43. package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +6 -3
  44. package/dist/generator-templates/starter/{remix.env.d.ts → env.d.ts} +8 -2
  45. package/dist/generator-templates/starter/package.json +14 -9
  46. package/dist/generator-templates/starter/server.ts +2 -1
  47. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +59 -3
  48. package/dist/generator-templates/starter/vite.config.ts +26 -0
  49. package/dist/{commands/hydrogen/init.d.ts → init.d.ts} +11 -4
  50. package/dist/lib/check-lockfile.js +12 -18
  51. package/dist/lib/codegen.js +37 -13
  52. package/dist/lib/common.js +50 -0
  53. package/dist/lib/cpu-profiler.js +4 -1
  54. package/dist/lib/dev-shared.js +99 -0
  55. package/dist/lib/environment-variables.js +51 -30
  56. package/dist/lib/file.js +8 -1
  57. package/dist/lib/flags.js +37 -26
  58. package/dist/lib/get-oxygen-deployment-data.js +10 -17
  59. package/dist/lib/graphql/admin/customer-application-update.js +29 -0
  60. package/dist/lib/graphql/admin/get-oxygen-data.js +1 -0
  61. package/dist/lib/graphql/admin/list-environments.js +1 -0
  62. package/dist/lib/graphql/admin/pull-variables.js +4 -4
  63. package/dist/lib/graphql/admin/test-helper.js +37 -0
  64. package/dist/lib/log.js +86 -13
  65. package/dist/lib/mini-oxygen/common.js +19 -33
  66. package/dist/lib/mini-oxygen/index.js +6 -2
  67. package/dist/lib/mini-oxygen/node.js +43 -31
  68. package/dist/lib/mini-oxygen/workerd.js +72 -165
  69. package/dist/lib/missing-routes.js +1 -1
  70. package/dist/lib/onboarding/common.js +85 -70
  71. package/dist/lib/onboarding/local.js +19 -9
  72. package/dist/lib/onboarding/remote.js +35 -30
  73. package/dist/lib/package-managers.js +24 -0
  74. package/dist/lib/remix-config.js +17 -1
  75. package/dist/lib/render-errors.js +17 -10
  76. package/dist/lib/request-events.js +6 -1
  77. package/dist/lib/setups/i18n/replacers.js +9 -6
  78. package/dist/lib/setups/routes/generate.js +1 -0
  79. package/dist/lib/shell.js +2 -1
  80. package/dist/lib/shopify-config.js +19 -1
  81. package/dist/lib/template-diff.js +36 -15
  82. package/dist/lib/template-downloader.js +35 -5
  83. package/dist/lib/transpile/morph/functions.js +26 -8
  84. package/dist/lib/transpile/morph/typedefs.js +6 -4
  85. package/dist/lib/transpile/project.js +8 -4
  86. package/dist/lib/tunneling.js +44 -0
  87. package/dist/lib/verify-linked-storefront.js +24 -0
  88. package/dist/lib/virtual-routes.js +1 -1
  89. package/dist/lib/vite-config.js +39 -9
  90. package/oclif.manifest.json +704 -508
  91. package/package.json +32 -24
  92. package/dist/commands/hydrogen/deploy.test.js +0 -553
  93. package/dist/commands/hydrogen/env/list.test.js +0 -148
  94. package/dist/commands/hydrogen/env/pull.test.js +0 -207
  95. package/dist/commands/hydrogen/env/push__unstable.test.js +0 -383
  96. package/dist/commands/hydrogen/generate/route.test.js +0 -43
  97. package/dist/commands/hydrogen/init.test.js +0 -641
  98. package/dist/commands/hydrogen/link.test.js +0 -187
  99. package/dist/commands/hydrogen/list.test.js +0 -111
  100. package/dist/commands/hydrogen/setup.test.js +0 -61
  101. package/dist/commands/hydrogen/shortcut.test.js +0 -30
  102. package/dist/commands/hydrogen/unlink.test.js +0 -36
  103. package/dist/commands/hydrogen/upgrade.test.js +0 -786
  104. package/dist/generator-templates/starter/remix.config.js +0 -24
  105. package/dist/lib/auth.test.js +0 -157
  106. package/dist/lib/check-lockfile.test.js +0 -81
  107. package/dist/lib/check-version.test.js +0 -86
  108. package/dist/lib/environment-variables.test.js +0 -149
  109. package/dist/lib/file.test.js +0 -68
  110. package/dist/lib/flags.test.js +0 -43
  111. package/dist/lib/get-oxygen-deployment-data.test.js +0 -120
  112. package/dist/lib/gid.test.js +0 -15
  113. package/dist/lib/graphql/admin/client.test.js +0 -76
  114. package/dist/lib/graphql/admin/create-storefront.test.js +0 -64
  115. package/dist/lib/graphql/admin/link-storefront.test.js +0 -38
  116. package/dist/lib/graphql/admin/list-environments.test.js +0 -44
  117. package/dist/lib/graphql/admin/list-storefronts.test.js +0 -44
  118. package/dist/lib/graphql/admin/pull-variables.test.js +0 -43
  119. package/dist/lib/graphql/business-platform/user-account.test.js +0 -80
  120. package/dist/lib/log.test.js +0 -92
  121. package/dist/lib/mini-oxygen/assets.js +0 -134
  122. package/dist/lib/mini-oxygen/mini-oxygen.test.js +0 -214
  123. package/dist/lib/mini-oxygen/workerd-inspector-logs.js +0 -227
  124. package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +0 -200
  125. package/dist/lib/mini-oxygen/workerd-inspector.js +0 -219
  126. package/dist/lib/missing-routes.test.js +0 -45
  127. package/dist/lib/remix-version-check.test.js +0 -39
  128. package/dist/lib/remix-version-interop.test.js +0 -13
  129. package/dist/lib/setups/i18n/domains.test.js +0 -39
  130. package/dist/lib/setups/i18n/replacers.test.js +0 -261
  131. package/dist/lib/setups/i18n/subdomains.test.js +0 -39
  132. package/dist/lib/setups/i18n/subfolders.test.js +0 -39
  133. package/dist/lib/setups/routes/generate.test.js +0 -296
  134. package/dist/lib/shell.test.js +0 -111
  135. package/dist/lib/shopify-config.test.js +0 -199
  136. package/dist/lib/string.test.js +0 -16
  137. package/dist/lib/virtual-routes.test.js +0 -49
  138. package/dist/lib/vite/hydrogen-middleware.js +0 -82
  139. package/dist/lib/vite/mini-oxygen.js +0 -152
  140. package/dist/lib/vite/plugins.d.ts +0 -27
  141. package/dist/lib/vite/plugins.js +0 -139
  142. package/dist/lib/vite/shared.js +0 -10
  143. package/dist/lib/vite/utils.js +0 -55
  144. package/dist/lib/vite/worker-entry.js +0 -1518
  145. /package/dist/generator-templates/starter/{.eslintrc.js → .eslintrc.cjs} +0 -0
@@ -1,5 +1,6 @@
1
+ // @ts-ignore
1
2
  // Virtual entry point for the app
2
- import * as remixBuild from '@remix-run/dev/server-build';
3
+ import * as remixBuild from 'virtual:remix/server-build';
3
4
  import {
4
5
  cartGetIdDefault,
5
6
  cartSetIdDefault,
@@ -31,7 +31,7 @@ export type CartLineFragment = Pick<
31
31
  image?: StorefrontAPI.Maybe<
32
32
  Pick<StorefrontAPI.Image, 'id' | 'url' | 'altText' | 'width' | 'height'>
33
33
  >;
34
- product: Pick<StorefrontAPI.Product, 'handle' | 'title' | 'id'>;
34
+ product: Pick<StorefrontAPI.Product, 'handle' | 'title' | 'id' | 'vendor'>;
35
35
  selectedOptions: Array<
36
36
  Pick<StorefrontAPI.SelectedOption, 'name' | 'value'>
37
37
  >;
@@ -40,7 +40,7 @@ export type CartLineFragment = Pick<
40
40
 
41
41
  export type CartApiQueryFragment = Pick<
42
42
  StorefrontAPI.Cart,
43
- 'id' | 'checkoutUrl' | 'totalQuantity' | 'note'
43
+ 'updatedAt' | 'id' | 'checkoutUrl' | 'totalQuantity' | 'note'
44
44
  > & {
45
45
  buyerIdentity: Pick<
46
46
  StorefrontAPI.CartBuyerIdentity,
@@ -81,7 +81,10 @@ export type CartApiQueryFragment = Pick<
81
81
  'id' | 'url' | 'altText' | 'width' | 'height'
82
82
  >
83
83
  >;
84
- product: Pick<StorefrontAPI.Product, 'handle' | 'title' | 'id'>;
84
+ product: Pick<
85
+ StorefrontAPI.Product,
86
+ 'handle' | 'title' | 'id' | 'vendor'
87
+ >;
85
88
  selectedOptions: Array<
86
89
  Pick<StorefrontAPI.SelectedOption, 'name' | 'value'>
87
90
  >;
@@ -673,6 +676,55 @@ export type StoreCollectionsQuery = {
673
676
  };
674
677
  };
675
678
 
679
+ export type CatalogQueryVariables = StorefrontAPI.Exact<{
680
+ country?: StorefrontAPI.InputMaybe<StorefrontAPI.CountryCode>;
681
+ language?: StorefrontAPI.InputMaybe<StorefrontAPI.LanguageCode>;
682
+ first?: StorefrontAPI.InputMaybe<StorefrontAPI.Scalars['Int']['input']>;
683
+ last?: StorefrontAPI.InputMaybe<StorefrontAPI.Scalars['Int']['input']>;
684
+ startCursor?: StorefrontAPI.InputMaybe<
685
+ StorefrontAPI.Scalars['String']['input']
686
+ >;
687
+ endCursor?: StorefrontAPI.InputMaybe<
688
+ StorefrontAPI.Scalars['String']['input']
689
+ >;
690
+ }>;
691
+
692
+ export type CatalogQuery = {
693
+ products: {
694
+ nodes: Array<
695
+ Pick<StorefrontAPI.Product, 'id' | 'handle' | 'title'> & {
696
+ featuredImage?: StorefrontAPI.Maybe<
697
+ Pick<
698
+ StorefrontAPI.Image,
699
+ 'id' | 'altText' | 'url' | 'width' | 'height'
700
+ >
701
+ >;
702
+ priceRange: {
703
+ minVariantPrice: Pick<
704
+ StorefrontAPI.MoneyV2,
705
+ 'amount' | 'currencyCode'
706
+ >;
707
+ maxVariantPrice: Pick<
708
+ StorefrontAPI.MoneyV2,
709
+ 'amount' | 'currencyCode'
710
+ >;
711
+ };
712
+ variants: {
713
+ nodes: Array<{
714
+ selectedOptions: Array<
715
+ Pick<StorefrontAPI.SelectedOption, 'name' | 'value'>
716
+ >;
717
+ }>;
718
+ };
719
+ }
720
+ >;
721
+ pageInfo: Pick<
722
+ StorefrontAPI.PageInfo,
723
+ 'hasPreviousPage' | 'hasNextPage' | 'startCursor' | 'endCursor'
724
+ >;
725
+ };
726
+ };
727
+
676
728
  export type PageQueryVariables = StorefrontAPI.Exact<{
677
729
  language?: StorefrontAPI.InputMaybe<StorefrontAPI.LanguageCode>;
678
730
  country?: StorefrontAPI.InputMaybe<StorefrontAPI.CountryCode>;
@@ -1119,6 +1171,10 @@ interface GeneratedQueryTypes {
1119
1171
  return: StoreCollectionsQuery;
1120
1172
  variables: StoreCollectionsQueryVariables;
1121
1173
  };
1174
+ '#graphql\n query Catalog(\n $country: CountryCode\n $language: LanguageCode\n $first: Int\n $last: Int\n $startCursor: String\n $endCursor: String\n ) @inContext(country: $country, language: $language) {\n products(first: $first, last: $last, before: $startCursor, after: $endCursor) {\n nodes {\n ...ProductItem\n }\n pageInfo {\n hasPreviousPage\n hasNextPage\n startCursor\n endCursor\n }\n }\n }\n #graphql\n fragment MoneyProductItem on MoneyV2 {\n amount\n currencyCode\n }\n fragment ProductItem on Product {\n id\n handle\n title\n featuredImage {\n id\n altText\n url\n width\n height\n }\n priceRange {\n minVariantPrice {\n ...MoneyProductItem\n }\n maxVariantPrice {\n ...MoneyProductItem\n }\n }\n variants(first: 1) {\n nodes {\n selectedOptions {\n name\n value\n }\n }\n }\n }\n\n': {
1175
+ return: CatalogQuery;
1176
+ variables: CatalogQueryVariables;
1177
+ };
1122
1178
  '#graphql\n query Page(\n $language: LanguageCode,\n $country: CountryCode,\n $handle: String!\n )\n @inContext(language: $language, country: $country) {\n page(handle: $handle) {\n id\n title\n body\n seo {\n description\n title\n }\n }\n }\n': {
1123
1179
  return: PageQuery;
1124
1180
  variables: PageQueryVariables;
@@ -0,0 +1,26 @@
1
+ import {defineConfig} from 'vite';
2
+ import {hydrogen} from '@shopify/hydrogen/vite';
3
+ import {oxygen} from '@shopify/mini-oxygen/vite';
4
+ import {vitePlugin as remix} from '@remix-run/dev';
5
+ import tsconfigPaths from 'vite-tsconfig-paths';
6
+
7
+ export default defineConfig({
8
+ plugins: [
9
+ hydrogen(),
10
+ oxygen(),
11
+ remix({
12
+ presets: [hydrogen.preset()],
13
+ future: {
14
+ v3_fetcherPersist: true,
15
+ v3_relativeSplatPath: true,
16
+ v3_throwAbortReason: true,
17
+ },
18
+ }),
19
+ tsconfigPaths(),
20
+ ],
21
+ build: {
22
+ // Allow a strict Content-Security-Policy
23
+ // withtout inlining assets as base64:
24
+ assetsInlineLimit: 0,
25
+ },
26
+ });
@@ -1,5 +1,6 @@
1
1
  import * as _oclif_core_lib_interfaces_parser_js from '@oclif/core/lib/interfaces/parser.js';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
+ import { PackageManager } from '@shopify/cli-kit/node/node-package-manager';
3
4
 
4
5
  declare const GENERATOR_SETUP_ASSETS_SUB_DIRS: readonly ["tailwind", "css-modules", "vanilla-extract", "postcss", "vite"];
5
6
  type AssetDir = (typeof GENERATOR_SETUP_ASSETS_SUB_DIRS)[number];
@@ -25,6 +26,8 @@ type InitOptions = {
25
26
  shortcut?: boolean;
26
27
  installDeps?: boolean;
27
28
  git?: boolean;
29
+ quickstart?: boolean;
30
+ packageManager?: PackageManager;
28
31
  };
29
32
  declare const LANGUAGES: {
30
33
  readonly js: "JavaScript";
@@ -33,13 +36,15 @@ declare const LANGUAGES: {
33
36
  type Language = keyof typeof LANGUAGES;
34
37
 
35
38
  declare class Init extends Command {
39
+ static descriptionWithMarkdown: string;
36
40
  static description: string;
37
41
  static flags: {
38
42
  routes: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
39
43
  git: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
44
+ quickstart: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
45
+ 'package-manager': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
40
46
  shortcut: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
41
47
  markets: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
42
- styling: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
43
48
  'mock-shop': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
44
49
  'install-deps': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
45
50
  path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -49,11 +54,13 @@ declare class Init extends Command {
49
54
  };
50
55
  run(): Promise<void>;
51
56
  }
52
- declare function runInit(options?: InitOptions): Promise<{
57
+ declare function runInit({ markets, ...options }?: InitOptions & {
58
+ markets?: InitOptions['i18n'];
59
+ }): Promise<{
53
60
  language?: "js" | "ts" | undefined;
54
- packageManager: "npm" | "pnpm" | "yarn" | "bun" | "unknown";
61
+ packageManager: "npm" | "yarn" | "pnpm" | "unknown" | "bun";
55
62
  cssStrategy?: CssStrategy | undefined;
56
- cliCommand: "h2" | "pnpm shopify hydrogen" | "yarn shopify hydrogen" | "bun shopify hydrogen" | "npx shopify hydrogen";
63
+ cliCommand: "h2" | "yarn shopify hydrogen" | "pnpm shopify hydrogen" | "bun shopify hydrogen" | "npx shopify hydrogen";
57
64
  depsInstalled: boolean;
58
65
  depsError?: Error | undefined;
59
66
  i18n?: "subfolders" | "domains" | "subdomains" | undefined;
@@ -3,7 +3,7 @@ import { resolvePath } from '@shopify/cli-kit/node/path';
3
3
  import { checkIfIgnoredInGitRepository } from '@shopify/cli-kit/node/git';
4
4
  import { renderWarning } from '@shopify/cli-kit/node/ui';
5
5
  import { AbortError } from '@shopify/cli-kit/node/error';
6
- import { lockfiles } from '@shopify/cli-kit/node/node-package-manager';
6
+ import { packageManagers } from './package-managers.js';
7
7
 
8
8
  function missingLockfileWarning(shouldExit) {
9
9
  const headline = "No lockfile found";
@@ -23,15 +23,9 @@ function missingLockfileWarning(shouldExit) {
23
23
  renderWarning({ headline, body, nextSteps });
24
24
  }
25
25
  }
26
- function multipleLockfilesWarning(lockfiles2, shouldExit) {
27
- const packageManagers = {
28
- "bun.lockb": "bun",
29
- "yarn.lock": "yarn",
30
- "package-lock.json": "npm",
31
- "pnpm-lock.yaml": "pnpm"
32
- };
33
- const lockfileList = lockfiles2.map((lockfile) => {
34
- return `${lockfile} (created by ${packageManagers[lockfile]})`;
26
+ function multipleLockfilesWarning(packageManagers2, shouldExit) {
27
+ const lockfileList = packageManagers2.map(({ name, lockfile }) => {
28
+ return `${lockfile} (created by ${name})`;
35
29
  });
36
30
  const headline = "Multiple lockfiles found";
37
31
  const body = [
@@ -61,19 +55,19 @@ function lockfileIgnoredWarning(lockfile) {
61
55
  async function checkLockfileStatus(directory, shouldExit = false) {
62
56
  if (process.env.LOCAL_DEV)
63
57
  return;
64
- const availableLockfiles = [];
65
- for (const lockFileName of lockfiles) {
66
- if (await fileExists(resolvePath(directory, lockFileName))) {
67
- availableLockfiles.push(lockFileName);
58
+ const foundPackageManagers = [];
59
+ for (const packageManager of packageManagers) {
60
+ if (await fileExists(resolvePath(directory, packageManager.lockfile))) {
61
+ foundPackageManagers.push(packageManager);
68
62
  }
69
63
  }
70
- if (availableLockfiles.length === 0) {
64
+ if (foundPackageManagers.length === 0) {
71
65
  return missingLockfileWarning(shouldExit);
72
66
  }
73
- if (availableLockfiles.length > 1) {
74
- return multipleLockfilesWarning(availableLockfiles, shouldExit);
67
+ if (foundPackageManagers.length > 1) {
68
+ return multipleLockfilesWarning(foundPackageManagers, shouldExit);
75
69
  }
76
- const lockfile = availableLockfiles[0];
70
+ const lockfile = foundPackageManagers[0].lockfile;
77
71
  const ignoredLockfile = await checkIfIgnoredInGitRepository(directory, [
78
72
  lockfile
79
73
  ]).catch(() => {
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { getCodeFormatOptions, formatCode } from './format-code.js';
4
- import { renderWarning, renderFatalError } from '@shopify/cli-kit/node/ui';
4
+ import { renderWarning } from '@shopify/cli-kit/node/ui';
5
5
  import { relativePath, joinPath, resolvePath, basename } from '@shopify/cli-kit/node/path';
6
6
  import { AbortError } from '@shopify/cli-kit/node/error';
7
7
 
@@ -17,6 +17,13 @@ if (isStandaloneProcess) {
17
17
  });
18
18
  }
19
19
  function normalizeCodegenError(errorMessage, rootDirectory) {
20
+ if (errorMessage.includes("AbortError: ")) {
21
+ const parsedError = errorMessage.split("AbortError: ")[1] ?? "";
22
+ const message2 = parsedError.split("\n")[0];
23
+ const details2 = parsedError.match(/tryMessage: '(.*)',$/m)?.[1];
24
+ if (message2)
25
+ return { message: message2, details: details2 };
26
+ }
20
27
  const [first = "", ...rest] = errorMessage.replaceAll("[FAILED]", "").replace(/\s{2,}/g, "\n").replace(/\n,\n/, "\n").trim().split("\n");
21
28
  const message = "[Codegen] " + first;
22
29
  let details = rest.join("\n");
@@ -48,25 +55,27 @@ function spawnCodegenProcess({
48
55
  const { message, details } = normalizeCodegenError(dataString, rootDirectory);
49
56
  if (/`punycode`/.test(message))
50
57
  return;
58
+ if (/\.body\[\d\]/.test(message))
59
+ return;
60
+ if (/console\.time(End)?\(\)/.test(message))
61
+ return;
51
62
  console.log("");
52
63
  renderWarning({ headline: message, body: details });
53
64
  });
54
65
  child.on("close", (code) => {
55
66
  if (code && code > 0) {
56
- renderFatalError({
57
- type: 0,
58
- name: "CodegenError",
59
- message: `Codegen process exited with code ${code}`,
60
- skipOclifErrorHandling: true,
61
- tryMessage: "Try restarting the dev server."
67
+ renderWarning({
68
+ headline: "Codegen process exited with code " + code,
69
+ body: "There should be more logs above."
62
70
  });
63
- process.exit(code);
64
71
  }
65
72
  });
66
73
  return child;
67
74
  }
68
75
  function codegen(options) {
69
76
  return generateTypes(options).catch((error) => {
77
+ if (error instanceof AbortError)
78
+ throw error;
70
79
  const { message, details } = normalizeCodegenError(
71
80
  error.message,
72
81
  options.rootDirectory
@@ -80,7 +89,12 @@ async function generateTypes({
80
89
  forceSfapiVersion,
81
90
  ...dirs
82
91
  }) {
83
- const { generate, loadCodegenConfig, CodegenContext } = await import('@graphql-codegen/cli');
92
+ const { generate, loadCodegenConfig, CodegenContext } = await import('@graphql-codegen/cli').catch(() => {
93
+ throw new AbortError(
94
+ "Could not load GraphQL Codegen CLI.",
95
+ "Please make sure you have `@graphql-codegen/cli` installed as a dev dependency."
96
+ );
97
+ });
84
98
  const { config: codegenConfig } = (
85
99
  // Load <root>/codegen.ts if available
86
100
  await loadCodegenConfig({
@@ -116,8 +130,18 @@ async function generateDefaultConfig({
116
130
  rootDirectory,
117
131
  appDirectory = resolvePath(rootDirectory, "app")
118
132
  }, forceSfapiVersion) {
119
- const { getSchema, preset, pluckConfig } = await import('@shopify/hydrogen-codegen');
120
- const { loadConfig } = await import('graphql-config');
133
+ const { getSchema, preset, pluckConfig } = await import('@shopify/hydrogen-codegen').catch(() => {
134
+ throw new AbortError(
135
+ "Could not load Hydrogen Codegen.",
136
+ "Please make sure you have `@shopify/hydrogen-codegen` installed as a dev dependency."
137
+ );
138
+ });
139
+ const { loadConfig } = await import('graphql-config').catch(() => {
140
+ throw new AbortError(
141
+ "Could not load GraphQL Config.",
142
+ "Please make sure you have `graphql-config` installed as a dev dependency."
143
+ );
144
+ });
121
145
  const gqlConfig = await loadConfig({
122
146
  rootDir: rootDirectory,
123
147
  throwOnEmpty: false,
@@ -128,8 +152,8 @@ async function generateDefaultConfig({
128
152
  const sfapiProject = findGqlProject(sfapiSchema, gqlConfig);
129
153
  const defaultGlob = "*!(*.d).{ts,tsx,js,jsx}";
130
154
  const appDirRelative = relativePath(rootDirectory, appDirectory);
131
- const caapiSchema = getSchema("customer-account");
132
- const caapiProject = findGqlProject(caapiSchema, gqlConfig);
155
+ const caapiSchema = getSchema("customer-account", { throwIfMissing: false });
156
+ const caapiProject = caapiSchema ? findGqlProject(caapiSchema, gqlConfig) : void 0;
133
157
  const customerAccountAPIConfig = caapiProject?.documents ? {
134
158
  ["customer-accountapi.generated.d.ts"]: {
135
159
  preset,
@@ -0,0 +1,50 @@
1
+ import { AbortError } from '@shopify/cli-kit/node/error';
2
+ import colors from '@shopify/cli-kit/node/colors';
3
+
4
+ function orderEnvironmentsBySafety(environments) {
5
+ return [
6
+ ...environments.filter((environment) => environment.type === "PREVIEW"),
7
+ ...environments.filter((environment) => environment.type === "CUSTOM"),
8
+ ...environments.filter((environment) => environment.type === "PRODUCTION")
9
+ ];
10
+ }
11
+ function createEnvironmentCliChoiceLabel(name, handle, branch) {
12
+ const metadataStringified = Object.entries({ handle, branch }).reduce((acc, [key, val]) => {
13
+ if (val) {
14
+ acc.push(`${key}: ${val}`);
15
+ }
16
+ return acc;
17
+ }, []).join(", ");
18
+ return `${name} ${colors.dim(`(${metadataStringified})`)}`;
19
+ }
20
+ function findEnvironmentOrThrow(environments, envHandle) {
21
+ const matchedEnvironment = environments.find(
22
+ ({ handle }) => handle === envHandle
23
+ );
24
+ if (!matchedEnvironment) {
25
+ throw environmentNotFound("handle", envHandle);
26
+ }
27
+ return matchedEnvironment;
28
+ }
29
+ function findEnvironmentByBranchOrThrow(environments, branch) {
30
+ const matchedEnvironment = environments.find(({ branch: b }) => b === branch);
31
+ if (!matchedEnvironment) {
32
+ throw environmentNotFound("branch", branch);
33
+ }
34
+ return matchedEnvironment;
35
+ }
36
+ function environmentNotFound(criterion, value) {
37
+ return new AbortError(
38
+ "Environment not found",
39
+ `We could not find an environment matching the ${criterion} '${value}'.`,
40
+ [
41
+ [
42
+ "Run",
43
+ { command: "env list" },
44
+ "to view a list of available environments."
45
+ ]
46
+ ]
47
+ );
48
+ }
49
+
50
+ export { createEnvironmentCliChoiceLabel, findEnvironmentByBranchOrThrow, findEnvironmentOrThrow, orderEnvironmentsBySafety };
@@ -1,8 +1,11 @@
1
1
  import { readFile } from '@shopify/cli-kit/node/fs';
2
2
  import { Session } from 'node:inspector';
3
+ import { handleMiniOxygenImportFail } from './mini-oxygen/common.js';
3
4
 
4
5
  async function createCpuStartupProfiler() {
5
- const { createMiniOxygen } = await import('@shopify/mini-oxygen');
6
+ const { createMiniOxygen } = await import('@shopify/mini-oxygen/node').catch(
7
+ handleMiniOxygenImportFail
8
+ );
6
9
  const miniOxygen = createMiniOxygen({
7
10
  script: "export default {}",
8
11
  modules: true,
@@ -0,0 +1,99 @@
1
+ import colors from '@shopify/cli-kit/node/colors';
2
+ import { outputInfo, outputContent, outputToken } from '@shopify/cli-kit/node/output';
3
+ import { renderInfo } from '@shopify/cli-kit/node/ui';
4
+ import { AbortError } from '@shopify/cli-kit/node/error';
5
+ import { getStorefrontId, runCustomerAccountPush } from '../commands/hydrogen/customer-account/push.js';
6
+ import { getLocalVariables } from '../lib/environment-variables.js';
7
+ import { startTunnelPlugin, pollTunnelURL } from './tunneling.js';
8
+ import { getConfig } from './shopify-config.js';
9
+ import { getGraphiQLUrl } from './graphiql-url.js';
10
+
11
+ function isMockShop(envVariables) {
12
+ return envVariables.PUBLIC_STORE_DOMAIN === "mock.shop" || // We fallback to mock.shop if the env var is falsy.
13
+ // When it's undefined, it might be overwritten by remote variables.
14
+ envVariables.PUBLIC_STORE_DOMAIN === "";
15
+ }
16
+ function notifyIssueWithTunnelAndMockShop(cliCommand) {
17
+ renderInfo({
18
+ headline: "Using mock.shop with `--customer-account-push` flag is not supported",
19
+ body: "The functionalities of this flag are disabled.",
20
+ nextSteps: [
21
+ "You may continue knowing Customer Account API (/account) interactions will fail.",
22
+ [
23
+ "Or run",
24
+ { command: `${cliCommand} env pull` },
25
+ "to link to your store credentials."
26
+ ]
27
+ ]
28
+ });
29
+ }
30
+ function getDevConfigInBackground(root, customerAccountPushFlag) {
31
+ return getLocalVariables(root).then(async ({ variables: localVariables }) => {
32
+ const customerAccountPush = customerAccountPushFlag && !isMockShop(localVariables);
33
+ if (customerAccountPush) {
34
+ await getStorefrontId(root);
35
+ }
36
+ const { shop, storefront } = await getConfig(root);
37
+ const storefrontId = storefront?.id;
38
+ return {
39
+ storefrontId,
40
+ customerAccountPush,
41
+ fetchRemote: !!shop && !!storefrontId,
42
+ localVariables,
43
+ storefrontTitle: storefront?.title
44
+ };
45
+ });
46
+ }
47
+ const TUNNEL_DOMAIN = Object.freeze({
48
+ ORIGINAL: ".trycloudflare.com",
49
+ REBRANDED: ".tryhydrogen.dev"
50
+ });
51
+ async function startTunnelAndPushConfig(root, cliConfig, port, storefrontId) {
52
+ outputInfo("\nStarting tunnel...\n");
53
+ const tunnel = await startTunnelPlugin(cliConfig, port, "cloudflare");
54
+ const host = await pollTunnelURL(tunnel).then(
55
+ (host2) => (
56
+ // Replace branded tunnel domain:
57
+ host2.replace(TUNNEL_DOMAIN.ORIGINAL, TUNNEL_DOMAIN.REBRANDED)
58
+ )
59
+ );
60
+ const cleanup = await runCustomerAccountPush({
61
+ path: root,
62
+ devOrigin: host,
63
+ storefrontId
64
+ }).catch((error) => {
65
+ if (error instanceof AbortError) {
66
+ renderInfo({
67
+ headline: "Customer Account Application setup update fail.",
68
+ body: error.tryMessage || void 0,
69
+ nextSteps: error.nextSteps
70
+ });
71
+ }
72
+ });
73
+ return { host, cleanup };
74
+ }
75
+ function getDebugBannerLine(publicInspectorPort) {
76
+ const isVSCode = process.env.TERM_PROGRAM === "vscode";
77
+ const debuggingDocsLink = "https://h2o.fyi/debugging/server-code" + (isVSCode ? "#visual-studio-code" : "#step-2-attach-a-debugger");
78
+ return outputContent`Debugging enabled on port ${String(
79
+ publicInspectorPort
80
+ )}.\nAttach a ${outputToken.link(
81
+ colors.yellow(isVSCode ? "VSCode debugger" : "debugger"),
82
+ debuggingDocsLink
83
+ )} or open DevTools in http://localhost:${String(publicInspectorPort)}.`.value;
84
+ }
85
+ function getUtilityBannerlines(host) {
86
+ host = host.endsWith("/") ? host.slice(0, -1) : host;
87
+ return [
88
+ `View GraphiQL API browser:
89
+ ${getGraphiQLUrl({
90
+ host
91
+ })}`,
92
+ `View server network requests:
93
+ ${host}/subrequest-profiler`
94
+ ].map((value, index) => ({
95
+ subdued: `${index === 0 ? "" : "\n\n"}${value}`
96
+ }));
97
+ }
98
+
99
+ export { TUNNEL_DOMAIN, getDebugBannerLine, getDevConfigInBackground, getUtilityBannerlines, isMockShop, notifyIssueWithTunnelAndMockShop, startTunnelAndPushConfig };
@@ -6,6 +6,8 @@ import { readAndParseDotEnv } from '@shopify/cli-kit/node/dot-env';
6
6
  import { renderWarning } from '@shopify/cli-kit/node/ui';
7
7
  import colors from '@shopify/cli-kit/node/colors';
8
8
  import { getStorefrontEnvVariables } from './graphql/admin/pull-variables.js';
9
+ import { getStorefrontEnvironments } from './graphql/admin/list-environments.js';
10
+ import { findEnvironmentByBranchOrThrow } from './common.js';
9
11
  import { login } from './auth.js';
10
12
 
11
13
  const createEmptyRemoteVars = () => ({
@@ -14,50 +16,63 @@ const createEmptyRemoteVars = () => ({
14
16
  });
15
17
  async function getAllEnvironmentVariables({
16
18
  root,
19
+ envHandle,
17
20
  envBranch,
18
- fetchRemote = true
21
+ fetchRemote = true,
22
+ localVariables: inlineLocalVariables
19
23
  }) {
20
- const dotEnvPath = resolvePath(root, ".env");
21
24
  const [{ remoteVariables, remoteSecrets }, { variables: localVariables }] = await Promise.all([
22
25
  // Get remote vars
23
- fetchRemote ? getRemoteVariables(root, envBranch).catch((error) => {
24
- renderWarning({
25
- headline: "Failed to load environment variables from Shopify. The development server will still start, but the following error occurred:",
26
- body: [error.message, error.tryMessage, error.nextSteps].filter(Boolean).join("\n\n")
27
- });
28
- return createEmptyRemoteVars();
29
- }) : createEmptyRemoteVars(),
26
+ fetchRemote ? getRemoteVariables(root, envHandle, envBranch).catch(
27
+ (error) => {
28
+ renderWarning({
29
+ headline: "Failed to load environment variables from Shopify. The development server will still start, but the following error occurred:",
30
+ body: [error.message, error.tryMessage, error.nextSteps].filter(Boolean).join("\n\n")
31
+ });
32
+ return createEmptyRemoteVars();
33
+ }
34
+ ) : createEmptyRemoteVars(),
30
35
  // Get local vars
31
- fileExists(dotEnvPath).then(
32
- (exists) => exists ? readAndParseDotEnv(dotEnvPath) : { variables: {} }
33
- )
36
+ inlineLocalVariables ? { variables: inlineLocalVariables } : getLocalVariables(root)
34
37
  ]);
35
38
  const remoteSecretKeys = Object.keys(remoteSecrets);
36
39
  const remotePublicKeys = Object.keys(remoteVariables);
37
40
  const localKeys = Object.keys(localVariables);
38
- if (localKeys.length > 0 || remotePublicKeys.length + remoteSecretKeys.length > 0) {
39
- outputInfo("\nEnvironment variables injected into MiniOxygen:\n");
40
- outputInfo(
41
- linesToColumns([
42
- ...remotePublicKeys.filter((key) => !localKeys.includes(key)).map((key) => [key, "from Oxygen"]),
43
- ...localKeys.map((key) => [key, "from local .env"]),
44
- // Ensure secret variables always get added to the bottom of the list
45
- ...remoteSecretKeys.filter((key) => !localKeys.includes(key)).map((key) => [
46
- colors.dim(key),
47
- colors.dim("from Oxygen (Marked as secret)")
41
+ function logInjectedVariables() {
42
+ if (localKeys.length > 0 || remotePublicKeys.length + remoteSecretKeys.length > 0) {
43
+ outputInfo("\nEnvironment variables injected into MiniOxygen:\n");
44
+ outputInfo(
45
+ linesToColumns([
46
+ ...remotePublicKeys.filter((key) => !localKeys.includes(key)).map((key) => [key, "from Oxygen"]),
47
+ ...localKeys.map((key) => [key, "from local .env"]),
48
+ // Ensure secret variables always get added to the bottom of the list
49
+ ...remoteSecretKeys.filter((key) => !localKeys.includes(key)).map((key) => [
50
+ colors.dim(key),
51
+ colors.dim("from Oxygen (Marked as secret)")
52
+ ])
48
53
  ])
49
- ])
50
- );
54
+ );
55
+ }
51
56
  }
52
57
  return {
53
- ...remoteSecrets,
54
- ...remoteVariables,
55
- ...localVariables
58
+ logInjectedVariables,
59
+ remoteVariables,
60
+ remoteSecrets,
61
+ localVariables,
62
+ allVariables: {
63
+ ...remoteSecrets,
64
+ ...remoteVariables,
65
+ ...localVariables
66
+ }
56
67
  };
57
68
  }
58
- async function getRemoteVariables(root, envBranch) {
69
+ async function getRemoteVariables(root, envHandle, envBranch) {
59
70
  const { session, config } = await login(root);
60
- const envVariables = (await getStorefrontEnvVariables(session, config.storefront.id, envBranch))?.environmentVariables || [];
71
+ if (envBranch) {
72
+ const environments = (await getStorefrontEnvironments(session, config.storefront.id))?.environments || [];
73
+ envHandle = findEnvironmentByBranchOrThrow(environments, envBranch).handle;
74
+ }
75
+ const envVariables = (await getStorefrontEnvVariables(session, config.storefront.id, envHandle))?.environmentVariables || [];
61
76
  const remoteVariables = {};
62
77
  const remoteSecrets = {};
63
78
  for (const { key, value, isSecret } of envVariables) {
@@ -68,5 +83,11 @@ async function getRemoteVariables(root, envBranch) {
68
83
  }
69
84
  return { remoteVariables, remoteSecrets };
70
85
  }
86
+ async function getLocalVariables(root) {
87
+ const dotEnvPath = resolvePath(root, ".env");
88
+ return await fileExists(dotEnvPath).then(
89
+ (exists) => exists ? readAndParseDotEnv(dotEnvPath) : { variables: {} }
90
+ );
91
+ }
71
92
 
72
- export { getAllEnvironmentVariables };
93
+ export { getAllEnvironmentVariables, getLocalVariables };
package/dist/lib/file.js CHANGED
@@ -13,7 +13,14 @@ async function replaceFileContent(filepath, formatConfig, replacer) {
13
13
  }
14
14
  return writeFile(filepath, content);
15
15
  }
16
- const DEFAULT_EXTENSIONS = ["tsx", "ts", "jsx", "js", "mjs", "cjs"];
16
+ const DEFAULT_EXTENSIONS = [
17
+ "tsx",
18
+ "ts",
19
+ "jsx",
20
+ "js",
21
+ "mjs",
22
+ "cjs"
23
+ ];
17
24
  async function findFileWithExtension(directory, fileBase, extensions = DEFAULT_EXTENSIONS) {
18
25
  const dirFiles = await readdir(directory);
19
26
  if (dirFiles.includes(fileBase)) {