@shopify/cli-hydrogen 5.0.2 → 5.1.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 (249) hide show
  1. package/dist/commands/hydrogen/build.js +21 -6
  2. package/dist/commands/hydrogen/check.js +2 -2
  3. package/dist/commands/hydrogen/codegen-unstable.js +14 -25
  4. package/dist/commands/hydrogen/dev.js +55 -43
  5. package/dist/commands/hydrogen/env/list.js +25 -24
  6. package/dist/commands/hydrogen/env/list.test.js +46 -43
  7. package/dist/commands/hydrogen/env/pull.js +53 -25
  8. package/dist/commands/hydrogen/env/pull.test.js +123 -42
  9. package/dist/commands/hydrogen/generate/route.js +31 -132
  10. package/dist/commands/hydrogen/generate/route.test.js +34 -126
  11. package/dist/commands/hydrogen/init.js +46 -127
  12. package/dist/commands/hydrogen/init.test.js +352 -100
  13. package/dist/commands/hydrogen/link.js +70 -69
  14. package/dist/commands/hydrogen/link.test.js +72 -107
  15. package/dist/commands/hydrogen/list.js +22 -12
  16. package/dist/commands/hydrogen/list.test.js +51 -48
  17. package/dist/commands/hydrogen/login.js +31 -0
  18. package/dist/commands/hydrogen/logout.js +21 -0
  19. package/dist/commands/hydrogen/preview.js +1 -1
  20. package/dist/commands/hydrogen/setup/css.js +79 -0
  21. package/dist/commands/hydrogen/setup/markets.js +53 -0
  22. package/dist/commands/hydrogen/setup.js +133 -0
  23. package/dist/commands/hydrogen/shortcut.js +2 -45
  24. package/dist/commands/hydrogen/shortcut.test.js +10 -37
  25. package/dist/generator-templates/assets/css-modules/package.json +6 -0
  26. package/dist/generator-templates/assets/postcss/package.json +10 -0
  27. package/dist/generator-templates/assets/postcss/postcss.config.js +8 -0
  28. package/dist/generator-templates/assets/tailwind/package.json +13 -0
  29. package/dist/generator-templates/assets/tailwind/postcss.config.js +10 -0
  30. package/dist/generator-templates/assets/tailwind/tailwind.config.js +8 -0
  31. package/dist/generator-templates/assets/tailwind/tailwind.css +3 -0
  32. package/dist/generator-templates/assets/vanilla-extract/package.json +9 -0
  33. package/dist/generator-templates/starter/.eslintignore +5 -0
  34. package/dist/generator-templates/starter/.eslintrc.js +18 -0
  35. package/dist/generator-templates/starter/.graphqlrc.yml +1 -0
  36. package/dist/generator-templates/starter/README.md +40 -0
  37. package/dist/generator-templates/starter/app/components/Aside.tsx +47 -0
  38. package/dist/generator-templates/starter/app/components/Cart.tsx +340 -0
  39. package/dist/generator-templates/starter/app/components/Footer.tsx +99 -0
  40. package/dist/generator-templates/starter/app/components/Header.tsx +178 -0
  41. package/dist/generator-templates/starter/app/components/Layout.tsx +95 -0
  42. package/dist/generator-templates/starter/app/components/Search.tsx +480 -0
  43. package/dist/generator-templates/starter/app/entry.client.tsx +12 -0
  44. package/dist/generator-templates/starter/app/entry.server.tsx +33 -0
  45. package/dist/generator-templates/starter/app/root.tsx +264 -0
  46. package/dist/generator-templates/starter/app/routes/$.tsx +7 -0
  47. package/dist/generator-templates/{routes → starter/app/routes}/[robots.txt].tsx +47 -69
  48. package/dist/generator-templates/starter/app/routes/[sitemap.xml].tsx +174 -0
  49. package/dist/generator-templates/starter/app/routes/_index.tsx +145 -0
  50. package/dist/generator-templates/starter/app/routes/account.$.tsx +9 -0
  51. package/dist/generator-templates/starter/app/routes/account.addresses.tsx +563 -0
  52. package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +309 -0
  53. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +196 -0
  54. package/dist/generator-templates/starter/app/routes/account.profile.tsx +289 -0
  55. package/dist/generator-templates/starter/app/routes/account.tsx +203 -0
  56. package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +157 -0
  57. package/dist/generator-templates/starter/app/routes/account_.login.tsx +143 -0
  58. package/dist/generator-templates/starter/app/routes/account_.logout.tsx +33 -0
  59. package/dist/generator-templates/starter/app/routes/account_.recover.tsx +124 -0
  60. package/dist/generator-templates/starter/app/routes/account_.register.tsx +207 -0
  61. package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +136 -0
  62. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +342 -0
  63. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +88 -0
  64. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +162 -0
  65. package/dist/generator-templates/starter/app/routes/blogs._index.tsx +94 -0
  66. package/dist/generator-templates/starter/app/routes/cart.tsx +104 -0
  67. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +184 -0
  68. package/dist/generator-templates/starter/app/routes/collections._index.tsx +120 -0
  69. package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +57 -0
  70. package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +94 -0
  71. package/dist/generator-templates/starter/app/routes/policies._index.tsx +63 -0
  72. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +418 -0
  73. package/dist/generator-templates/starter/app/routes/search.tsx +168 -0
  74. package/dist/generator-templates/starter/app/styles/app.css +473 -0
  75. package/dist/generator-templates/starter/app/styles/reset.css +129 -0
  76. package/dist/generator-templates/starter/app/utils.ts +46 -0
  77. package/dist/generator-templates/starter/package.json +43 -0
  78. package/dist/generator-templates/starter/public/favicon.svg +28 -0
  79. package/dist/generator-templates/starter/remix.config.js +26 -0
  80. package/dist/generator-templates/starter/remix.env.d.ts +39 -0
  81. package/dist/generator-templates/starter/server.ts +253 -0
  82. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +1906 -0
  83. package/dist/generator-templates/starter/tsconfig.json +22 -0
  84. package/dist/lib/auth.js +123 -0
  85. package/dist/lib/auth.test.js +157 -0
  86. package/dist/lib/build.js +51 -0
  87. package/dist/lib/check-version.js +3 -3
  88. package/dist/lib/check-version.test.js +24 -0
  89. package/dist/lib/codegen.js +26 -17
  90. package/dist/lib/environment-variables.js +68 -0
  91. package/dist/lib/environment-variables.test.js +147 -0
  92. package/dist/lib/file.js +41 -0
  93. package/dist/lib/file.test.js +69 -0
  94. package/dist/lib/flags.js +39 -2
  95. package/dist/lib/format-code.js +26 -0
  96. package/dist/lib/gid.js +12 -0
  97. package/dist/lib/{graphql.test.js → gid.test.js} +1 -1
  98. package/dist/lib/graphql/admin/client.js +27 -0
  99. package/dist/lib/graphql/admin/client.test.js +51 -0
  100. package/dist/lib/graphql/admin/create-storefront.js +13 -15
  101. package/dist/lib/graphql/admin/create-storefront.test.js +64 -0
  102. package/dist/lib/graphql/admin/fetch-job.js +6 -15
  103. package/dist/lib/graphql/admin/link-storefront.js +7 -11
  104. package/dist/lib/graphql/admin/link-storefront.test.js +38 -0
  105. package/dist/lib/graphql/admin/list-environments.js +2 -2
  106. package/dist/lib/graphql/admin/list-environments.test.js +44 -0
  107. package/dist/lib/graphql/admin/list-storefronts.js +7 -11
  108. package/dist/lib/graphql/admin/list-storefronts.test.js +44 -0
  109. package/dist/lib/graphql/admin/pull-variables.js +3 -3
  110. package/dist/lib/graphql/admin/pull-variables.test.js +37 -0
  111. package/dist/lib/graphql/business-platform/user-account.js +83 -0
  112. package/dist/lib/graphql/business-platform/user-account.test.js +80 -0
  113. package/dist/lib/log.js +216 -9
  114. package/dist/lib/log.test.js +92 -0
  115. package/dist/lib/mini-oxygen.js +19 -9
  116. package/dist/lib/missing-routes.js +0 -2
  117. package/dist/lib/onboarding/common.js +456 -0
  118. package/dist/lib/onboarding/index.js +2 -0
  119. package/dist/lib/onboarding/local.js +229 -0
  120. package/dist/lib/onboarding/remote.js +89 -0
  121. package/dist/lib/remix-config.js +135 -0
  122. package/dist/lib/remix-version-check.js +51 -0
  123. package/dist/lib/remix-version-check.test.js +38 -0
  124. package/dist/lib/remix-version-interop.js +6 -6
  125. package/dist/lib/remix-version-interop.test.js +12 -2
  126. package/dist/lib/render-errors.js +13 -11
  127. package/dist/lib/setups/css/assets.js +89 -0
  128. package/dist/lib/setups/css/css-modules.js +22 -0
  129. package/dist/lib/setups/css/index.js +44 -0
  130. package/dist/lib/setups/css/postcss.js +34 -0
  131. package/dist/lib/setups/css/replacers.js +137 -0
  132. package/dist/lib/setups/css/tailwind.js +54 -0
  133. package/dist/lib/setups/css/vanilla-extract.js +22 -0
  134. package/dist/lib/setups/i18n/domains.test.js +25 -0
  135. package/dist/lib/setups/i18n/index.js +46 -0
  136. package/dist/lib/setups/i18n/replacers.js +227 -0
  137. package/dist/lib/setups/i18n/subdomains.test.js +25 -0
  138. package/dist/lib/setups/i18n/subfolders.test.js +25 -0
  139. package/dist/lib/setups/i18n/templates/domains.js +14 -0
  140. package/dist/lib/setups/i18n/templates/domains.ts +25 -0
  141. package/dist/lib/setups/i18n/templates/subdomains.js +14 -0
  142. package/dist/lib/setups/i18n/templates/subdomains.ts +24 -0
  143. package/dist/lib/setups/i18n/templates/subfolders.js +14 -0
  144. package/dist/lib/setups/i18n/templates/subfolders.ts +28 -0
  145. package/dist/lib/setups/routes/generate.js +244 -0
  146. package/dist/lib/setups/routes/generate.test.js +313 -0
  147. package/dist/lib/shell.js +52 -5
  148. package/dist/lib/shell.test.js +42 -16
  149. package/dist/lib/shopify-config.js +23 -18
  150. package/dist/lib/shopify-config.test.js +63 -73
  151. package/dist/lib/template-downloader.js +9 -7
  152. package/dist/lib/transpile-ts.js +9 -29
  153. package/dist/virtual-routes/routes/index.jsx +40 -19
  154. package/oclif.manifest.json +710 -1
  155. package/package.json +20 -21
  156. package/dist/commands/hydrogen/build.d.ts +0 -23
  157. package/dist/commands/hydrogen/check.d.ts +0 -15
  158. package/dist/commands/hydrogen/codegen-unstable.d.ts +0 -15
  159. package/dist/commands/hydrogen/dev.d.ts +0 -21
  160. package/dist/commands/hydrogen/env/list.d.ts +0 -18
  161. package/dist/commands/hydrogen/env/pull.d.ts +0 -22
  162. package/dist/commands/hydrogen/g.d.ts +0 -10
  163. package/dist/commands/hydrogen/generate/route.d.ts +0 -32
  164. package/dist/commands/hydrogen/generate/route.test.d.ts +0 -1
  165. package/dist/commands/hydrogen/generate/routes.d.ts +0 -16
  166. package/dist/commands/hydrogen/init.d.ts +0 -24
  167. package/dist/commands/hydrogen/init.test.d.ts +0 -1
  168. package/dist/commands/hydrogen/link.d.ts +0 -23
  169. package/dist/commands/hydrogen/link.test.d.ts +0 -1
  170. package/dist/commands/hydrogen/list.d.ts +0 -21
  171. package/dist/commands/hydrogen/list.test.d.ts +0 -1
  172. package/dist/commands/hydrogen/preview.d.ts +0 -17
  173. package/dist/commands/hydrogen/shortcut.d.ts +0 -9
  174. package/dist/commands/hydrogen/shortcut.test.d.ts +0 -1
  175. package/dist/commands/hydrogen/unlink.d.ts +0 -16
  176. package/dist/commands/hydrogen/unlink.test.d.ts +0 -1
  177. package/dist/create-app.d.ts +0 -1
  178. package/dist/generator-templates/routes/[sitemap.xml].tsx +0 -235
  179. package/dist/generator-templates/routes/account/login.tsx +0 -103
  180. package/dist/generator-templates/routes/account/register.tsx +0 -103
  181. package/dist/generator-templates/routes/cart.tsx +0 -81
  182. package/dist/generator-templates/routes/collections/$collectionHandle.tsx +0 -104
  183. package/dist/generator-templates/routes/collections/index.tsx +0 -102
  184. package/dist/generator-templates/routes/graphiql.tsx +0 -10
  185. package/dist/generator-templates/routes/index.tsx +0 -40
  186. package/dist/generator-templates/routes/pages/$pageHandle.tsx +0 -112
  187. package/dist/generator-templates/routes/policies/$policyHandle.tsx +0 -140
  188. package/dist/generator-templates/routes/policies/index.tsx +0 -117
  189. package/dist/generator-templates/routes/products/$productHandle.tsx +0 -92
  190. package/dist/hooks/init.d.ts +0 -5
  191. package/dist/lib/admin-session.d.ts +0 -6
  192. package/dist/lib/admin-session.js +0 -16
  193. package/dist/lib/admin-session.test.d.ts +0 -1
  194. package/dist/lib/admin-session.test.js +0 -27
  195. package/dist/lib/admin-urls.d.ts +0 -8
  196. package/dist/lib/check-lockfile.d.ts +0 -3
  197. package/dist/lib/check-lockfile.test.d.ts +0 -1
  198. package/dist/lib/check-version.d.ts +0 -16
  199. package/dist/lib/check-version.test.d.ts +0 -1
  200. package/dist/lib/codegen.d.ts +0 -26
  201. package/dist/lib/combined-environment-variables.d.ts +0 -8
  202. package/dist/lib/combined-environment-variables.js +0 -57
  203. package/dist/lib/combined-environment-variables.test.d.ts +0 -1
  204. package/dist/lib/combined-environment-variables.test.js +0 -111
  205. package/dist/lib/config.d.ts +0 -20
  206. package/dist/lib/config.js +0 -141
  207. package/dist/lib/flags.d.ts +0 -27
  208. package/dist/lib/flags.test.d.ts +0 -1
  209. package/dist/lib/graphql/admin/create-storefront.d.ts +0 -17
  210. package/dist/lib/graphql/admin/fetch-job.d.ts +0 -23
  211. package/dist/lib/graphql/admin/link-storefront.d.ts +0 -14
  212. package/dist/lib/graphql/admin/list-environments.d.ts +0 -21
  213. package/dist/lib/graphql/admin/list-storefronts.d.ts +0 -25
  214. package/dist/lib/graphql/admin/pull-variables.d.ts +0 -21
  215. package/dist/lib/graphql.d.ts +0 -21
  216. package/dist/lib/graphql.js +0 -18
  217. package/dist/lib/graphql.test.d.ts +0 -1
  218. package/dist/lib/log.d.ts +0 -6
  219. package/dist/lib/mini-oxygen.d.ts +0 -22
  220. package/dist/lib/missing-routes.d.ts +0 -8
  221. package/dist/lib/missing-routes.test.d.ts +0 -1
  222. package/dist/lib/missing-storefronts.d.ts +0 -5
  223. package/dist/lib/missing-storefronts.js +0 -18
  224. package/dist/lib/process.d.ts +0 -6
  225. package/dist/lib/pull-environment-variables.d.ts +0 -20
  226. package/dist/lib/pull-environment-variables.js +0 -57
  227. package/dist/lib/pull-environment-variables.test.d.ts +0 -1
  228. package/dist/lib/pull-environment-variables.test.js +0 -174
  229. package/dist/lib/remix-version-interop.d.ts +0 -11
  230. package/dist/lib/remix-version-interop.test.d.ts +0 -1
  231. package/dist/lib/render-errors.d.ts +0 -16
  232. package/dist/lib/shell.d.ts +0 -11
  233. package/dist/lib/shell.test.d.ts +0 -1
  234. package/dist/lib/shop.d.ts +0 -7
  235. package/dist/lib/shop.js +0 -32
  236. package/dist/lib/shop.test.d.ts +0 -1
  237. package/dist/lib/shop.test.js +0 -78
  238. package/dist/lib/shopify-config.d.ts +0 -35
  239. package/dist/lib/shopify-config.test.d.ts +0 -1
  240. package/dist/lib/string.d.ts +0 -3
  241. package/dist/lib/string.test.d.ts +0 -1
  242. package/dist/lib/template-downloader.d.ts +0 -6
  243. package/dist/lib/transpile-ts.d.ts +0 -16
  244. package/dist/lib/user-errors.d.ts +0 -9
  245. package/dist/lib/user-errors.js +0 -11
  246. package/dist/lib/virtual-routes.d.ts +0 -7
  247. package/dist/lib/virtual-routes.test.d.ts +0 -1
  248. /package/dist/{commands/hydrogen/env/list.test.d.ts → lib/setups/css/common.js} +0 -0
  249. /package/dist/{commands/hydrogen/env/pull.test.d.ts → lib/setups/i18n/mock-i18n-types.js} +0 -0
