@shopify/cli-hydrogen 5.0.2 → 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 (243) hide show
  1. package/dist/commands/hydrogen/build.js +16 -2
  2. package/dist/commands/hydrogen/codegen-unstable.js +13 -24
  3. package/dist/commands/hydrogen/dev.js +45 -39
  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 +70 -69
  13. package/dist/commands/hydrogen/link.test.js +72 -107
  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/setup/css.js +79 -0
  19. package/dist/commands/hydrogen/setup/markets.js +53 -0
  20. package/dist/commands/hydrogen/setup.js +133 -0
  21. package/dist/commands/hydrogen/shortcut.js +2 -45
  22. package/dist/commands/hydrogen/shortcut.test.js +10 -37
  23. package/dist/generator-templates/assets/css-modules/package.json +6 -0
  24. package/dist/generator-templates/assets/postcss/package.json +10 -0
  25. package/dist/generator-templates/assets/postcss/postcss.config.js +8 -0
  26. package/dist/generator-templates/assets/tailwind/package.json +13 -0
  27. package/dist/generator-templates/assets/tailwind/postcss.config.js +10 -0
  28. package/dist/generator-templates/assets/tailwind/tailwind.config.js +8 -0
  29. package/dist/generator-templates/assets/tailwind/tailwind.css +3 -0
  30. package/dist/generator-templates/assets/vanilla-extract/package.json +9 -0
  31. package/dist/generator-templates/starter/.eslintignore +5 -0
  32. package/dist/generator-templates/starter/.eslintrc.js +18 -0
  33. package/dist/generator-templates/starter/.graphqlrc.yml +1 -0
  34. package/dist/generator-templates/starter/README.md +40 -0
  35. package/dist/generator-templates/starter/app/components/Aside.tsx +47 -0
  36. package/dist/generator-templates/starter/app/components/Cart.tsx +340 -0
  37. package/dist/generator-templates/starter/app/components/Footer.tsx +99 -0
  38. package/dist/generator-templates/starter/app/components/Header.tsx +178 -0
  39. package/dist/generator-templates/starter/app/components/Layout.tsx +95 -0
  40. package/dist/generator-templates/starter/app/components/Search.tsx +480 -0
  41. package/dist/generator-templates/starter/app/entry.client.tsx +12 -0
  42. package/dist/generator-templates/starter/app/entry.server.tsx +33 -0
  43. package/dist/generator-templates/starter/app/root.tsx +270 -0
  44. package/dist/generator-templates/starter/app/routes/$.tsx +7 -0
  45. package/dist/generator-templates/{routes → starter/app/routes}/[robots.txt].tsx +47 -69
  46. package/dist/generator-templates/starter/app/routes/[sitemap.xml].tsx +174 -0
  47. package/dist/generator-templates/starter/app/routes/_index.tsx +145 -0
  48. package/dist/generator-templates/starter/app/routes/account.$.tsx +9 -0
  49. package/dist/generator-templates/starter/app/routes/account.addresses.tsx +563 -0
  50. package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +309 -0
  51. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +196 -0
  52. package/dist/generator-templates/starter/app/routes/account.profile.tsx +289 -0
  53. package/dist/generator-templates/starter/app/routes/account.tsx +203 -0
  54. package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +157 -0
  55. package/dist/generator-templates/starter/app/routes/account_.login.tsx +143 -0
  56. package/dist/generator-templates/starter/app/routes/account_.logout.tsx +33 -0
  57. package/dist/generator-templates/starter/app/routes/account_.recover.tsx +124 -0
  58. package/dist/generator-templates/starter/app/routes/account_.register.tsx +207 -0
  59. package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +136 -0
  60. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +342 -0
  61. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +88 -0
  62. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +162 -0
  63. package/dist/generator-templates/starter/app/routes/blogs._index.tsx +94 -0
  64. package/dist/generator-templates/starter/app/routes/cart.tsx +104 -0
  65. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +184 -0
  66. package/dist/generator-templates/starter/app/routes/collections._index.tsx +120 -0
  67. package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +57 -0
  68. package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +94 -0
  69. package/dist/generator-templates/starter/app/routes/policies._index.tsx +63 -0
  70. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +418 -0
  71. package/dist/generator-templates/starter/app/routes/search.tsx +168 -0
  72. package/dist/generator-templates/starter/app/styles/app.css +473 -0
  73. package/dist/generator-templates/starter/app/styles/reset.css +129 -0
  74. package/dist/generator-templates/starter/app/utils.ts +46 -0
  75. package/dist/generator-templates/starter/package.json +43 -0
  76. package/dist/generator-templates/starter/public/favicon.svg +28 -0
  77. package/dist/generator-templates/starter/remix.config.js +26 -0
  78. package/dist/generator-templates/starter/remix.env.d.ts +39 -0
  79. package/dist/generator-templates/starter/server.ts +253 -0
  80. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +1906 -0
  81. package/dist/generator-templates/starter/tsconfig.json +22 -0
  82. package/dist/lib/auth.js +123 -0
  83. package/dist/lib/auth.test.js +157 -0
  84. package/dist/lib/build.js +51 -0
  85. package/dist/lib/check-version.js +3 -3
  86. package/dist/lib/check-version.test.js +24 -0
  87. package/dist/lib/codegen.js +26 -17
  88. package/dist/lib/environment-variables.js +68 -0
  89. package/dist/lib/environment-variables.test.js +147 -0
  90. package/dist/lib/file.js +41 -0
  91. package/dist/lib/file.test.js +69 -0
  92. package/dist/lib/flags.js +39 -2
  93. package/dist/lib/format-code.js +26 -0
  94. package/dist/lib/gid.js +12 -0
  95. package/dist/lib/{graphql.test.js → gid.test.js} +1 -1
  96. package/dist/lib/graphql/admin/client.js +27 -0
  97. package/dist/lib/graphql/admin/client.test.js +51 -0
  98. package/dist/lib/graphql/admin/create-storefront.js +13 -15
  99. package/dist/lib/graphql/admin/create-storefront.test.js +64 -0
  100. package/dist/lib/graphql/admin/fetch-job.js +6 -15
  101. package/dist/lib/graphql/admin/link-storefront.js +7 -11
  102. package/dist/lib/graphql/admin/link-storefront.test.js +38 -0
  103. package/dist/lib/graphql/admin/list-environments.js +2 -2
  104. package/dist/lib/graphql/admin/list-environments.test.js +44 -0
  105. package/dist/lib/graphql/admin/list-storefronts.js +7 -11
  106. package/dist/lib/graphql/admin/list-storefronts.test.js +44 -0
  107. package/dist/lib/graphql/admin/pull-variables.js +3 -3
  108. package/dist/lib/graphql/admin/pull-variables.test.js +37 -0
  109. package/dist/lib/graphql/business-platform/user-account.js +83 -0
  110. package/dist/lib/graphql/business-platform/user-account.test.js +80 -0
  111. package/dist/lib/log.js +185 -9
  112. package/dist/lib/log.test.js +92 -0
  113. package/dist/lib/mini-oxygen.js +19 -9
  114. package/dist/lib/missing-routes.js +0 -2
  115. package/dist/lib/onboarding/common.js +456 -0
  116. package/dist/lib/onboarding/index.js +2 -0
  117. package/dist/lib/onboarding/local.js +229 -0
  118. package/dist/lib/onboarding/remote.js +89 -0
  119. package/dist/lib/remix-version-interop.js +5 -5
  120. package/dist/lib/remix-version-interop.test.js +11 -1
  121. package/dist/lib/render-errors.js +13 -11
  122. package/dist/lib/setups/css/assets.js +89 -0
  123. package/dist/lib/setups/css/css-modules.js +22 -0
  124. package/dist/lib/setups/css/index.js +44 -0
  125. package/dist/lib/setups/css/postcss.js +34 -0
  126. package/dist/lib/setups/css/replacers.js +137 -0
  127. package/dist/lib/setups/css/tailwind.js +54 -0
  128. package/dist/lib/setups/css/vanilla-extract.js +22 -0
  129. package/dist/lib/setups/i18n/domains.test.js +25 -0
  130. package/dist/lib/setups/i18n/index.js +46 -0
  131. package/dist/lib/setups/i18n/replacers.js +227 -0
  132. package/dist/lib/setups/i18n/subdomains.test.js +25 -0
  133. package/dist/lib/setups/i18n/subfolders.test.js +25 -0
  134. package/dist/lib/setups/i18n/templates/domains.js +14 -0
  135. package/dist/lib/setups/i18n/templates/domains.ts +25 -0
  136. package/dist/lib/setups/i18n/templates/subdomains.js +14 -0
  137. package/dist/lib/setups/i18n/templates/subdomains.ts +24 -0
  138. package/dist/lib/setups/i18n/templates/subfolders.js +14 -0
  139. package/dist/lib/setups/i18n/templates/subfolders.ts +28 -0
  140. package/dist/lib/setups/routes/generate.js +244 -0
  141. package/dist/lib/setups/routes/generate.test.js +313 -0
  142. package/dist/lib/shell.js +52 -5
  143. package/dist/lib/shell.test.js +42 -16
  144. package/dist/lib/shopify-config.js +23 -18
  145. package/dist/lib/shopify-config.test.js +63 -73
  146. package/dist/lib/template-downloader.js +9 -7
  147. package/dist/lib/transpile-ts.js +9 -29
  148. package/dist/virtual-routes/routes/index.jsx +40 -19
  149. package/oclif.manifest.json +710 -1
  150. package/package.json +16 -16
  151. package/dist/commands/hydrogen/build.d.ts +0 -23
  152. package/dist/commands/hydrogen/check.d.ts +0 -15
  153. package/dist/commands/hydrogen/codegen-unstable.d.ts +0 -15
  154. package/dist/commands/hydrogen/dev.d.ts +0 -21
  155. package/dist/commands/hydrogen/env/list.d.ts +0 -18
  156. package/dist/commands/hydrogen/env/pull.d.ts +0 -22
  157. package/dist/commands/hydrogen/g.d.ts +0 -10
  158. package/dist/commands/hydrogen/generate/route.d.ts +0 -32
  159. package/dist/commands/hydrogen/generate/route.test.d.ts +0 -1
  160. package/dist/commands/hydrogen/generate/routes.d.ts +0 -16
  161. package/dist/commands/hydrogen/init.d.ts +0 -24
  162. package/dist/commands/hydrogen/init.test.d.ts +0 -1
  163. package/dist/commands/hydrogen/link.d.ts +0 -23
  164. package/dist/commands/hydrogen/link.test.d.ts +0 -1
  165. package/dist/commands/hydrogen/list.d.ts +0 -21
  166. package/dist/commands/hydrogen/list.test.d.ts +0 -1
  167. package/dist/commands/hydrogen/preview.d.ts +0 -17
  168. package/dist/commands/hydrogen/shortcut.d.ts +0 -9
  169. package/dist/commands/hydrogen/shortcut.test.d.ts +0 -1
  170. package/dist/commands/hydrogen/unlink.d.ts +0 -16
  171. package/dist/commands/hydrogen/unlink.test.d.ts +0 -1
  172. package/dist/create-app.d.ts +0 -1
  173. package/dist/generator-templates/routes/[sitemap.xml].tsx +0 -235
  174. package/dist/generator-templates/routes/account/login.tsx +0 -103
  175. package/dist/generator-templates/routes/account/register.tsx +0 -103
  176. package/dist/generator-templates/routes/cart.tsx +0 -81
  177. package/dist/generator-templates/routes/collections/$collectionHandle.tsx +0 -104
  178. package/dist/generator-templates/routes/collections/index.tsx +0 -102
  179. package/dist/generator-templates/routes/graphiql.tsx +0 -10
  180. package/dist/generator-templates/routes/index.tsx +0 -40
  181. package/dist/generator-templates/routes/pages/$pageHandle.tsx +0 -112
  182. package/dist/generator-templates/routes/policies/$policyHandle.tsx +0 -140
  183. package/dist/generator-templates/routes/policies/index.tsx +0 -117
  184. package/dist/generator-templates/routes/products/$productHandle.tsx +0 -92
  185. package/dist/hooks/init.d.ts +0 -5
  186. package/dist/lib/admin-session.d.ts +0 -6
  187. package/dist/lib/admin-session.js +0 -16
  188. package/dist/lib/admin-session.test.d.ts +0 -1
  189. package/dist/lib/admin-session.test.js +0 -27
  190. package/dist/lib/admin-urls.d.ts +0 -8
  191. package/dist/lib/check-lockfile.d.ts +0 -3
  192. package/dist/lib/check-lockfile.test.d.ts +0 -1
  193. package/dist/lib/check-version.d.ts +0 -16
  194. package/dist/lib/check-version.test.d.ts +0 -1
  195. package/dist/lib/codegen.d.ts +0 -26
  196. package/dist/lib/combined-environment-variables.d.ts +0 -8
  197. package/dist/lib/combined-environment-variables.js +0 -57
  198. package/dist/lib/combined-environment-variables.test.d.ts +0 -1
  199. package/dist/lib/combined-environment-variables.test.js +0 -111
  200. package/dist/lib/config.d.ts +0 -20
  201. package/dist/lib/flags.d.ts +0 -27
  202. package/dist/lib/flags.test.d.ts +0 -1
  203. package/dist/lib/graphql/admin/create-storefront.d.ts +0 -17
  204. package/dist/lib/graphql/admin/fetch-job.d.ts +0 -23
  205. package/dist/lib/graphql/admin/link-storefront.d.ts +0 -14
  206. package/dist/lib/graphql/admin/list-environments.d.ts +0 -21
  207. package/dist/lib/graphql/admin/list-storefronts.d.ts +0 -25
  208. package/dist/lib/graphql/admin/pull-variables.d.ts +0 -21
  209. package/dist/lib/graphql.d.ts +0 -21
  210. package/dist/lib/graphql.js +0 -18
  211. package/dist/lib/graphql.test.d.ts +0 -1
  212. package/dist/lib/log.d.ts +0 -6
  213. package/dist/lib/mini-oxygen.d.ts +0 -22
  214. package/dist/lib/missing-routes.d.ts +0 -8
  215. package/dist/lib/missing-routes.test.d.ts +0 -1
  216. package/dist/lib/missing-storefronts.d.ts +0 -5
  217. package/dist/lib/missing-storefronts.js +0 -18
  218. package/dist/lib/process.d.ts +0 -6
  219. package/dist/lib/pull-environment-variables.d.ts +0 -20
  220. package/dist/lib/pull-environment-variables.js +0 -57
  221. package/dist/lib/pull-environment-variables.test.d.ts +0 -1
  222. package/dist/lib/pull-environment-variables.test.js +0 -174
  223. package/dist/lib/remix-version-interop.d.ts +0 -11
  224. package/dist/lib/remix-version-interop.test.d.ts +0 -1
  225. package/dist/lib/render-errors.d.ts +0 -16
  226. package/dist/lib/shell.d.ts +0 -11
  227. package/dist/lib/shell.test.d.ts +0 -1
  228. package/dist/lib/shop.d.ts +0 -7
  229. package/dist/lib/shop.js +0 -32
  230. package/dist/lib/shop.test.d.ts +0 -1
  231. package/dist/lib/shop.test.js +0 -78
  232. package/dist/lib/shopify-config.d.ts +0 -35
  233. package/dist/lib/shopify-config.test.d.ts +0 -1
  234. package/dist/lib/string.d.ts +0 -3
  235. package/dist/lib/string.test.d.ts +0 -1
  236. package/dist/lib/template-downloader.d.ts +0 -6
  237. package/dist/lib/transpile-ts.d.ts +0 -16
  238. package/dist/lib/user-errors.d.ts +0 -9
  239. package/dist/lib/user-errors.js +0 -11
  240. package/dist/lib/virtual-routes.d.ts +0 -7
  241. package/dist/lib/virtual-routes.test.d.ts +0 -1
  242. /package/dist/{commands/hydrogen/env/list.test.d.ts → lib/setups/css/common.js} +0 -0
  243. /package/dist/{commands/hydrogen/env/pull.test.d.ts → lib/setups/i18n/mock-i18n-types.js} +0 -0
