@shopify/cli-hydrogen 5.0.1 → 5.1.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 (240) hide show
  1. package/dist/commands/hydrogen/build.js +38 -8
  2. package/dist/commands/hydrogen/codegen-unstable.js +13 -24
  3. package/dist/commands/hydrogen/dev.js +61 -41
  4. package/dist/commands/hydrogen/env/list.js +25 -24
  5. package/dist/commands/hydrogen/env/list.test.js +46 -43
  6. package/dist/commands/hydrogen/env/pull.js +53 -25
  7. package/dist/commands/hydrogen/env/pull.test.js +123 -42
  8. package/dist/commands/hydrogen/generate/route.js +31 -132
  9. package/dist/commands/hydrogen/generate/route.test.js +34 -126
  10. package/dist/commands/hydrogen/init.js +46 -127
  11. package/dist/commands/hydrogen/init.test.js +352 -100
  12. package/dist/commands/hydrogen/link.js +101 -43
  13. package/dist/commands/hydrogen/link.test.js +108 -74
  14. package/dist/commands/hydrogen/list.js +22 -12
  15. package/dist/commands/hydrogen/list.test.js +51 -48
  16. package/dist/commands/hydrogen/login.js +31 -0
  17. package/dist/commands/hydrogen/logout.js +21 -0
  18. package/dist/commands/hydrogen/preview.js +2 -1
  19. package/dist/commands/hydrogen/setup/css.js +79 -0
  20. package/dist/commands/hydrogen/setup/markets.js +53 -0
  21. package/dist/commands/hydrogen/setup.js +133 -0
  22. package/dist/commands/hydrogen/shortcut.js +2 -45
  23. package/dist/commands/hydrogen/shortcut.test.js +10 -37
  24. package/dist/generator-templates/assets/css-modules/package.json +6 -0
  25. package/dist/generator-templates/assets/postcss/package.json +10 -0
  26. package/dist/generator-templates/assets/postcss/postcss.config.js +8 -0
  27. package/dist/generator-templates/assets/tailwind/package.json +13 -0
  28. package/dist/generator-templates/assets/tailwind/postcss.config.js +10 -0
  29. package/dist/generator-templates/assets/tailwind/tailwind.config.js +8 -0
  30. package/dist/generator-templates/assets/tailwind/tailwind.css +3 -0
  31. package/dist/generator-templates/assets/vanilla-extract/package.json +9 -0
  32. package/dist/generator-templates/starter/.eslintignore +5 -0
  33. package/dist/generator-templates/starter/.eslintrc.js +18 -0
  34. package/dist/generator-templates/starter/.graphqlrc.yml +1 -0
  35. package/dist/generator-templates/starter/README.md +40 -0
  36. package/dist/generator-templates/starter/app/components/Aside.tsx +47 -0
  37. package/dist/generator-templates/starter/app/components/Cart.tsx +340 -0
  38. package/dist/generator-templates/starter/app/components/Footer.tsx +99 -0
  39. package/dist/generator-templates/starter/app/components/Header.tsx +178 -0
  40. package/dist/generator-templates/starter/app/components/Layout.tsx +95 -0
  41. package/dist/generator-templates/starter/app/components/Search.tsx +480 -0
  42. package/dist/generator-templates/starter/app/entry.client.tsx +12 -0
  43. package/dist/generator-templates/starter/app/entry.server.tsx +33 -0
  44. package/dist/generator-templates/starter/app/root.tsx +270 -0
  45. package/dist/generator-templates/starter/app/routes/$.tsx +7 -0
  46. package/dist/generator-templates/{routes → starter/app/routes}/[robots.txt].tsx +47 -69
  47. package/dist/generator-templates/starter/app/routes/[sitemap.xml].tsx +174 -0
  48. package/dist/generator-templates/starter/app/routes/_index.tsx +145 -0
  49. package/dist/generator-templates/starter/app/routes/account.$.tsx +9 -0
  50. package/dist/generator-templates/starter/app/routes/account.addresses.tsx +563 -0
  51. package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +309 -0
  52. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +196 -0
  53. package/dist/generator-templates/starter/app/routes/account.profile.tsx +289 -0
  54. package/dist/generator-templates/starter/app/routes/account.tsx +203 -0
  55. package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +157 -0
  56. package/dist/generator-templates/starter/app/routes/account_.login.tsx +143 -0
  57. package/dist/generator-templates/starter/app/routes/account_.logout.tsx +33 -0
  58. package/dist/generator-templates/starter/app/routes/account_.recover.tsx +124 -0
  59. package/dist/generator-templates/starter/app/routes/account_.register.tsx +207 -0
  60. package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +136 -0
  61. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +342 -0
  62. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +88 -0
  63. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +162 -0
  64. package/dist/generator-templates/starter/app/routes/blogs._index.tsx +94 -0
  65. package/dist/generator-templates/starter/app/routes/cart.tsx +104 -0
  66. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +184 -0
  67. package/dist/generator-templates/starter/app/routes/collections._index.tsx +120 -0
  68. package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +57 -0
  69. package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +94 -0
  70. package/dist/generator-templates/starter/app/routes/policies._index.tsx +63 -0
  71. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +418 -0
  72. package/dist/generator-templates/starter/app/routes/search.tsx +168 -0
  73. package/dist/generator-templates/starter/app/styles/app.css +473 -0
  74. package/dist/generator-templates/starter/app/styles/reset.css +129 -0
  75. package/dist/generator-templates/starter/app/utils.ts +46 -0
  76. package/dist/generator-templates/starter/package.json +43 -0
  77. package/dist/generator-templates/starter/public/favicon.svg +28 -0
  78. package/dist/generator-templates/starter/remix.config.js +26 -0
  79. package/dist/generator-templates/starter/remix.env.d.ts +39 -0
  80. package/dist/generator-templates/starter/server.ts +253 -0
  81. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +1906 -0
  82. package/dist/generator-templates/starter/tsconfig.json +22 -0
  83. package/dist/lib/auth.js +123 -0
  84. package/dist/lib/auth.test.js +157 -0
  85. package/dist/lib/build.js +51 -0
  86. package/dist/lib/check-version.js +3 -3
  87. package/dist/lib/check-version.test.js +24 -0
  88. package/dist/lib/codegen.js +26 -17
  89. package/dist/lib/environment-variables.js +68 -0
  90. package/dist/lib/environment-variables.test.js +147 -0
  91. package/dist/lib/file.js +41 -0
  92. package/dist/lib/file.test.js +69 -0
  93. package/dist/lib/flags.js +39 -2
  94. package/dist/lib/format-code.js +26 -0
  95. package/dist/lib/gid.js +12 -0
  96. package/dist/lib/{graphql.test.js → gid.test.js} +1 -1
  97. package/dist/lib/graphql/admin/client.js +27 -0
  98. package/dist/lib/graphql/admin/client.test.js +51 -0
  99. package/dist/lib/graphql/admin/create-storefront.js +36 -0
  100. package/dist/lib/graphql/admin/create-storefront.test.js +64 -0
  101. package/dist/lib/graphql/admin/fetch-job.js +39 -0
  102. package/dist/lib/graphql/admin/link-storefront.js +7 -11
  103. package/dist/lib/graphql/admin/link-storefront.test.js +38 -0
  104. package/dist/lib/graphql/admin/list-environments.js +2 -2
  105. package/dist/lib/graphql/admin/list-environments.test.js +44 -0
  106. package/dist/lib/graphql/admin/list-storefronts.js +7 -11
  107. package/dist/lib/graphql/admin/list-storefronts.test.js +44 -0
  108. package/dist/lib/graphql/admin/pull-variables.js +3 -3
  109. package/dist/lib/graphql/admin/pull-variables.test.js +37 -0
  110. package/dist/lib/graphql/business-platform/user-account.js +83 -0
  111. package/dist/lib/graphql/business-platform/user-account.test.js +80 -0
  112. package/dist/lib/log.js +185 -9
  113. package/dist/lib/log.test.js +92 -0
  114. package/dist/lib/mini-oxygen.js +32 -14
  115. package/dist/lib/missing-routes.js +0 -2
  116. package/dist/lib/onboarding/common.js +456 -0
  117. package/dist/lib/onboarding/index.js +2 -0
  118. package/dist/lib/onboarding/local.js +229 -0
  119. package/dist/lib/onboarding/remote.js +89 -0
  120. package/dist/lib/remix-version-interop.js +5 -5
  121. package/dist/lib/remix-version-interop.test.js +11 -1
  122. package/dist/lib/render-errors.js +13 -11
  123. package/dist/lib/setups/css/assets.js +89 -0
  124. package/dist/lib/setups/css/css-modules.js +22 -0
  125. package/dist/lib/setups/css/index.js +44 -0
  126. package/dist/lib/setups/css/postcss.js +34 -0
  127. package/dist/lib/setups/css/replacers.js +137 -0
  128. package/dist/lib/setups/css/tailwind.js +54 -0
  129. package/dist/lib/setups/css/vanilla-extract.js +22 -0
  130. package/dist/lib/setups/i18n/domains.test.js +25 -0
  131. package/dist/lib/setups/i18n/index.js +46 -0
  132. package/dist/lib/setups/i18n/replacers.js +227 -0
  133. package/dist/lib/setups/i18n/subdomains.test.js +25 -0
  134. package/dist/lib/setups/i18n/subfolders.test.js +25 -0
  135. package/dist/lib/setups/i18n/templates/domains.js +14 -0
  136. package/dist/lib/setups/i18n/templates/domains.ts +25 -0
  137. package/dist/lib/setups/i18n/templates/subdomains.js +14 -0
  138. package/dist/lib/setups/i18n/templates/subdomains.ts +24 -0
  139. package/dist/lib/setups/i18n/templates/subfolders.js +14 -0
  140. package/dist/lib/setups/i18n/templates/subfolders.ts +28 -0
  141. package/dist/lib/setups/routes/generate.js +244 -0
  142. package/dist/lib/setups/routes/generate.test.js +313 -0
  143. package/dist/lib/shell.js +52 -5
  144. package/dist/lib/shell.test.js +42 -16
  145. package/dist/lib/shopify-config.js +23 -18
  146. package/dist/lib/shopify-config.test.js +63 -73
  147. package/dist/lib/string.js +7 -0
  148. package/dist/lib/string.test.js +16 -0
  149. package/dist/lib/template-downloader.js +9 -7
  150. package/dist/lib/transpile-ts.js +9 -29
  151. package/dist/virtual-routes/routes/index.jsx +40 -19
  152. package/oclif.manifest.json +710 -1
  153. package/package.json +16 -16
  154. package/dist/commands/hydrogen/build.d.ts +0 -23
  155. package/dist/commands/hydrogen/check.d.ts +0 -15
  156. package/dist/commands/hydrogen/codegen-unstable.d.ts +0 -15
  157. package/dist/commands/hydrogen/dev.d.ts +0 -21
  158. package/dist/commands/hydrogen/env/list.d.ts +0 -18
  159. package/dist/commands/hydrogen/env/pull.d.ts +0 -22
  160. package/dist/commands/hydrogen/g.d.ts +0 -10
  161. package/dist/commands/hydrogen/generate/route.d.ts +0 -32
  162. package/dist/commands/hydrogen/generate/route.test.d.ts +0 -1
  163. package/dist/commands/hydrogen/generate/routes.d.ts +0 -16
  164. package/dist/commands/hydrogen/init.d.ts +0 -24
  165. package/dist/commands/hydrogen/init.test.d.ts +0 -1
  166. package/dist/commands/hydrogen/link.d.ts +0 -23
  167. package/dist/commands/hydrogen/link.test.d.ts +0 -1
  168. package/dist/commands/hydrogen/list.d.ts +0 -21
  169. package/dist/commands/hydrogen/list.test.d.ts +0 -1
  170. package/dist/commands/hydrogen/preview.d.ts +0 -17
  171. package/dist/commands/hydrogen/shortcut.d.ts +0 -9
  172. package/dist/commands/hydrogen/shortcut.test.d.ts +0 -1
  173. package/dist/commands/hydrogen/unlink.d.ts +0 -16
  174. package/dist/commands/hydrogen/unlink.test.d.ts +0 -1
  175. package/dist/create-app.d.ts +0 -1
  176. package/dist/generator-templates/routes/[sitemap.xml].tsx +0 -235
  177. package/dist/generator-templates/routes/account/login.tsx +0 -103
  178. package/dist/generator-templates/routes/account/register.tsx +0 -103
  179. package/dist/generator-templates/routes/cart.tsx +0 -81
  180. package/dist/generator-templates/routes/collections/$collectionHandle.tsx +0 -104
  181. package/dist/generator-templates/routes/collections/index.tsx +0 -102
  182. package/dist/generator-templates/routes/graphiql.tsx +0 -10
  183. package/dist/generator-templates/routes/index.tsx +0 -40
  184. package/dist/generator-templates/routes/pages/$pageHandle.tsx +0 -112
  185. package/dist/generator-templates/routes/policies/$policyHandle.tsx +0 -140
  186. package/dist/generator-templates/routes/policies/index.tsx +0 -117
  187. package/dist/generator-templates/routes/products/$productHandle.tsx +0 -92
  188. package/dist/hooks/init.d.ts +0 -5
  189. package/dist/lib/admin-session.d.ts +0 -6
  190. package/dist/lib/admin-session.js +0 -16
  191. package/dist/lib/admin-session.test.d.ts +0 -1
  192. package/dist/lib/admin-session.test.js +0 -27
  193. package/dist/lib/admin-urls.d.ts +0 -8
  194. package/dist/lib/check-lockfile.d.ts +0 -3
  195. package/dist/lib/check-lockfile.test.d.ts +0 -1
  196. package/dist/lib/check-version.d.ts +0 -16
  197. package/dist/lib/check-version.test.d.ts +0 -1
  198. package/dist/lib/codegen.d.ts +0 -26
  199. package/dist/lib/combined-environment-variables.d.ts +0 -8
  200. package/dist/lib/combined-environment-variables.js +0 -57
  201. package/dist/lib/combined-environment-variables.test.d.ts +0 -1
  202. package/dist/lib/combined-environment-variables.test.js +0 -111
  203. package/dist/lib/config.d.ts +0 -20
  204. package/dist/lib/flags.d.ts +0 -27
  205. package/dist/lib/flags.test.d.ts +0 -1
  206. package/dist/lib/graphql/admin/link-storefront.d.ts +0 -14
  207. package/dist/lib/graphql/admin/list-environments.d.ts +0 -21
  208. package/dist/lib/graphql/admin/list-storefronts.d.ts +0 -25
  209. package/dist/lib/graphql/admin/pull-variables.d.ts +0 -21
  210. package/dist/lib/graphql.d.ts +0 -21
  211. package/dist/lib/graphql.js +0 -18
  212. package/dist/lib/graphql.test.d.ts +0 -1
  213. package/dist/lib/log.d.ts +0 -6
  214. package/dist/lib/mini-oxygen.d.ts +0 -14
  215. package/dist/lib/missing-routes.d.ts +0 -8
  216. package/dist/lib/missing-routes.test.d.ts +0 -1
  217. package/dist/lib/missing-storefronts.d.ts +0 -5
  218. package/dist/lib/missing-storefronts.js +0 -18
  219. package/dist/lib/process.d.ts +0 -6
  220. package/dist/lib/pull-environment-variables.d.ts +0 -20
  221. package/dist/lib/pull-environment-variables.js +0 -57
  222. package/dist/lib/pull-environment-variables.test.d.ts +0 -1
  223. package/dist/lib/pull-environment-variables.test.js +0 -174
  224. package/dist/lib/remix-version-interop.d.ts +0 -11
  225. package/dist/lib/remix-version-interop.test.d.ts +0 -1
  226. package/dist/lib/render-errors.d.ts +0 -16
  227. package/dist/lib/shell.d.ts +0 -11
  228. package/dist/lib/shell.test.d.ts +0 -1
  229. package/dist/lib/shop.d.ts +0 -7
  230. package/dist/lib/shop.js +0 -32
  231. package/dist/lib/shop.test.d.ts +0 -1
  232. package/dist/lib/shop.test.js +0 -78
  233. package/dist/lib/shopify-config.d.ts +0 -35
  234. package/dist/lib/shopify-config.test.d.ts +0 -1
  235. package/dist/lib/template-downloader.d.ts +0 -6
  236. package/dist/lib/transpile-ts.d.ts +0 -16
  237. package/dist/lib/virtual-routes.d.ts +0 -7
  238. package/dist/lib/virtual-routes.test.d.ts +0 -1
  239. /package/dist/{commands/hydrogen/env/list.test.d.ts → lib/setups/css/common.js} +0 -0
  240. /package/dist/{commands/hydrogen/env/pull.test.d.ts → lib/setups/i18n/mock-i18n-types.js} +0 -0