@@ -1,47 +1,78 @@
1
1
  import { diffLines } from 'diff';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
- import { renderInfo, renderConfirmationPrompt, renderWarning, renderSuccess } from '@shopify/cli-kit/node/ui';
4
- import { outputContent, outputToken } from '@shopify/cli-kit/node/output';
3
+ import { renderConfirmationPrompt, renderInfo, renderWarning, renderSuccess } from '@shopify/cli-kit/node/ui';
4
+ import { outputContent, outputToken, outputInfo } from '@shopify/cli-kit/node/output';
5
5
  import { fileExists, readFile, writeFile } from '@shopify/cli-kit/node/fs';
6
6
  import { resolvePath } from '@shopify/cli-kit/node/path';
7
7
  import { patchEnvFile } from '@shopify/cli-kit/node/dot-env';
8
8
  import colors from '@shopify/cli-kit/node/colors';
9
9
  import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
10
- import { pullRemoteEnvironmentVariables } from '../../../lib/pull-environment-variables.js';
11
- import { getConfig } from '../../../lib/shopify-config.js';
10
+ import { login } from '../../../lib/auth.js';
11
+ import { getCliCommand } from '../../../lib/shell.js';
12
+ import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
13
+ import { linkStorefront } from '../link.js';
14
+ import { getStorefrontEnvVariables } from '../../../lib/graphql/admin/pull-variables.js';
12
15
 