@@ -1,26 +1,31 @@
1
- import { describe, beforeEach, vi, it, expect } from 'vitest';
1
+ import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
2
+ import { platform, userInfo } from 'node:os';
2
3
  import { fileExists } from '@shopify/cli-kit/node/fs';
3
4
  import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
4
- import { shellWriteAlias, getCliCommand } from './shell.js';
5
+ import { shellWriteAlias, createPlatformShortcut, getCliCommand } from './shell.js';
5
6
  import { execAsync } from './process.js';
6
7
 
8
+ vi.mock("node:os");
9
+ vi.mock("node:child_process");
10
+ vi.mock("@shopify/cli-kit/node/fs");
11
+ vi.mock("@shopify/cli-kit/node/node-package-manager");
12
+ vi.mock("./process.js", async () => {
13
+ const original = await vi.importActual(
14
+ "./process.js"
15
+ );
16
+ return {
17
+ ...original,
18
+ execAsync: vi.fn()
19
+ };
20
+ });
21
+ vi.mocked(fileExists).mockResolvedValue(false);
22
+ vi.mocked(getPackageManager).mockResolvedValue("npm");
7
23
  describe("shell", () => {
8
24
  beforeEach(() => {
9
25
  vi.resetAllMocks();
10
- vi.mock("node:child_process");
11
- vi.mock("@shopify/cli-kit/node/fs");
12
- vi.mock("@shopify/cli-kit/node/node-package-manager");
13
- vi.mock("./process.js", async () => {
14
- const original = await vi.importActual(
15
- "./process.js"
16
- );
17
- return {
18
- ...original,
19
- execAsync: vi.fn()
20
- };
21
- });
22
- vi.mocked(fileExists).mockResolvedValue(false);
23
- vi.mocked(getPackageManager).mockResolvedValue("npm");
26
+ });
27
+ afterEach(() => {
28
+ delete process.env.MINGW_PREFIX;
24
29
  });
25
30
  describe("shellWriteAlias", () => {
26
31
  ["bash", "zsh", "fish"].forEach((shell) => {
@@ -63,8 +68,29 @@ describe("shell", () => {
63
68
  });
64
69
  });
65
70
  });
71
+ describe("createPlatformShortcut", () => {
72
+ it("creates aliases for Unix", async () => {
73
+ vi.mocked(platform).mockReturnValue("darwin");
74
+ const result = await createPlatformShortcut();
75
+ expect(result).toEqual(expect.arrayContaining(["zsh", "bash", "fish"]));
76
+ });
77
+ it("creates aliases for Windows", async () => {
78
+ vi.mocked(platform).mockReturnValue("win32");
79
+ const result = await createPlatformShortcut();
80
+ expect(result).toEqual(
81
+ expect.arrayContaining(["PowerShell", "PowerShell 7+"])
82
+ );
83
+ });
84
+ it("creates aliases for Windows in Git Bash", async () => {
85
+ process.env.MINGW_PREFIX = "something";
86
+ vi.mocked(platform).mockReturnValue("win32");
87
+ const result = await createPlatformShortcut();
88
+ expect(result).toEqual(expect.arrayContaining(["bash"]));
89
+ });
90
+ });
66
91
  describe("getCliCommand", () => {
67
92
  it("returns the shortcut alias if available", async () => {
93
+ vi.mocked(userInfo).mockReturnValue({ shell: "/bin/bash" });
68
94
  vi.mocked(execAsync).mockImplementation(
69
95
  (shellCommand) => shellCommand.startsWith("grep") ? Promise.resolve({ stdout: "stuff", stderr: "" }) : Promise.reject(null)
70
96
  );
@@ -1,10 +1,16 @@
1
1
  import { resolvePath, dirname } from '@shopify/cli-kit/node/path';
2
- import { fileExists, readFile, mkdir, writeFile } from '@shopify/cli-kit/node/fs';
2
+ import { fileExists, writeFile, readFile, mkdir } from '@shopify/cli-kit/node/fs';
3
3
  import { AbortError } from '@shopify/cli-kit/node/error';
4
- import { outputInfo } from '@shopify/cli-kit/node/output';
5
4
 
6
5
  const SHOPIFY_DIR = ".shopify";
7
6
  const SHOPIFY_DIR_PROJECT = "project.json";
7
+ async function resetConfig(root) {
8
+ const filePath = resolvePath(root, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
9
+ if (!await fileExists(filePath)) {
10
+ return;
11
+ }
12
+ await writeFile(filePath, JSON.stringify({}));
13
+ }
8
14
  async function getConfig(root) {
9
15
  const filePath = resolvePath(root, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
10
16
  if (!await fileExists(filePath)) {
@@ -12,25 +18,23 @@ async function getConfig(root) {
12
18
  }
13
19
  return JSON.parse(await readFile(filePath));
14
20
  }
15
- async function setShop(root, shop) {
21
+ async function setUserAccount(root, { shop, shopName, email }) {
16
22
  const filePath = resolvePath(root, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
17
- if (!await fileExists(filePath)) {
23
+ let existingConfig = {};
24
+ if (await fileExists(filePath)) {
25
+ existingConfig = JSON.parse(await readFile(filePath));
26
+ } else {
18
27
  await mkdir(dirname(filePath));
19
- const newConfig = {
20
- shop
21
- };
22
- await writeFile(filePath, JSON.stringify(newConfig));
23
- await ensureShopifyGitIgnore(root);
24
- return newConfig;
25
28
  }
26
- const existingConfig = JSON.parse(await readFile(filePath));
27
- const config = {
29
+ const newConfig = {
28
30
  ...existingConfig,
29
- shop
31
+ shop,
32
+ shopName,
33
+ email
30
34
  };
31
- await writeFile(filePath, JSON.stringify(config));
35
+ await writeFile(filePath, JSON.stringify(newConfig));
32
36
  await ensureShopifyGitIgnore(root);
33
- return config;
37
+ return newConfig;
34
38
  }
35
39
  async function setStorefront(root, { id, title }) {
36
40
  try {
@@ -50,7 +54,9 @@ async function setStorefront(root, { id, title }) {
50
54
  async function unsetStorefront(root) {
51
55
  try {
52
56
  const filePath = resolvePath(root, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
53
- const existingConfig = JSON.parse(await readFile(filePath));
57
+ const existingConfig = JSON.parse(
58
+ await readFile(filePath)
59
+ );
54
60
  const config = {
55
61
  ...existingConfig,
56
62
  storefront: void 0
@@ -75,7 +81,6 @@ async function ensureShopifyGitIgnore(root) {
75
81
  }
76
82
  gitIgnoreContents += `${SHOPIFY_DIR}\r
77
83
  `;
78
- outputInfo("Adding .shopify to .gitignore...");
79
84
  await writeFile(gitIgnoreFilePath, gitIgnoreContents);
80
85
  return true;
81
86
  } catch {
@@ -83,4 +88,4 @@ async function ensureShopifyGitIgnore(root) {
83
88
  }
84
89
  }
85
90
 
86
- export { SHOPIFY_DIR, SHOPIFY_DIR_PROJECT, ensureShopifyGitIgnore, getConfig, setShop, setStorefront, unsetStorefront };
91
+ export { SHOPIFY_DIR, SHOPIFY_DIR_PROJECT, ensureShopifyGitIgnore, getConfig, resetConfig, setStorefront, setUserAccount, unsetStorefront };
@@ -1,8 +1,24 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { inTemporaryDirectory, mkdir, writeFile, fileExists, readFile } from '@shopify/cli-kit/node/fs';
3
3
  import { joinPath, dirname } from '@shopify/cli-kit/node/path';
4
- import { getConfig, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT, setShop, setStorefront, unsetStorefront, ensureShopifyGitIgnore } from './shopify-config.js';
4
+ import { getConfig, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT, resetConfig, setUserAccount, setStorefront, unsetStorefront, ensureShopifyGitIgnore } from './shopify-config.js';
5
5
 
6
+ async function writeExistingConfig(dir, config) {
7
+ const existingConfig = config ?? {
8
+ shop: "previous-shop",
9
+ shopName: "Previous Shop",
10
+ email: "email",
11
+ storefront: {
12
+ id: "gid://shopify/HydrogenStorefront/1",
13
+ title: "Hydrogen"
14
+ }
15
+ };
16
+ const filePath = joinPath(dir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
17
+ await mkdir(dirname(filePath));
18
+ await writeFile(filePath, JSON.stringify(existingConfig));
19
+ expect(JSON.parse(await readFile(filePath))).toStrictEqual(existingConfig);
20
+ return { existingConfig, filePath };
21
+ }
6
22
  describe("getConfig()", () => {
7
23
  describe("when no config exists", () => {
8
24
  it("returns an empty object", async () => {
@@ -16,7 +32,9 @@ describe("getConfig()", () => {
16
32
  it("returns the config", async () => {
17
33
  await inTemporaryDirectory(async (tmpDir) => {
18
34
  const existingConfig = {
19
- shop: "my-shop"
35
+ shop: "my-shop",
36
+ shopName: "My Shop",
37
+ email: "email"
20
38
  };
21
39
  const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
22
40
  await mkdir(dirname(filePath));
@@ -27,64 +45,70 @@ describe("getConfig()", () => {
27
45
  });
28
46
  });
29
47
  });
30
- describe("setShop()", () => {
48
+ describe("resetConfig()", () => {
49
+ it("writes an empty object", async () => {
50
+ await inTemporaryDirectory(async (tmpDir) => {
51
+ await writeExistingConfig(tmpDir);
52
+ await resetConfig(tmpDir);
53
+ const config = await getConfig(tmpDir);
54
+ expect(config).toStrictEqual({});
55
+ });
56
+ });
57
+ });
58
+ describe("setUserAccount()", () => {
31
59
  describe("when no config exists", () => {
32
60
  it("creates a new config file", async () => {
33
61
  await inTemporaryDirectory(async (tmpDir) => {
34
62
  const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
35
63
  expect(await fileExists(filePath)).toBeFalsy();
36
- await setShop(tmpDir, "new-shop");
64
+ await setUserAccount(tmpDir, {
65
+ shop: "new-shop",
66
+ shopName: "New Shop",
67
+ email: "email"
68
+ });
37
69
  expect(await fileExists(filePath)).toBeTruthy();
38
70
  });
39
71
  });
40
72
  it("returns the new config", async () => {
41
73
  await inTemporaryDirectory(async (tmpDir) => {
42
- const config = await setShop(tmpDir, "new-shop");
43
- expect(config).toStrictEqual({
44
- shop: "new-shop"
45
- });
74
+ const newConfig = {
75
+ shop: "new-shop",
76
+ email: "email",
77
+ shopName: "New Shop"
78
+ };
79
+ const config = await setUserAccount(tmpDir, newConfig);
80
+ expect(config).toStrictEqual(newConfig);
46
81
  });
47
82
  });
48
83
  });
49
84
  describe("when a config exists", () => {
50
85
  it("updates the config file", async () => {
51
86
  await inTemporaryDirectory(async (tmpDir) => {
52
- const existingConfig = {
53
- shop: "previous-shop",
54
- storefront: {
55
- id: "gid://shopify/HydrogenStorefront/1",
56
- title: "Hydrogen"
57
- }
87
+ const { existingConfig, filePath } = await writeExistingConfig(tmpDir);
88
+ const newConfig = {
89
+ shop: "new-shop",
90
+ shopName: "New Shop",
91
+ email: "email"
58
92
  };
59
- const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
60
- await mkdir(dirname(filePath));
61
- await writeFile(filePath, JSON.stringify(existingConfig));
62
- expect(JSON.parse(await readFile(filePath))).toStrictEqual(
63
- existingConfig
64
- );
65
- await setShop(tmpDir, "new-shop");
93
+ await setUserAccount(tmpDir, newConfig);
66
94
  expect(JSON.parse(await readFile(filePath))).toStrictEqual({
67
95
  ...existingConfig,
68
- shop: "new-shop"
96
+ ...newConfig
69
97
  });
70
98
  });
71
99
  });
72
100
  it("returns the new config", async () => {
73
101
  await inTemporaryDirectory(async (tmpDir) => {
74
- const existingConfig = {
75
- shop: "previous-shop",
76
- storefront: {
77
- id: "gid://shopify/HydrogenStorefront/1",
78
- title: "Hydrogen"
79
- }
102
+ const { existingConfig } = await writeExistingConfig(tmpDir);
103
+ const newConfig = {
104
+ shop: "new-shop",
105
+ email: "email",
106
+ shopName: "New Shop"
80
107
  };
81
- const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
82
- await mkdir(dirname(filePath));
83
- await writeFile(filePath, JSON.stringify(existingConfig));
84
- const config = await setShop(tmpDir, "new-shop");
108
+ const config = await setUserAccount(tmpDir, newConfig);
85
109
  expect(config).toStrictEqual({
86
110
  ...existingConfig,
87
- shop: "new-shop"
111
+ ...newConfig
88
112
  });
89
113
  });
90
114
  });
@@ -93,19 +117,7 @@ describe("setShop()", () => {
93
117
  describe("setStorefront()", () => {
94
118
  it("updates the config file", async () => {
95
119
  await inTemporaryDirectory(async (tmpDir) => {
96
- const existingConfig = {
97
- shop: "previous-shop",
98
- storefront: {
99
- id: "gid://shopify/HydrogenStorefront/1",
100
- title: "Hydrogen"
101
- }
102
- };
103
- const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
104
- await mkdir(dirname(filePath));
105
- await writeFile(filePath, JSON.stringify(existingConfig));
106
- expect(JSON.parse(await readFile(filePath))).toStrictEqual(
107
- existingConfig
108
- );
120
+ const { existingConfig, filePath } = await writeExistingConfig(tmpDir);
109
121
  const newStorefront = {
110
122
  id: "gid://shopify/HydrogenStorefront/2",
111
123
  title: "Remix"
@@ -119,16 +131,7 @@ describe("setStorefront()", () => {
119
131
  });
120
132
  it("returns the new config", async () => {
121
133
  await inTemporaryDirectory(async (tmpDir) => {
122
- const existingConfig = {
123
- shop: "previous-shop",
124
- storefront: {
125
- id: "gid://shopify/HydrogenStorefront/1",
126
- title: "Hydrogen"
127
- }
128
- };
129
- const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
130
- await mkdir(dirname(filePath));
131
- await writeFile(filePath, JSON.stringify(existingConfig));
134
+ const { existingConfig } = await writeExistingConfig(tmpDir);
132
135
  const newStorefront = {
133
136
  id: "gid://shopify/HydrogenStorefront/2",
134
137
  title: "Remix"
@@ -144,27 +147,14 @@ describe("setStorefront()", () => {
144
147
  describe("unsetStorefront()", () => {
145
148
  it("removes the storefront configuration and returns the config", async () => {
146
149
  await inTemporaryDirectory(async (tmpDir) => {
147
- const existingConfig = {
148
- shop: "previous-shop",
149
- storefront: {
150
- id: "gid://shopify/HydrogenStorefront/1",
151
- title: "Hydrogen"
152
- }
153
- };
154
- const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
155
- await mkdir(dirname(filePath));
156
- await writeFile(filePath, JSON.stringify(existingConfig));
157
- expect(JSON.parse(await readFile(filePath))).toStrictEqual(
158
- existingConfig
159
- );
150
+ const { filePath, existingConfig } = await writeExistingConfig(tmpDir);
160
151
  const config = await unsetStorefront(tmpDir);
161
152
  expect(config).toStrictEqual({
162
- shop: "previous-shop",
153
+ ...existingConfig,
163
154
  storefront: void 0
164
155
  });
165
- expect(JSON.parse(await readFile(filePath))).toStrictEqual({
166
- shop: "previous-shop"
167
- });
156
+ const { storefront, ...actualConfig } = existingConfig;
157
+ expect(JSON.parse(await readFile(filePath))).toStrictEqual(actualConfig);
168
158
  });
169
159
  });
170
160
  });
@@ -9,8 +9,8 @@ import { fileURLToPath } from 'url';
9
9
 
10
10
  const REPO_RELEASES_URL = `https://api.github.com/repos/shopify/hydrogen/releases/latest`;
11
11
  const getTryMessage = (status) => status === 403 ? `If you are using a VPN, WARP, or similar service, consider disabling it momentarily.` : void 0;
12
- async function getLatestReleaseDownloadUrl() {
13
- const response = await fetch(REPO_RELEASES_URL);
12
+ async function getLatestReleaseDownloadUrl(signal) {
13
+ const response = await fetch(REPO_RELEASES_URL, { signal });
14
14
  if (!response.ok || response.status >= 400) {
15
15
  throw new AbortError(
16
16
  `Failed to fetch the latest release information. Status ${response.status} ${response.statusText.replace(/\.$/, "")}.`,
@@ -23,8 +23,8 @@ async function getLatestReleaseDownloadUrl() {
23
23
  url: release.tarball_url
24
24
  };
25
25
  }
26
- async function downloadTarball(url, storageDir) {
27
- const response = await fetch(url);
26
+ async function downloadTarball(url, storageDir, signal) {
27
+ const response = await fetch(url, { signal });
28
28
  if (!response.ok || response.status >= 400) {
29
29
  throw new AbortError(
30
30
  `Failed to download the latest release files. Status ${response.status} ${response.statusText}}`,
@@ -43,9 +43,11 @@ async function downloadTarball(url, storageDir) {
43
43
  })
44
44
  );
45
45
  }
46
- async function getLatestTemplates() {
46
+ async function getLatestTemplates({
47
+ signal
48
+ } = {}) {
47
49
  try {
48
- const { version, url } = await getLatestReleaseDownloadUrl();
50
+ const { version, url } = await getLatestReleaseDownloadUrl(signal);
49
51
  const templateStoragePath = fileURLToPath(
50
52
  new URL("../starter-templates", import.meta.url)
51
53
  );
@@ -54,7 +56,7 @@ async function getLatestTemplates() {
54
56
  }
55
57
  const templateStorageVersionPath = path.join(templateStoragePath, version);
56
58
  if (!await fileExists(templateStorageVersionPath)) {
57
- await downloadTarball(url, templateStorageVersionPath);
59
+ await downloadTarball(url, templateStorageVersionPath, signal);
58
60
  }
59
61
  return {
60
62
  version,
@@ -1,9 +1,9 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs/promises';
3
- import prettier from 'prettier';
4
3
  import ts from 'typescript';
5
4
  import glob from 'fast-glob';
6
5
  import { outputDebug } from '@shopify/cli-kit/node/output';
6
+ import { getCodeFormatOptions, formatCode } from './format-code.js';
7
7
 
8
8
  const escapeNewLines = (code) => code.replace(/\n\n/g, "\n/* :newline: */");
9
9
  const restoreNewLines = (code) => code.replace(/\/\* :newline: \*\//g, "\n");
@@ -30,27 +30,6 @@ function transpileFile(code, config = DEFAULT_TS_CONFIG) {
30
30
  });
31
31
  return restoreNewLines(compiled.outputText);
32
32
  }
33
- const DEFAULT_PRETTIER_CONFIG = {
34
- arrowParens: "always",
35
- singleQuote: true,
36
- bracketSpacing: false,
37
- trailingComma: "all"
38
- };
39
- async function resolveFormatConfig(filePath = process.cwd()) {
40
- try {
41
- return await prettier.resolveConfig(filePath) || DEFAULT_PRETTIER_CONFIG;
42
- } catch {
43
- return DEFAULT_PRETTIER_CONFIG;
44
- }
45
- }
46
- function format(content, config, filePath = "") {
47
- const ext = path.extname(filePath);
48
- const formattedContent = prettier.format(content, {
49
- parser: ext === ".tsx" || ext === ".ts" ? "typescript" : "babel",
50
- ...config
51
- });
52
- return formattedContent;
53
- }
54
33
  const DEFAULT_JS_CONFIG = {
55
34
  allowJs: true,
56
35
  forceConsistentCasingInFileNames: true,
@@ -95,14 +74,14 @@ async function transpileProject(projectDir) {
95
74
  absolute: true,
96
75
  cwd: projectDir
97
76
  });
98
- const formatConfig = await resolveFormatConfig();
77
+ const formatConfig = await getCodeFormatOptions();
99
78
  for (const entry of entries) {
100
79
  if (entry.endsWith(".d.ts")) {
101
80
  await fs.rm(entry);
102
81
  continue;
103
82
  }
104
83
  const tsx = await fs.readFile(entry, "utf8");
105
- const mjs = format(transpileFile(tsx), formatConfig);
84
+ const mjs = formatCode(transpileFile(tsx), formatConfig);
106
85
  await fs.rm(entry);
107
86
  await fs.writeFile(entry.replace(/\.ts(x?)$/, ".js$1"), mjs, "utf8");
108
87
  }
@@ -145,11 +124,12 @@ async function transpileProject(projectDir) {
145
124
  delete pkgJson.devDependencies[key];
146
125
  }
147
126
  }
127
+ const codegenFlag = /\s*--codegen(-unstable)?/;
148
128
  if (pkgJson.scripts?.dev) {
149
- pkgJson.scripts.dev = pkgJson.scripts.dev.replace(
150
- /\s*--codegen(-unstable)?/,
151
- ""
152
- );
129
+ pkgJson.scripts.dev = pkgJson.scripts.dev.replace(codegenFlag, "");
130
+ }
131
+ if (pkgJson.scripts?.build) {
132
+ pkgJson.scripts.build = pkgJson.scripts.build.replace(codegenFlag, "");
153
133
  }
154
134
  await fs.writeFile(
155
135
  path.join(projectDir, "package.json"),
@@ -172,4 +152,4 @@ async function transpileProject(projectDir) {
172
152
  }
173
153
  }
174
154
 
175
- export { convertConfigToJS, format, resolveFormatConfig, transpileFile, transpileProject };
155
+ export { convertConfigToJS, transpileFile, transpileProject };
@@ -30,53 +30,74 @@ const links = () => [
30
30
  crossOrigin: "anonymous"
31
31
  }
32
32
  ];
33
- async function loader({ context }) {
34
- const layout = await context.storefront.query(LAYOUT_QUERY);
35
- return { layout };
33
+ async function loader({
34
+ context: { storefront }
35
+ }) {
36
+ const layout = await storefront.query(LAYOUT_QUERY);
37
+ return { layout, isMockShop: storefront.getApiUrl().includes("mock.shop") };
36
38
  }
37
39
  const HYDROGEN_SHOP_ID = "gid://shopify/Shop/55145660472";
38
40
  function ErrorBoundary() {
39
41
  return <ErrorPage />;
40
42
  }
41
43
  function Index() {
42
- const data = useLoaderData();
43
- const { name: shopName, id: shopId } = data.layout.shop;
44
- const configDone = shopId !== HYDROGEN_SHOP_ID;
44
+ const {
45
+ isMockShop,
46
+ layout: { shop }
47
+ } = useLoaderData();
48
+ let { name: shopName, id: shopId } = shop;
49
+ const configDone = shopId !== HYDROGEN_SHOP_ID && !isMockShop;
50
+ if (isMockShop || !shopName)
51
+ shopName = "Hydrogen";
45
52
  return <><Layout shopName={shopName}>
46
53
  {configDone ? <HydrogenLogoBaseColor /> : <HydrogenLogoBaseBW />}
47
54
  <h1>
48
55
  {"Hello, "}
49
- {shopName || "Hydrogen"}
56
+ {shopName}
50
57
  </h1>
51
58
  <p>Welcome to your new custom storefront</p>
52
- {configDone ? null : <section className="Banner">
59
+ <section className="Banner">
53
60
  <div>
54
61
  <IconBanner />
55
- <h2>Configure storefront token</h2>
62
+ <h2>{configDone ? "Create your first route" : "Configure storefront token"}</h2>
56
63
  </div>
57
- <p>
64
+ {configDone ? <p>
65
+ {"You\u2019re seeing this because you don\u2019t have a home route in your project yet. "}
66
+ <br />
67
+ {"Run "}
68
+ <code>h2 generate route home</code>
69
+ {" to create your home route. Learn more about"}
70
+ {` `}
71
+ <CreateRoutesLink />
72
+ </p> : <p>
58
73
  {"You\u2019re seeing this because you have not yet configured your storefront token. "}
59
74
  <br />
60
75
  <br />
61
- {" To get started, edit "}
62
- {` `}
63
- <code>.env</code>
64
- {". Then, create your first route with the file"}
76
+ {" To link your store,"}
65
77
  {` `}
66
- <code>/app/routes/_index.jsx</code>
67
- {". Learn more about"}
78
+ {"run "}
79
+ <code>{"h2 link && h2 env pull"}</code>
80
+ {". Then, run"}
81
+ {" "}
82
+ <code>h2 generate route home</code>
83
+ {" to create your first route."}
84
+ <br />
85
+ {"Learn more about"}
68
86
  {` `}
69
87
  <a target="_blank" rel="norefferer noopener" href="https://shopify.dev/docs/custom-storefronts/hydrogen/environment-variables">editing environment variables</a>
70
88
  {` `}
71
89
  {"and"}
72
90
  {` `}
73
- <a target="_blank" rel="norefferer noopener" href="https://shopify.dev/docs/custom-storefronts/hydrogen/building/begin-development#step-4-create-a-route">creating routes</a>
91
+ <CreateRoutesLink />
74
92
  {"."}
75
- </p>
76
- </section>}
93
+ </p>}
94
+ </section>
77
95
  <ResourcesLinks />
78
96
  </Layout></>;
79
97
  }
98
+ function CreateRoutesLink() {
99
+ return <a target="_blank" rel="norefferer noopener" href="https://shopify.dev/docs/custom-storefronts/hydrogen/building/begin-development#step-4-create-a-route">creating routes</a>;
100
+ }
80
101
  function ErrorPage() {
81
102
  return <><Layout shopName="Hydrogen">
82
103
  <HydrogenLogoBaseBW />