@@ -1,8 +1,8 @@
1
- import path from 'path';
2
1
  import { Flags } from '@oclif/core';
3
2
  import Command from '@shopify/cli-kit/node/base-command';
4
3
  import { outputInfo, outputContent, outputToken, outputWarn } from '@shopify/cli-kit/node/output';
5
- import { rmdir, fileSize, copyFile } from '@shopify/cli-kit/node/fs';
4
+ import { rmdir, fileSize, glob, removeFile, copyFile } from '@shopify/cli-kit/node/fs';
5
+ import { resolvePath, relativePath, joinPath } from '@shopify/cli-kit/node/path';
6
6
  import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
7
7
  import colors from '@shopify/cli-kit/node/colors';
8
8
  import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
@@ -10,30 +10,47 @@ import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js'
10
10
  import { checkLockfileStatus } from '../../lib/check-lockfile.js';
11
11
  import { findMissingRoutes } from '../../lib/missing-routes.js';
12
12
  import { warnOnce } from '../../lib/log.js';
13
+ import { codegen } from '../../lib/codegen.js';
13
14
 
14
15
  const LOG_WORKER_BUILT = "\u{1F4E6} Worker built";
15
16
  class Build extends Command {
16
17
  static description = "Builds a Hydrogen storefront for production.";
17
18
  static flags = {
18
19
  path: commonFlags.path,
19
- sourcemap: commonFlags.sourcemap,
20
+ sourcemap: Flags.boolean({
21
+ description: "Generate sourcemaps for the build.",
22
+ env: "SHOPIFY_HYDROGEN_FLAG_SOURCEMAP",
23
+ default: false
24
+ }),
20
25
  "disable-route-warning": Flags.boolean({
21
26
  description: "Disable warning about missing standard routes.",
22
27
  env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_ROUTE_WARNING"
23
28
  }),
29
+ ["codegen-unstable"]: Flags.boolean({
30
+ description: "Generate types for the Storefront API queries found in your project.",
31
+ required: false,
32
+ default: false
33
+ }),
34
+ ["codegen-config-path"]: commonFlags.codegenConfigPath,
24
35
  base: deprecated("--base")(),
25
36
  entry: deprecated("--entry")(),
26
37
  target: deprecated("--target")()
27
38
  };
28
39
  async run() {
29
40
  const { flags } = await this.parse(Build);
30
- const directory = flags.path ? path.resolve(flags.path) : process.cwd();
31
- await runBuild({ ...flagsToCamelObject(flags), path: directory });
41
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
42
+ await runBuild({
43
+ ...flagsToCamelObject(flags),
44
+ useCodegen: flags["codegen-unstable"],
45
+ path: directory
46
+ });
32
47
  }
33
48
  }