13
16
  class EnvPull extends Command {
14
17
  static description = "Populate your .env with variables from your Hydrogen storefront.";
15
18
  static flags = {
16
- ["env-branch"]: commonFlags["env-branch"],
19
+ ["env-branch"]: commonFlags.envBranch,
17
20
  path: commonFlags.path,
18
- shop: commonFlags.shop,
19
21
  force: commonFlags.force
20
22
  };
21
23
  async run() {
22
24
  const { flags } = await this.parse(EnvPull);
23
- await pullVariables({ ...flagsToCamelObject(flags) });
25
+ await runEnvPull({ ...flagsToCamelObject(flags) });
24
26
  }
25
27
  }
26
- async function pullVariables({
28
+ async function runEnvPull({
27
29
  envBranch,
28
- force,
29
- path,
30
- shop: flagShop
30
+ path: root = process.cwd(),
31
+ force
31
32
  }) {
32
- const actualPath = path ?? process.cwd();
33
- const environmentVariables = await pullRemoteEnvironmentVariables({
34
- root: actualPath,
35
- flagShop,
33
+ const [{ session, config }, cliCommand] = await Promise.all([
34
+ login(root),
35
+ getCliCommand()
36
+ ]);
37
+ if (!config.storefront?.id) {
38
+ renderMissingLink({ session, cliCommand });
39
+ const runLink = await renderConfirmationPrompt({
40
+ message: outputContent`Run ${outputToken.genericShellCommand(
41
+ `${cliCommand} link`
42
+ )}?`.value
43
+ });
44
+ if (!runLink)
45
+ return;
46
+ config.storefront = await linkStorefront(root, session, config, {
47
+ cliCommand
48
+ });
49
+ }
50
+ if (!config.storefront?.id)
51
+ return;
52
+ const storefront = await getStorefrontEnvVariables(
53
+ session,
54
+ config.storefront.id,
36
55
  envBranch
37
- });
38
- if (!environmentVariables.length) {
56
+ );
57
+ if (!storefront) {
58
+ renderMissingStorefront({
59
+ session,
60
+ storefront: config.storefront,
61
+ cliCommand
62
+ });
63
+ return;
64
+ }
65
+ if (!storefront.environmentVariables.length) {
66
+ outputInfo(`No environment variables found.`);
39
67
  return;
40
68
  }
69
+ const variables = storefront.environmentVariables;
70
+ if (!variables.length)
71
+ return;
41
72
  const fileName = colors.whiteBright(`.env`);
42
- const dotEnvPath = resolvePath(actualPath, ".env");
73
+ const dotEnvPath = resolvePath(root, ".env");
43
74
  const fetchedEnv = {};
44
- environmentVariables.forEach(({ isSecret, key, value }) => {
75
+ variables.forEach(({ isSecret, key, value }) => {
45
76
  fetchedEnv[key] = isSecret ? `""` : value;
46
77
  });
47
78
  if (await fileExists(dotEnvPath) && !force) {
@@ -70,13 +101,10 @@ Continue?`.value
70
101
  const newEnv = patchEnvFile(null, fetchedEnv);
71
102
  await writeFile(dotEnvPath, newEnv);
72
103
  }
73
- const hasSecretVariables = environmentVariables.some(
74
- ({ isSecret }) => isSecret
75
- );
104
+ const hasSecretVariables = variables.some(({ isSecret }) => isSecret);
76
105
  if (hasSecretVariables) {
77
- const { storefront: configStorefront } = await getConfig(actualPath);
78
106
  renderWarning({
79
- body: `${configStorefront.title} contains environment variables marked as secret, so their values weren\u2019t pulled.`
107
+ body: `${config.storefront.title} contains environment variables marked as secret, so their values weren\u2019t pulled.`
80
108
  });
81
109
  }
82
110
  renderSuccess({
@@ -84,4 +112,4 @@ Continue?`.value
84
112
  });
85
113
  }
86
114
 
87
- export { EnvPull as default, pullVariables };
115
+ export { EnvPull as default, runEnvPull };
@@ -3,10 +3,11 @@ import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
3
3
  import { inTemporaryDirectory, fileExists, readFile, writeFile } from '@shopify/cli-kit/node/fs';
4
4
  import { joinPath } from '@shopify/cli-kit/node/path';
5
5
  import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
6
- import { getAdminSession } from '../../../lib/admin-session.js';
7
- import { pullRemoteEnvironmentVariables } from '../../../lib/pull-environment-variables.js';
8
- import { getConfig } from '../../../lib/shopify-config.js';
9
- import { pullVariables } from './pull.js';
6
+ import { login } from '../../../lib/auth.js';
7
+ import { getStorefrontEnvVariables } from '../../../lib/graphql/admin/pull-variables.js';
8
+ import { runEnvPull } from './pull.js';
9
+ import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
10
+ import { linkStorefront } from '../link.js';
10
11
 
11
12
  vi.mock("@shopify/cli-kit/node/ui", async () => {
12
13
  const original = await vi.importActual("@shopify/cli-kit/node/ui");
@@ -16,58 +17,65 @@ vi.mock("@shopify/cli-kit/node/ui", async () => {
16
17
  };
17
18
  });
18
19
  vi.mock("../link.js");
19
- vi.mock("../../../lib/admin-session.js");
20
- vi.mock("../../../lib/shopify-config.js");
21
- vi.mock("../../../lib/pull-environment-variables.js");
22
- vi.mock("../../../lib/shop.js", () => ({
23
- getHydrogenShop: () => "my-shop"
24
- }));
20
+ vi.mock("../../../lib/auth.js");
21
+ vi.mock("../../../lib/render-errors.js");
22
+ vi.mock("../../../lib/graphql/admin/pull-variables.js");
25
23
  describe("pullVariables", () => {
26
24
  const ADMIN_SESSION = {
27
25
  token: "abc123",
28
26
  storeFqdn: "my-shop"
29
27
  };
28
+ const SHOPIFY_CONFIG = {
29
+ shop: "my-shop",
30
+ shopName: "My Shop",
31
+ email: "email",
32
+ storefront: {
33
+ id: "gid://shopify/HydrogenStorefront/2",
34
+ title: "Existing Link"
35
+ }
36
+ };
30
37
  beforeEach(async () => {
31
- vi.mocked(getAdminSession).mockResolvedValue(ADMIN_SESSION);
32
- vi.mocked(getConfig).mockResolvedValue({
33
- storefront: {
34
- id: "gid://shopify/HydrogenStorefront/2",
35
- title: "Existing Link"
36
- }
37
- });
38
- vi.mocked(pullRemoteEnvironmentVariables).mockResolvedValue([
39
- {
40
- id: "gid://shopify/HydrogenStorefrontEnvironmentVariable/1",
41
- key: "PUBLIC_API_TOKEN",
42
- value: "abc123",
43
- isSecret: false
44
- },
45
- {
46
- id: "gid://shopify/HydrogenStorefrontEnvironmentVariable/2",
47
- key: "PRIVATE_API_TOKEN",
48
- value: "",
49
- isSecret: true
50
- }
51
- ]);
38
+ vi.mocked(login).mockResolvedValue({
39
+ session: ADMIN_SESSION,
40
+ config: SHOPIFY_CONFIG
41
+ });
42
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
43
+ id: SHOPIFY_CONFIG.storefront.id,
44
+ environmentVariables: [
45
+ {
46
+ id: "gid://shopify/HydrogenStorefrontEnvironmentVariable/1",
47
+ key: "PUBLIC_API_TOKEN",
48
+ value: "abc123",
49
+ isSecret: false
50
+ },
51
+ {
52
+ id: "gid://shopify/HydrogenStorefrontEnvironmentVariable/2",
53
+ key: "PRIVATE_API_TOKEN",
54
+ value: "",
55
+ isSecret: true
56
+ }
57
+ ]
58
+ });
52
59
  });
53
60
  afterEach(() => {
54
61
  vi.resetAllMocks();
55
62
  mockAndCaptureOutput().clear();
56
63
  });
57
- it("calls pullRemoteEnvironmentVariables", async () => {
64
+ it("calls getStorefrontEnvVariables", async () => {
58
65
  await inTemporaryDirectory(async (tmpDir) => {
59
- await pullVariables({ path: tmpDir, envBranch: "staging" });
60
- expect(pullRemoteEnvironmentVariables).toHaveBeenCalledWith({
61
- root: tmpDir,
62
- envBranch: "staging"
63
- });
66
+ await runEnvPull({ path: tmpDir, envBranch: "staging" });
67
+ expect(getStorefrontEnvVariables).toHaveBeenCalledWith(
68
+ ADMIN_SESSION,
69
+ SHOPIFY_CONFIG.storefront.id,
70
+ "staging"
71
+ );
64
72
  });
65
73
  });
66
74
  it("writes environment variables to a .env file", async () => {
67
75
  await inTemporaryDirectory(async (tmpDir) => {
68
76
  const filePath = joinPath(tmpDir, ".env");
69
77
  expect(await fileExists(filePath)).toBeFalsy();
70
- await pullVariables({ path: tmpDir });
78
+ await runEnvPull({ path: tmpDir });
71
79
  expect(await readFile(filePath)).toStrictEqual(
72
80
  'PUBLIC_API_TOKEN=abc123\nPRIVATE_API_TOKEN=""'
73
81
  );
@@ -76,7 +84,7 @@ describe("pullVariables", () => {
76
84
  it("warns about secret environment variables", async () => {
77
85
  await inTemporaryDirectory(async (tmpDir) => {
78
86
  const outputMock = mockAndCaptureOutput();
79
- await pullVariables({ path: tmpDir });
87
+ await runEnvPull({ path: tmpDir });
80
88
  expect(outputMock.warn()).toMatch(
81
89
  /Existing Link contains environment variables marked as secret, so their/
82
90
  );
@@ -86,12 +94,85 @@ describe("pullVariables", () => {
86
94
  it("renders a success message", async () => {
87
95
  await inTemporaryDirectory(async (tmpDir) => {
88
96
  const outputMock = mockAndCaptureOutput();
89
- await pullVariables({ path: tmpDir });
97
+ await runEnvPull({ path: tmpDir });
90
98
  expect(outputMock.info()).toMatch(
91
99
  /Changes have been made to your \.env file/
92
100
  );
93
101
  });
94
102
  });
103
+ describe("when environment variables are empty", () => {
104
+ beforeEach(() => {
105
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
106
+ id: "gid://shopify/HydrogenStorefront/1",
107
+ environmentVariables: []
108
+ });
109
+ });
110
+ it("renders a message", async () => {
111
+ await inTemporaryDirectory(async (tmpDir) => {
112
+ const outputMock = mockAndCaptureOutput();
113
+ await runEnvPull({ path: tmpDir });
114
+ expect(outputMock.info()).toMatch(/No environment variables found\./);
115
+ });
116
+ });
117
+ });
118
+ describe("when there is no linked storefront", () => {
119
+ beforeEach(async () => {
120
+ vi.mocked(login).mockResolvedValue({
121
+ session: ADMIN_SESSION,
122
+ config: {
123
+ ...SHOPIFY_CONFIG,
124
+ storefront: void 0
125
+ }
126
+ });
127
+ });
128
+ it("calls renderMissingLink", async () => {
129
+ await inTemporaryDirectory(async (tmpDir) => {
130
+ await runEnvPull({ path: tmpDir });
131
+ expect(renderMissingLink).toHaveBeenCalledOnce();
132
+ });
133
+ });
134
+ it("prompts the user to create a link", async () => {
135
+ vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
136
+ await inTemporaryDirectory(async (tmpDir) => {
137
+ await runEnvPull({ path: tmpDir });
138
+ expect(renderConfirmationPrompt).toHaveBeenCalledWith({
139
+ message: expect.stringMatching(/Run .* link.*\?/i)
140
+ });
141
+ expect(linkStorefront).toHaveBeenCalledWith(
142
+ tmpDir,
143
+ ADMIN_SESSION,
144
+ { ...SHOPIFY_CONFIG, storefront: void 0 },
145
+ expect.anything()
146
+ );
147
+ });
148
+ });
149
+ it("ends without requesting variables", async () => {
150
+ await inTemporaryDirectory(async (tmpDir) => {
151
+ await runEnvPull({ path: tmpDir });
152
+ expect(getStorefrontEnvVariables).not.toHaveBeenCalled();
153
+ });
154
+ });
155
+ describe("and the user does not create a new link", () => {
156
+ it("ends without requesting variables", async () => {
157
+ vi.mocked(renderConfirmationPrompt).mockResolvedValue(false);
158
+ await inTemporaryDirectory(async (tmpDir) => {
159
+ await runEnvPull({ path: tmpDir });
160
+ expect(getStorefrontEnvVariables).not.toHaveBeenCalled();
161
+ });
162
+ });
163
+ });
164
+ });
165
+ describe("when there is no matching storefront in the shop", () => {
166
+ beforeEach(() => {
167
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue(null);
168
+ });
169
+ it("renders missing storefronts message and ends", async () => {
170
+ await inTemporaryDirectory(async (tmpDir) => {
171
+ await runEnvPull({ path: tmpDir });
172
+ expect(renderMissingStorefront).toHaveBeenCalledOnce();
173
+ });
174
+ });
175
+ });
95
176
  describe("when a .env file already exists", () => {
96
177
  beforeEach(() => {
97
178
  vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
@@ -100,7 +181,7 @@ describe("pullVariables", () => {
100
181
  await inTemporaryDirectory(async (tmpDir) => {
101
182
  const filePath = joinPath(tmpDir, ".env");
102
183
  await writeFile(filePath, "EXISTING_TOKEN=1");
103
- await pullVariables({ path: tmpDir });
184
+ await runEnvPull({ path: tmpDir });
104
185
  expect(renderConfirmationPrompt).toHaveBeenCalledWith({
105
186
  confirmationMessage: `Yes, confirm changes`,
106
187
  cancellationMessage: `No, make changes later`,
@@ -115,7 +196,7 @@ describe("pullVariables", () => {
115
196
  await inTemporaryDirectory(async (tmpDir) => {
116
197
  const filePath = joinPath(tmpDir, ".env");
117
198
  await writeFile(filePath, "EXISTING_TOKEN=1");
118
- await pullVariables({ path: tmpDir, force: true });
199
+ await runEnvPull({ path: tmpDir, force: true });
119
200
  expect(renderConfirmationPrompt).not.toHaveBeenCalled();
120
201
  });
121
202
  });
@@ -1,27 +1,11 @@
1
- import { fileURLToPath } from 'url';
2
1
  import Command from '@shopify/cli-kit/node/base-command';
3
- import { fileExists, readFile, mkdir, writeFile } from '@shopify/cli-kit/node/fs';
4
- import { resolvePath, joinPath, relativePath, relativizePath, dirname } from '@shopify/cli-kit/node/path';
5
- import { AbortError } from '@shopify/cli-kit/node/error';
6
- import { renderSuccess, renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
2
+ import { resolvePath } from '@shopify/cli-kit/node/path';
3
+ import { renderSuccess } from '@shopify/cli-kit/node/ui';
4
+ import colors from '@shopify/cli-kit/node/colors';
7
5
  import { commonFlags } from '../../../lib/flags.js';
8
6
  import { Flags, Args } from '@oclif/core';
9
- import { transpileFile, format, resolveFormatConfig } from '../../../lib/transpile-ts.js';
10
- import { getV2Flags, convertRouteToV2, convertTemplateToRemixVersion } from '../../../lib/remix-version-interop.js';
7
+ import { ALL_ROUTE_CHOICES, generateRoutes } from '../../../lib/setups/routes/generate.js';
11
8
 
12
- const GENERATOR_TEMPLATES_DIR = "generator-templates";
13
- const ROUTE_MAP = {
14
- home: "/index",
15
- page: "/pages/$pageHandle",
16
- cart: "/cart",
17
- products: "/products/$productHandle",
18
- collections: "/collections/$collectionHandle",
19
- policies: ["/policies/index", "/policies/$policyHandle"],
20
- robots: "/[robots.txt]",
21
- sitemap: "/[sitemap.xml]",
22
- account: ["/account/login", "/account/register"]
23
- };
24
- const ROUTES = [...Object.keys(ROUTE_MAP), "all"];
25
9
  class GenerateRoute extends Command {
26
10
  static description = "Generates a standard Shopify route.";
27
11
  static flags = {
@@ -38,131 +22,46 @@ class GenerateRoute extends Command {
38
22
  };
39
23
  static hidden;
40
24
  static args = {
41
- route: Args.string({
42
- name: "route",
43
- description: `The route to generate. One of ${ROUTES.join()}.`,
25
+ routeName: Args.string({
26
+ name: "routeName",
27
+ description: `The route to generate. One of ${ALL_ROUTE_CHOICES.join()}.`,
44
28
  required: true,
45
- options: ROUTES,
29
+ options: ALL_ROUTE_CHOICES,
46
30
  env: "SHOPIFY_HYDROGEN_ARG_ROUTE"
47
31
  })
48
32
  };
49
33
  async run() {
50
- const result = /* @__PURE__ */ new Map();
51
34
  const {
52
35
  flags,
53
- args: { route }
36
+ args: { routeName }
54
37
  } = await this.parse(GenerateRoute);
55
- const routePath = route === "all" ? Object.values(ROUTE_MAP).flat() : ROUTE_MAP[route];
56
- if (!routePath) {
57
- throw new AbortError(
58
- `No route found for ${route}. Try one of ${ROUTES.join()}.`
59
- );
60
- }
61
38
  const directory = flags.path ? resolvePath(flags.path) : process.cwd();
62
- const isTypescript = flags.typescript || await fileExists(joinPath(directory, "tsconfig.json"));
63
- const routesArray = Array.isArray(routePath) ? routePath : [routePath];
64
- try {
65
- const { isV2RouteConvention, ...v2Flags } = await getV2Flags(directory);
66
- for (const item of routesArray) {
67
- const routeFrom = item;
68
- const routeTo = isV2RouteConvention ? convertRouteToV2(item) : item;
69
- result.set(
70
- routeTo,
71
- await runGenerate(routeFrom, routeTo, {
72
- directory,
73
- typescript: isTypescript,
74
- force: flags.force,
75
- adapter: flags.adapter,
76
- v2Flags
77
- })
78
- );
79
- }
80
- } catch (err) {
81
- throw new AbortError(err.message);
82
- }
83
- const extension = isTypescript ? ".tsx" : ".jsx";
84
- const success = Array.from(result.values()).filter(
85
- (result2) => result2.operation !== "skipped"
86
- );
87
- renderSuccess({
88
- headline: `${success.length} of ${result.size} route${result.size > 1 ? "s" : ""} generated`,
89
- body: {
90
- list: {
91
- items: Array.from(result.entries()).map(
92
- ([path, { operation }]) => `[${operation}] app/routes${path}${extension}`
93
- )
94
- }
95
- }
39
+ await runGenerate({
40
+ ...flags,
41
+ directory,
42
+ routeName
96
43
  });
97
44
  }
98
45
  }
99
- async function runGenerate(routeFrom, routeTo, {
100
- directory,
101
- typescript,
102
- force,
103
- adapter,
104
- templatesRoot = fileURLToPath(new URL("../../../", import.meta.url)),
105
- v2Flags = {}
106
- }) {
107
- let operation;
108
- const extension = typescript ? ".tsx" : ".jsx";
109
- const templatePath = joinPath(
110
- templatesRoot,
111
- GENERATOR_TEMPLATES_DIR,
112
- "routes",
113
- `${routeFrom}.tsx`
46
+ async function runGenerate(options) {
47
+ const { routes } = await generateRoutes(options);
48
+ const padEnd = 3 + routes.reduce(
49
+ (acc, route) => Math.max(acc, route.destinationRoute.length),
50
+ 0
114
51
  );
115
- const destinationPath = joinPath(
116
- directory,
117
- "app",
118
- "routes",
119
- `${routeTo}${extension}`
120
- );
121
- const relativeDestinationPath = relativePath(directory, destinationPath);
122
- if (!force && await fileExists(destinationPath)) {
123
- const shouldOverwrite = await renderConfirmationPrompt({
124
- message: `The file ${relativizePath(
125
- relativeDestinationPath
126
- )} already exists. Do you want to overwrite it?`,
127
- defaultValue: false
128
- });
129
- operation = shouldOverwrite ? "overwritten" : "skipped";
130
- if (operation === "skipped") {
131
- return { operation };
52
+ const successfulGenerationCount = routes.filter(
53
+ ({ operation }) => operation !== "skipped"
54
+ ).length;
55
+ renderSuccess({
56
+ headline: `${successfulGenerationCount} of ${routes.length} route${routes.length > 1 ? "s" : ""} generated`,
57
+ body: {
58
+ list: {
59
+ items: routes.map(
60
+ ({ operation, destinationRoute }) => destinationRoute.padEnd(padEnd) + colors.dim(`[${operation}]`)
61
+ )
62
+ }
132
63
  }
133
- } else {
134
- operation = "generated";
135
- }
136
- let templateContent = await readFile(templatePath);
137
- templateContent = convertTemplateToRemixVersion(templateContent, v2Flags);
138
- if (!typescript) {
139
- const jsConfigPath = joinPath(directory, "jsconfig.json");
140
- const config = await fileExists(jsConfigPath) ? JSON.parse(
141
- (await readFile(jsConfigPath, { encoding: "utf8" })).replace(
142
- /^\s*\/\/.*$/gm,
143
- ""
144
- )
145
- ) : void 0;
146
- templateContent = transpileFile(templateContent, config?.compilerOptions);
147
- }
148
- if (adapter) {
149
- templateContent = templateContent.replace(
150
- /@shopify\/remix-oxygen/g,
151
- adapter
152
- );
153
- }
154
- templateContent = format(
155
- templateContent,
156
- await resolveFormatConfig(destinationPath),
157
- destinationPath
158
- );
159
- if (!await fileExists(dirname(destinationPath))) {
160
- await mkdir(dirname(destinationPath));
161
- }
162
- await writeFile(destinationPath, templateContent);
163
- return {
164
- operation
165
- };
64
+ });
166
65
  }
167
66
 
168
- export { GENERATOR_TEMPLATES_DIR, GenerateRoute as default, runGenerate };
67
+ export { GenerateRoute as default, runGenerate };