34
49
  async function runBuild({
35
50
  path: appPath,
36
- sourcemap = true,
51
+ useCodegen = false,
52
+ codegenConfigPath,
53
+ sourcemap = false,
37
54
  disableRouteWarning = false
38
55
  }) {
39
56
  if (!process.env.NODE_ENV) {
@@ -64,14 +81,15 @@ async function runBuild({
64
81
  }).catch((thrown) => {
65
82
  logThrown(thrown);
66
83
  process.exit(1);
67
- })
84
+ }),
85
+ useCodegen && codegen({ ...remixConfig, configFilePath: codegenConfigPath })
68
86
  ]);
69
87
  if (process.env.NODE_ENV !== "development") {
70
88
  console.timeEnd(LOG_WORKER_BUILT);
71
89
  const sizeMB = await fileSize(buildPathWorkerFile) / (1024 * 1024);
72
90
  outputInfo(
73
91
  outputContent` ${colors.dim(
74
- path.relative(root, buildPathWorkerFile)
92
+ relativePath(root, buildPathWorkerFile)
75
93
  )} ${outputToken.yellow(sizeMB.toFixed(2))} MB\n`
76
94
  );
77
95
  if (sizeMB >= 1) {
@@ -80,6 +98,18 @@ async function runBuild({
80
98
  `
81
99
  );
82
100
  }
101
+ if (sourcemap) {
102
+ if (process.env.HYDROGEN_ASSET_BASE_URL) {
103
+ const filepaths = await glob(joinPath(buildPathClient, "**/*.js.map"));
104
+ for (const filepath of filepaths) {
105
+ await removeFile(filepath);
106
+ }
107
+ } else {
108
+ outputWarn(
109
+ "\u{1F6A8} Sourcemaps are enabled in production! Use this only for testing.\n"
110
+ );
111
+ }
112
+ }
83
113
  }
84
114
  if (!disableRouteWarning) {
85
115
  const missingRoutes = findMissingRoutes(remixConfig);
@@ -1,11 +1,10 @@
1
1
  import path from 'path';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
- import { AbortError } from '@shopify/cli-kit/node/error';
4
3
  import { renderSuccess } from '@shopify/cli-kit/node/ui';
5
4
  import { Flags } from '@oclif/core';
6
5
  import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
7
6
  import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
8
- import { patchGqlPluck, generateTypes, normalizeCodegenError } from '../../lib/codegen.js';
7
+ import { codegen } from '../../lib/codegen.js';
9
8
 
10
9
  class Codegen extends Command {
11
10
  static description = "Generate types for the Storefront API queries found in your project.";
@@ -42,29 +41,19 @@ async function runCodegen({
42
41
  }) {
43
42
  const { root } = getProjectPaths(appPath);
44
43
  const remixConfig = await getRemixConfig(root);
45
- await patchGqlPluck();
46
- try {
47
- const generatedFiles = await generateTypes({
48
- ...remixConfig,
49
- configFilePath: codegenConfigPath,
50
- forceSfapiVersion,
51
- watch
44
+ console.log("");
45
+ const generatedFiles = await codegen({
46
+ ...remixConfig,
47
+ configFilePath: codegenConfigPath,
48
+ forceSfapiVersion,
49
+ watch
50
+ });
51
+ if (!watch) {
52
+ renderSuccess({
53
+ headline: "Generated types for GraphQL:",
54
+ body: generatedFiles.map((file) => `- ${file}`).join("\n")
52
55
  });
53
- if (!watch) {
54
- console.log("");
55
- renderSuccess({
56
- headline: "Generated types for GraphQL:",
57
- body: generatedFiles.map((file) => `- ${file}`).join("\n")
58
- });
59
- }
60
- } catch (error) {
61
- const { message, details } = normalizeCodegenError(
62
- error.message,
63
- remixConfig.rootDirectory
64
- );
65
- console.log("");
66
- throw new AbortError(message, details);
67
56
  }
68
57
  }
69
58
 
70
- export { Codegen as default };
59
+ export { Codegen as default, runCodegen };
@@ -1,21 +1,22 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs/promises';
3
- import { outputInfo } from '@shopify/cli-kit/node/output';
3
+ import { outputInfo, outputDebug } from '@shopify/cli-kit/node/output';
4
4
  import { fileExists } from '@shopify/cli-kit/node/fs';
5
5
  import { renderFatalError } from '@shopify/cli-kit/node/ui';
6
+ import colors from '@shopify/cli-kit/node/colors';
6
7
  import { copyPublicFiles } from './build.js';
7
8
  import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
8
- import { muteDevLogs, warnOnce } from '../../lib/log.js';
9
+ import { muteDevLogs, warnOnce, enhanceH2Logs } from '../../lib/log.js';
9
10
  import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js';
10
11
  import Command from '@shopify/cli-kit/node/base-command';
11
12
  import { Flags } from '@oclif/core';
12
13
  import { startMiniOxygen } from '../../lib/mini-oxygen.js';
13
14
  import { checkHydrogenVersion } from '../../lib/check-version.js';
14
15
  import { addVirtualRoutes } from '../../lib/virtual-routes.js';
15
- import { combinedEnvironmentVariables } from '../../lib/combined-environment-variables.js';
16
+ import { spawnCodegenProcess } from '../../lib/codegen.js';
17
+ import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
16
18
  import { getConfig } from '../../lib/shopify-config.js';
17
19
 
18
- const LOG_INITIAL_BUILD = "\n\u{1F3C1} Initial build";
19
20
  const LOG_REBUILDING = "\u{1F9F1} Rebuilding...";
20
21
  const LOG_REBUILT = "\u{1F680} Rebuilt";
21
22
  class Dev extends Command {
@@ -28,32 +29,27 @@ class Dev extends Command {
28
29
  required: false,
29
30
  default: false
30
31
  }),
31
- ["codegen-config-path"]: Flags.string({
32
- description: "Specify a path to a codegen configuration file. Defaults to `<root>/codegen.ts` if it exists.",
33
- required: false,
34
- dependsOn: ["codegen-unstable"]
35
- }),
32
+ ["codegen-config-path"]: commonFlags.codegenConfigPath,
36
33
  sourcemap: commonFlags.sourcemap,
37
34
  "disable-virtual-routes": Flags.boolean({
38
35
  description: "Disable rendering fallback routes when a route file doesn't exist.",
39
36
  env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_VIRTUAL_ROUTES",
40
37
  default: false
41
38
  }),
42
- shop: commonFlags.shop,
43
39
  debug: Flags.boolean({
44
40
  description: "Attaches a Node inspector",
45
41
  env: "SHOPIFY_HYDROGEN_FLAG_DEBUG",
46
42
  default: false
47
43
  }),
48
44
  host: deprecated("--host")(),
49
- ["env-branch"]: commonFlags["env-branch"]
45
+ ["env-branch"]: commonFlags.envBranch
50
46
  };
51
47
  async run() {
52
48
  const { flags } = await this.parse(Dev);
53
49
  const directory = flags.path ? path.resolve(flags.path) : process.cwd();
54
50
  await runDev({
55
51
  ...flagsToCamelObject(flags),
56
- codegen: flags["codegen-unstable"],
52
+ useCodegen: flags["codegen-unstable"],
57
53
  path: directory
58
54
  });
59
55
  }
@@ -61,10 +57,9 @@ class Dev extends Command {
61
57
  async function runDev({
62
58
  port,
63
59
  path: appPath,
64
- codegen = false,
60
+ useCodegen = false,
65
61
  codegenConfigPath,
66
62
  disableVirtualRoutes,
67
- shop,
68
63
  envBranch,
69
64
  debug = false,
70
65
  sourcemap = true
@@ -74,51 +69,67 @@ async function runDev({
74
69
  muteDevLogs();
75
70
  if (debug)
76
71
  (await import('node:inspector')).open();
77
- console.time(LOG_INITIAL_BUILD);
78
72
  const { root, publicPath, buildPathClient, buildPathWorkerFile } = getProjectPaths(appPath);
79
73
  const checkingHydrogenVersion = checkHydrogenVersion(root);
80
74
  const copyingFiles = copyPublicFiles(publicPath, buildPathClient);
81
75
  const reloadConfig = async () => {
82
76
  const config = await getRemixConfig(root);
83
- return disableVirtualRoutes ? config : addVirtualRoutes(config);
77
+ return disableVirtualRoutes ? config : addVirtualRoutes(config).catch((error) => {
78
+ outputDebug(
79
+ "Could not add virtual routes: " + (error?.stack ?? error?.message ?? error)
80
+ );
81
+ return config;
82
+ });
84
83
  };
85
84
  const getFilePaths = (file) => {
86
85
  const fileRelative = path.relative(root, file);
87
86
  return [fileRelative, path.resolve(root, fileRelative)];
88
87
  };
89
88
  const serverBundleExists = () => fileExists(buildPathWorkerFile);
90
- const hasLinkedStorefront = !!(await getConfig(root))?.storefront?.id;
91
- const environmentVariables = hasLinkedStorefront ? await combinedEnvironmentVariables({
92
- root,
93
- shop,
94
- envBranch
95
- }) : void 0;
96
- let isMiniOxygenStarted = false;
89
+ const { shop, storefront } = await getConfig(root);
90
+ const fetchRemote = !!shop && !!storefront?.id;
91
+ const envPromise = getAllEnvironmentVariables({ root, fetchRemote, envBranch });
92
+ const [{ watch }, { createFileWatchCache }] = await Promise.all([
93
+ import('@remix-run/dev/dist/compiler/watch.js'),
94
+ import('@remix-run/dev/dist/compiler/fileWatchCache.js')
95
+ ]);
96
+ let isInitialBuild = true;
97
+ let initialBuildDurationMs = 0;
98
+ let initialBuildStartTimeMs = Date.now();
99
+ let miniOxygen;
97
100
  async function safeStartMiniOxygen() {
98
- if (isMiniOxygenStarted)
101
+ if (miniOxygen)
99
102
  return;
100
- await startMiniOxygen({
103
+ miniOxygen = await startMiniOxygen({
101
104
  root,
102
105
  port,
103
106
  watch: true,
104
107
  buildPathWorkerFile,
105
108
  buildPathClient,
106
- environmentVariables
109
+ env: await envPromise
107
110
  });
108
- isMiniOxygenStarted = true;
111
+ const graphiqlUrl = `${miniOxygen.listeningAt}/graphiql`;
112
+ enhanceH2Logs({ graphiqlUrl, ...remixConfig });
113
+ miniOxygen.showBanner({
114
+ appName: storefront ? colors.cyan(storefront?.title) : void 0,
115
+ headlinePrefix: initialBuildDurationMs > 0 ? `Initial build: ${initialBuildDurationMs}ms
116
+ ` : "",
117
+ extraLines: [colors.dim(`
118
+ View GraphiQL API browser: ${graphiqlUrl}`)]
119
+ });
120
+ if (useCodegen) {
121
+ spawnCodegenProcess({ ...remixConfig, configFilePath: codegenConfigPath });
122
+ }
109
123
  const showUpgrade = await checkingHydrogenVersion;
110
124
  if (showUpgrade)
111
125
  showUpgrade();
112
126
  }
113
- let isInitialBuild = true;
114
- const [{ watch }, { createFileWatchCache }] = await Promise.all([
115
- import('@remix-run/dev/dist/compiler/watch.js'),
116
- import('@remix-run/dev/dist/compiler/fileWatchCache.js')
117
- ]);
127
+ const remixConfig = await reloadConfig();
118
128
  const fileWatchCache = createFileWatchCache();
129
+ let skipRebuildLogs = false;
119
130
  await watch(
120
131
  {
121
- config: await reloadConfig(),
132
+ config: remixConfig,
122
133
  options: {
123
134
  mode: process.env.NODE_ENV,
124
135
  onWarning: warnOnce,
@@ -129,24 +140,23 @@ async function runDev({
129
140
  {
130
141
  reloadConfig,
131
142
  onBuildStart() {
132
- if (isInitialBuild) {
133
- console.time(LOG_INITIAL_BUILD);
134
- } else {
135
- console.time(LOG_REBUILT);
143
+ if (!isInitialBuild && !skipRebuildLogs) {
136
144
  outputInfo(LOG_REBUILDING);
145
+ console.time(LOG_REBUILT);
137
146
  }
138
147
  },
139
148
  async onBuildFinish() {
140
149
  if (isInitialBuild) {
141
150
  await copyingFiles;
142
- console.timeEnd(LOG_INITIAL_BUILD);
151
+ initialBuildDurationMs = Date.now() - initialBuildStartTimeMs;
143
152
  isInitialBuild = false;
144
- } else {
153
+ } else if (!skipRebuildLogs) {
154
+ skipRebuildLogs = false;
145
155
  console.timeEnd(LOG_REBUILT);
146
- if (!isMiniOxygenStarted)
156
+ if (!miniOxygen)
147
157
  console.log("");
148
158
  }
149
- if (!isMiniOxygenStarted) {
159
+ if (!miniOxygen) {
150
160
  if (!await serverBundleExists()) {
151
161
  return renderFatalError({
152
162
  name: "BuildError",
@@ -174,6 +184,16 @@ async function runDev({
174
184
  const [relative, absolute] = getFilePaths(file);
175
185
  outputInfo(`
176
186
  \u{1F4C4} File changed: ${relative}`);
187
+ if (relative.endsWith(".env")) {
188
+ skipRebuildLogs = true;
189
+ await miniOxygen.reload({
190
+ env: await getAllEnvironmentVariables({
191
+ root,
192
+ fetchRemote,
193
+ envBranch
194
+ })
195
+ });
196
+ }
177
197
  if (absolute.startsWith(publicPath)) {
178
198
  await copyPublicFiles(
179
199
  absolute,
@@ -2,53 +2,54 @@ import Command from '@shopify/cli-kit/node/base-command';
2
2
  import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
3
3
  import { pluralize } from '@shopify/cli-kit/common/string';
4
4
  import colors from '@shopify/cli-kit/node/colors';
5
- import { outputContent, outputToken, outputNewline, outputInfo } from '@shopify/cli-kit/node/output';
5
+ import { outputNewline, outputInfo, outputContent } from '@shopify/cli-kit/node/output';
6
6
  import { linkStorefront } from '../link.js';
7
7
  import { commonFlags } from '../../../lib/flags.js';
8
- import { getHydrogenShop } from '../../../lib/shop.js';
9
- import { getAdminSession } from '../../../lib/admin-session.js';
10
8
  import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
11
- import { getConfig } from '../../../lib/shopify-config.js';
12
9
  import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
10
+ import { login } from '../../../lib/auth.js';
11
+ import { getCliCommand } from '../../../lib/shell.js';
13
12
 
14
13
  class EnvList extends Command {
15
14
  static description = "List the environments on your linked Hydrogen storefront.";
16
15
  static flags = {
17
- path: commonFlags.path,
18
- shop: commonFlags.shop
16
+ path: commonFlags.path
19
17
  };
20
18
  async run() {
21
19
  const { flags } = await this.parse(EnvList);
22
- await listEnvironments(flags);
20
+ await runEnvList(flags);
23
21
  }
24
22
  }
25
- async function listEnvironments({ path, shop: flagShop }) {
26
- const shop = await getHydrogenShop({ path, shop: flagShop });
27
- const adminSession = await getAdminSession(shop);
28
- const actualPath = path ?? process.cwd();
29
- let configStorefront = (await getConfig(actualPath)).storefront;
23
+ async function runEnvList({ path: root = process.cwd() }) {
24
+ const [{ session, config }, cliCommand] = await Promise.all([
25
+ login(root),
26
+ getCliCommand()
27
+ ]);
28
+ let configStorefront = config.storefront;
30
29
  if (!configStorefront?.id) {
31
- renderMissingLink({ adminSession });
30
+ renderMissingLink({ session, cliCommand });
32
31
  const runLink = await renderConfirmationPrompt({
33
- message: outputContent`Run ${outputToken.genericShellCommand(
34
- `npx shopify hydrogen link`
35
- )}?`.value
32
+ message: ["Run", { command: `${cliCommand} link` }, "?"]
36
33
  });
37
34
  if (!runLink) {
38
35
  return;
39
36
  }
40
- await linkStorefront({ path, shop: flagShop, silent: true });
37
+ configStorefront = await linkStorefront(root, session, config, {
38
+ cliCommand
39
+ });
41
40
  }
42
- configStorefront = (await getConfig(actualPath)).storefront;
43
- if (!configStorefront) {
41
+ if (!configStorefront)
44
42
  return;
45
- }
46
- const { storefront } = await getStorefrontEnvironments(
47
- adminSession,
43
+ const storefront = await getStorefrontEnvironments(
44
+ session,
48
45
  configStorefront.id
49
46
  );
50
47
  if (!storefront) {
51
- renderMissingStorefront({ adminSession, storefront: configStorefront });
48
+ renderMissingStorefront({
49
+ session,
50
+ storefront: configStorefront,
51
+ cliCommand
52
+ });
52
53
  return;
53
54
  }
54
55
  const previewEnvironmentIndex = storefront.environments.findIndex(
@@ -91,4 +92,4 @@ const pluralizedEnvironments = ({
91
92
  );
92
93
  };
93
94
 
94
- export { EnvList as default, listEnvironments };
95
+ export { EnvList as default, runEnvList };
@@ -3,11 +3,10 @@ import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
3
3
  import { inTemporaryDirectory } from '@shopify/cli-kit/node/fs';
4
4
  import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
5
5
  import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
6
- import { getAdminSession } from '../../../lib/admin-session.js';
7
- import { getConfig } from '../../../lib/shopify-config.js';
6
+ import { login } from '../../../lib/auth.js';
8
7
  import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
9
8
  import { linkStorefront } from '../link.js';
10
- import { listEnvironments } from './list.js';
9
+ import { runEnvList } from './list.js';
11
10
 
12
11
  const SHOP = "my-shop";
13
12
  vi.mock("@shopify/cli-kit/node/ui", async () => {
@@ -18,20 +17,25 @@ vi.mock("@shopify/cli-kit/node/ui", async () => {
18
17
  };
19
18
  });
20
19
  vi.mock("../link.js");
21
- vi.mock("../../../lib/admin-session.js");
20
+ vi.mock("../../../lib/auth.js");
22
21
  vi.mock("../../../lib/shopify-config.js");
23
22
  vi.mock("../../../lib/render-errors.js");
24
- vi.mock("../../../lib/graphql/admin/list-environments.js", () => {
25
- return { getStorefrontEnvironments: vi.fn() };
26
- });
27
- vi.mock("../../../lib/shop.js", () => ({
28
- getHydrogenShop: () => SHOP
29
- }));
23
+ vi.mock("../../../lib/graphql/admin/list-environments.js");
24
+ vi.mock("../../../lib/shell.js", () => ({ getCliCommand: () => "h2" }));
30
25
  describe("listEnvironments", () => {
31
26
  const ADMIN_SESSION = {
32
27
  token: "abc123",
33
28
  storeFqdn: SHOP
34
29
  };
30
+ const SHOPIFY_CONFIG = {
31
+ shop: SHOP,
32
+ shopName: "My Shop",
33
+ email: "email",
34
+ storefront: {
35
+ id: "gid://shopify/HydrogenStorefront/1",
36
+ title: "Existing Link"
37
+ }
38
+ };
35
39
  const PRODUCTION_ENVIRONMENT = {
36
40
  id: "gid://shopify/HydrogenStorefrontEnvironment/1",
37
41
  branch: "main",
@@ -57,44 +61,39 @@ describe("listEnvironments", () => {
57
61
  url: null
58
62
  };
59
63
  beforeEach(async () => {
60
- vi.mocked(getAdminSession).mockResolvedValue(ADMIN_SESSION);
61
- vi.mocked(getConfig).mockResolvedValue({
62
- storefront: {
63
- id: "gid://shopify/HydrogenStorefront/1",
64
- title: "Existing Link"
65
- }
64
+ vi.mocked(login).mockResolvedValue({
65
+ session: ADMIN_SESSION,
66
+ config: SHOPIFY_CONFIG
66
67
  });
67
68
  vi.mocked(getStorefrontEnvironments).mockResolvedValue({
68
- storefront: {
69
- id: "gid://shopify/HydrogenStorefront/1",
70
- productionUrl: "https://example.com",
71
- environments: [
72
- PRODUCTION_ENVIRONMENT,
73
- CUSTOM_ENVIRONMENT,
74
- PREVIEW_ENVIRONMENT
75
- ]
76
- }
69
+ id: "gid://shopify/HydrogenStorefront/1",
70
+ productionUrl: "https://example.com",
71
+ environments: [
72
+ PRODUCTION_ENVIRONMENT,
73
+ CUSTOM_ENVIRONMENT,
74
+ PREVIEW_ENVIRONMENT
75
+ ]
77
76
  });
78
77
  });
79
78
  afterEach(() => {
80
79
  vi.resetAllMocks();
81
80
  mockAndCaptureOutput().clear();
82
81
  });
83
- it("makes a GraphQL call to fetch environment variables", async () => {
82
+ it("fetchs environment variables", async () => {
84
83
  await inTemporaryDirectory(async (tmpDir) => {
85
- await listEnvironments({ path: tmpDir });
84
+ await runEnvList({ path: tmpDir });
86
85
  expect(getStorefrontEnvironments).toHaveBeenCalledWith(
87
86
  ADMIN_SESSION,
88
- "gid://shopify/HydrogenStorefront/1"
87
+ SHOPIFY_CONFIG.storefront.id
89
88
  );
90
89
  });
91
90
  });
92
91
  it("lists the environments", async () => {
93
92
  await inTemporaryDirectory(async (tmpDir) => {
94
93
  const output = mockAndCaptureOutput();
95
- await listEnvironments({ path: tmpDir });
94
+ await runEnvList({ path: tmpDir });
96
95
  expect(output.info()).toMatch(
97
- /Showing 3 environments for the Hydrogen storefront Existing Link/
96
+ /Showing 3 environments for the Hydrogen storefront Existing Link/i
98
97
  );
99
98
  expect(output.info()).toMatch(/Production \(Branch: main\)/);
100
99
  expect(output.info()).toMatch(/https:\/\/example\.com/);
@@ -105,39 +104,43 @@ describe("listEnvironments", () => {
105
104
  });
106
105
  describe("when there is no linked storefront", () => {
107
106
  beforeEach(() => {
108
- vi.mocked(getConfig).mockResolvedValue({
109
- storefront: void 0
107
+ vi.mocked(login).mockResolvedValue({
108
+ session: ADMIN_SESSION,
109
+ config: {
110
+ ...SHOPIFY_CONFIG,
111
+ storefront: void 0
112
+ }
110
113
  });
111
114
  });
112
115
  it("calls renderMissingLink", async () => {
113
116
  await inTemporaryDirectory(async (tmpDir) => {
114
- await listEnvironments({ path: tmpDir });
117
+ await runEnvList({ path: tmpDir });
115
118
  expect(renderMissingLink).toHaveBeenCalledOnce();
116
119
  });
117
120
  });
118
121
  it("prompts the user to create a link", async () => {
119
122
  vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
120
123
  await inTemporaryDirectory(async (tmpDir) => {
121
- await listEnvironments({ path: tmpDir });
124
+ await runEnvList({ path: tmpDir });
122
125
  expect(renderConfirmationPrompt).toHaveBeenCalledWith({
123
- message: expect.stringMatching(/Run .*npx shopify hydrogen link.*\?/)
124
- });
125
- expect(linkStorefront).toHaveBeenCalledWith({
126
- path: tmpDir,
127
- silent: true
126
+ message: expect.arrayContaining([{ command: "h2 link" }])
128
127
  });
128
+ expect(linkStorefront).toHaveBeenCalledWith(
129
+ tmpDir,
130
+ ADMIN_SESSION,
131
+ { ...SHOPIFY_CONFIG, storefront: void 0 },
132
+ expect.anything()
133
+ );
129
134
  });
130
135
  });
131
136
  });
132
137
  describe("when there is no matching storefront in the shop", () => {
133
138
  beforeEach(() => {
134
- vi.mocked(getStorefrontEnvironments).mockResolvedValue({
135
- storefront: null
136
- });
139
+ vi.mocked(getStorefrontEnvironments).mockResolvedValue(null);
137
140
  });
138
141
  it("calls renderMissingStorefront", async () => {
139
142
  await inTemporaryDirectory(async (tmpDir) => {
140
- await listEnvironments({ path: tmpDir });
143
+ await runEnvList({ path: tmpDir });
141
144
  expect(renderMissingStorefront).toHaveBeenCalledOnce();
142
145
  });
143
146
  });