@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,126 +1,378 @@
1
- import { describe, beforeEach, vi, it, expect } from 'vitest';
1
+ import { fileURLToPath } from 'node:url';
2
+ import { vi, describe, beforeEach, it, expect } from 'vitest';
2
3
  import { temporaryDirectoryTask } from 'tempy';
3
4
  import { runInit } from './init.js';
4
- import { renderSelectPrompt, renderConfirmationPrompt, renderTextPrompt, renderInfo } from '@shopify/cli-kit/node/ui';
5
- import { outputContent } from '@shopify/cli-kit/node/output';
6
- import { installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
5
+ import { exec } from '@shopify/cli-kit/node/system';
6
+ import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
7
+ import { readFile, writeFile, isDirectory } from '@shopify/cli-kit/node/fs';
8
+ import { basename, joinPath } from '@shopify/cli-kit/node/path';
9
+ import { checkHydrogenVersion } from '../../lib/check-version.js';
10
+ import { handleProjectLocation } from '../../lib/onboarding/common.js';
11
+ import glob from 'fast-glob';
12
+ import { getSkeletonSourceDir } from '../../lib/build.js';
13
+ import { execAsync } from '../../lib/process.js';
14
+ import { rmdir, symlink } from 'fs-extra';
7
15
 
16
+ vi.mock("../../lib/template-downloader.js", async () => ({
17
+ getLatestTemplates: () => Promise.resolve({})
18
+ }));
19
+ vi.mock(
20
+ "@shopify/cli-kit/node/node-package-manager",
21
+ async (importOriginal) => {
22
+ const original = await importOriginal();
23
+ return {
24
+ ...original,
25
+ installNodeModules: vi.fn(),
26
+ getPackageManager: () => Promise.resolve("npm"),
27
+ packageManagerUsedForCreating: () => Promise.resolve("npm")
28
+ };
29
+ }
30
+ );
31
+ vi.mock("../../lib/check-version.js");
32
+ const { renderTasksHook } = vi.hoisted(() => {
33
+ return {
34
+ renderTasksHook: vi.fn()
35
+ };
36
+ });
37
+ vi.mock("@shopify/cli-kit/node/ui", async () => {
38
+ const original = await vi.importActual("@shopify/cli-kit/node/ui");
39
+ return {
40
+ ...original,
41
+ renderConfirmationPrompt: vi.fn(),
42
+ renderSelectPrompt: vi.fn(),
43
+ renderTextPrompt: vi.fn(),
44
+ renderInfo: vi.fn(),
45
+ renderTasks: vi.fn(async (args) => {
46
+ await original.renderTasks(args);
47
+ renderTasksHook();
48
+ })
49
+ };
50
+ });
51
+ vi.mock("../../lib/onboarding/common.js", async (importOriginal) => {
52
+ const original = await importOriginal();
53
+ return Object.keys(original).reduce((acc, item) => {
54
+ const key = item;
55
+ const value = original[key];
56
+ if (typeof value === "function") {
57
+ acc[key] = vi.fn(value);
58
+ } else {
59
+ acc[key] = value;
60
+ }
61
+ return acc;
62
+ }, {});
63
+ });
8
64
  describe("init", () => {
65
+ const outputMock = mockAndCaptureOutput();
9
66
  beforeEach(() => {
10
- vi.resetAllMocks();
11
- vi.mock("@shopify/cli-kit/node/output");
12
- vi.mock("../../lib/transpile-ts.js");
13
- vi.mock("../../lib/template-downloader.js", async () => ({
14
- getLatestTemplates: () => Promise.resolve({})
15
- }));
16
- vi.mock("@shopify/cli-kit/node/node-package-manager");
17
- vi.mocked(outputContent).mockImplementation(() => ({
18
- value: ""
19
- }));
20
- vi.mock("@shopify/cli-kit/node/ui");
21
- vi.mock("@shopify/cli-kit/node/fs");
67
+ vi.clearAllMocks();
68
+ outputMock.clear();
22
69
  });
23
- const defaultOptions = (stubs) => ({
24
- template: "hello-world",
25
- language: "js",
26
- path: "path/to/project",
27
- ...stubs
70
+ it("checks Hydrogen version", async () => {
71
+ await temporaryDirectoryTask(async (tmpDir) => {
72
+ const showUpgradeMock = vi.fn((param) => ({
73
+ currentVersion: "1.0.0",
74
+ newVersion: "1.0.1"
75
+ }));
76
+ vi.mocked(checkHydrogenVersion).mockResolvedValueOnce(showUpgradeMock);
77
+ vi.mocked(handleProjectLocation).mockResolvedValueOnce(void 0);
78
+ const project = await runInit({ path: tmpDir, git: false });
79
+ expect(project).toBeFalsy();
80
+ expect(checkHydrogenVersion).toHaveBeenCalledOnce();
81
+ expect(showUpgradeMock).toHaveBeenCalledWith(
82
+ expect.stringContaining("npm create @shopify/hydrogen@latest")
83
+ );
84
+ });
28
85
  });
29
- describe.each([
30
- {
31
- flag: "template",
32
- value: "hello-world",
33
- condition: { fn: renderSelectPrompt, match: /template/i }
34
- },
35
- {
36
- flag: "installDeps",
37
- value: true,
38
- condition: { fn: renderConfirmationPrompt, match: /install dependencies/i }
39
- },
40
- {
41
- flag: "language",
42
- value: "ts",
43
- condition: { fn: renderSelectPrompt, match: /language/i }
44
- },
45
- {
46
- flag: "path",
47
- value: "./my-app",
48
- condition: { fn: renderTextPrompt, match: /where/i }
49
- }
50
- ])("flag $flag", ({ flag, value, condition }) => {
51
- it(`does not prompt the user for ${flag} when a value is passed in options`, async () => {
86
+ describe("local templates", () => {
87
+ it("creates basic projects", async () => {
52
88
  await temporaryDirectoryTask(async (tmpDir) => {
53
- const options = defaultOptions({
89
+ await runInit({
54
90
  path: tmpDir,
55
- [flag]: value
91
+ git: false,
92
+ language: "ts",
93
+ mockShop: true
94
+ });
95
+ const skeletonFiles = await glob("**/*", {
96
+ cwd: getSkeletonSourceDir(),
97
+ ignore: ["**/node_modules/**", "**/dist/**"]
56
98
  });
57
- await runInit(options);
58
- expect(condition.fn).not.toHaveBeenCalledWith(
59
- expect.objectContaining({
60
- message: expect.stringMatching(condition.match)
61
- })
99
+ const projectFiles = await glob("**/*", { cwd: tmpDir });
100
+ const nonAppFiles = skeletonFiles.filter(
101
+ (item) => !item.startsWith("app/")
102
+ );
103
+ expect(projectFiles).toEqual(expect.arrayContaining(nonAppFiles));
104
+ expect(projectFiles).toContain("app/root.tsx");
105
+ expect(projectFiles).toContain("app/entry.client.tsx");
106
+ expect(projectFiles).toContain("app/entry.server.tsx");
107
+ expect(projectFiles).toContain("app/components/Layout.tsx");
108
+ expect(projectFiles).not.toContain("app/routes/_index.tsx");
109
+ await expect(readFile(`${tmpDir}/server.ts`)).resolves.toEqual(
110
+ await readFile(`${getSkeletonSourceDir()}/server.ts`)
111
+ );
112
+ await expect(readFile(`${tmpDir}/package.json`)).resolves.toMatch(
113
+ `"name": "${basename(tmpDir)}"`
114
+ );
115
+ await expect(readFile(`${tmpDir}/.env`)).resolves.toMatch(
116
+ `PUBLIC_STORE_DOMAIN="mock.shop"`
117
+ );
118
+ const output = outputMock.info();
119
+ expect(output).toMatch("success");
120
+ expect(output).not.toMatch("warning");
121
+ expect(output).toMatch(basename(tmpDir));
122
+ expect(output).not.toMatch("Routes");
123
+ expect(output).toMatch(/Language:\s*TypeScript/);
124
+ expect(output).toMatch("Help");
125
+ expect(output).toMatch("Next steps");
126
+ expect(output).toMatch(
127
+ /Run `cd .*? &&[^\w]*?npm[^\w]*?install[^\w]*?&&[^\w]*?npm[^\w]*?run[^\w]*?dev`/ims
62
128
  );
63
129
  });
64
130
  });
65
- it(`prompts the user for ${flag} when no value is passed in options`, async () => {
131
+ it("creates projects with route files", async () => {
66
132
  await temporaryDirectoryTask(async (tmpDir) => {
67
- const options = defaultOptions({
68
- path: tmpDir,
69
- [flag]: void 0
133
+ await runInit({ path: tmpDir, git: false, routes: true, language: "ts" });
134
+ const skeletonFiles = await glob("**/*", {
135
+ cwd: getSkeletonSourceDir(),
136
+ ignore: ["**/node_modules/**", "**/dist/**"]
70
137
  });
71
- await runInit(options);
72
- expect(condition.fn).toHaveBeenCalledWith(
73
- expect.objectContaining({
74
- message: expect.stringMatching(condition.match)
75
- })
138
+ const projectFiles = await glob("**/*", { cwd: tmpDir });
139
+ expect(projectFiles).toEqual(expect.arrayContaining(skeletonFiles));
140
+ expect(projectFiles).toContain("app/routes/_index.tsx");
141
+ await expect(readFile(`${tmpDir}/server.ts`)).resolves.toEqual(
142
+ await readFile(`${getSkeletonSourceDir()}/server.ts`)
76
143
  );
144
+ const output = outputMock.info();
145
+ expect(output).toMatch("success");
146
+ expect(output).not.toMatch("warning");
147
+ expect(output).toMatch(basename(tmpDir));
148
+ expect(output).toMatch(/Language:\s*TypeScript/);
149
+ expect(output).toMatch("Routes");
150
+ expect(output).toMatch("Home (/ & /:catchAll)");
151
+ expect(output).toMatch("Account (/account/*)");
77
152
  });
78
153
  });
79
- });
80
- it("installs dependencies when installDeps is true", async () => {
81
- await temporaryDirectoryTask(async (tmpDir) => {
82
- const options = defaultOptions({ installDeps: true, path: tmpDir });
83
- await runInit(options);
84
- expect(installNodeModules).toHaveBeenCalled();
154
+ it("transpiles projects to JS", async () => {
155
+ await temporaryDirectoryTask(async (tmpDir) => {
156
+ await runInit({ path: tmpDir, git: false, routes: true, language: "js" });
157
+ const skeletonFiles = await glob("**/*", {
158
+ cwd: getSkeletonSourceDir(),
159
+ ignore: ["**/node_modules/**", "**/dist/**"]
160
+ });
161
+ const projectFiles = await glob("**/*", { cwd: tmpDir });
162
+ expect(projectFiles).toEqual(
163
+ expect.arrayContaining(
164
+ skeletonFiles.filter((item) => !item.endsWith(".d.ts")).map(
165
+ (item) => item.replace(/\.ts(x)?$/, ".js$1").replace(/tsconfig\.json$/, "jsconfig.json")
166
+ )
167
+ )
168
+ );
169
+ expect(projectFiles).toContain("app/routes/_index.jsx");
170
+ await expect(readFile(`${tmpDir}/server.js`)).resolves.toMatch(
171
+ /export default {\n\s+async fetch\(\s*request,\s*env,\s*executionContext,?\s*\)/
172
+ );
173
+ const output = outputMock.info();
174
+ expect(output).toMatch("success");
175
+ expect(output).not.toMatch("warning");
176
+ expect(output).toMatch(basename(tmpDir));
177
+ expect(output).toMatch(/Language:\s*JavaScript/);
178
+ expect(output).toMatch("Routes");
179
+ expect(output).toMatch("Home (/ & /:catchAll)");
180
+ expect(output).toMatch("Account (/account/*)");
181
+ });
85
182
  });
86
- });
87
- it("does not install dependencies when installDeps is false", async () => {
88
- await temporaryDirectoryTask(async (tmpDir) => {
89
- const options = defaultOptions({ installDeps: false, path: tmpDir });
90
- await runInit(options);
91
- expect(installNodeModules).not.toHaveBeenCalled();
183
+ describe("styling libraries", () => {
184
+ it("scaffolds Tailwind CSS", async () => {
185
+ await temporaryDirectoryTask(async (tmpDir) => {
186
+ await runInit({
187
+ path: tmpDir,
188
+ git: false,
189
+ language: "ts",
190
+ styling: "tailwind"
191
+ });
192
+ await expect(readFile(`${tmpDir}/remix.config.js`)).resolves.toMatch(
193
+ /tailwind: true,\n\s*postcss: true,\n\s*future:/
194
+ );
195
+ await expect(
196
+ readFile(`${tmpDir}/app/styles/tailwind.css`)
197
+ ).resolves.toMatch(/@tailwind base;/);
198
+ const rootFile = await readFile(`${tmpDir}/app/root.tsx`);
199
+ await expect(rootFile).toMatch(/import tailwindCss from/);
200
+ await expect(rootFile).toMatch(
201
+ /export function links\(\) \{.*?return \[.*\{rel: 'stylesheet', href: tailwindCss\}/ims
202
+ );
203
+ const output = outputMock.info();
204
+ expect(output).toMatch("success");
205
+ expect(output).not.toMatch("warning");
206
+ expect(output).toMatch(/Styling:\s*Tailwind/);
207
+ });
208
+ });
209
+ it("scaffolds CSS Modules", async () => {
210
+ await temporaryDirectoryTask(async (tmpDir) => {
211
+ await runInit({
212
+ path: tmpDir,
213
+ git: false,
214
+ language: "ts",
215
+ styling: "css-modules"
216
+ });
217
+ await expect(readFile(`${tmpDir}/package.json`)).resolves.toMatch(
218
+ `"@remix-run/css-bundle": "`
219
+ );
220
+ const rootFile = await readFile(`${tmpDir}/app/root.tsx`);
221
+ await expect(rootFile).toMatch(/import {cssBundleHref} from/);
222
+ await expect(rootFile).toMatch(
223
+ /export function links\(\) \{.*?return \[.*\{rel: 'stylesheet', href: cssBundleHref\}/ims
224
+ );
225
+ const output = outputMock.info();
226
+ expect(output).toMatch("success");
227
+ expect(output).not.toMatch("warning");
228
+ expect(output).toMatch(/Styling:\s*CSS Modules/);
229
+ });
230
+ });
231
+ it("scaffolds Vanilla Extract", async () => {
232
+ await temporaryDirectoryTask(async (tmpDir) => {
233
+ await runInit({
234
+ path: tmpDir,
235
+ git: false,
236
+ language: "ts",
237
+ styling: "vanilla-extract"
238
+ });
239
+ const packageJson = await readFile(`${tmpDir}/package.json`);
240
+ expect(packageJson).toMatch(/"@remix-run\/css-bundle": "/);
241
+ expect(packageJson).toMatch(/"@vanilla-extract\/css": "/);
242
+ const rootFile = await readFile(`${tmpDir}/app/root.tsx`);
243
+ await expect(rootFile).toMatch(/import {cssBundleHref} from/);
244
+ await expect(rootFile).toMatch(
245
+ /export function links\(\) \{.*?return \[.*\{rel: 'stylesheet', href: cssBundleHref\}/ims
246
+ );
247
+ const output = outputMock.info();
248
+ expect(output).toMatch("success");
249
+ expect(output).not.toMatch("warning");
250
+ expect(output).toMatch(/Styling:\s*Vanilla Extract/);
251
+ });
252
+ });
92
253
  });
93
- });
94
- it("displays inventory information when using the demo-store template", async () => {
95
- await temporaryDirectoryTask(async (tmpDir) => {
96
- const options = defaultOptions({
97
- installDeps: false,
98
- path: tmpDir,
99
- template: "demo-store"
254
+ describe("i18n strategies", () => {
255
+ it("scaffolds i18n with domains strategy", async () => {
256
+ await temporaryDirectoryTask(async (tmpDir) => {
257
+ await runInit({
258
+ path: tmpDir,
259
+ git: false,
260
+ language: "ts",
261
+ i18n: "domains",
262
+ routes: true
263
+ });
264
+ const projectFiles = await glob("**/*", { cwd: tmpDir });
265
+ expect(projectFiles).toContain("app/routes/_index.tsx");
266
+ const serverFile = await readFile(`${tmpDir}/server.ts`);
267
+ expect(serverFile).toMatch(/i18n: getLocaleFromRequest\(request\),/);
268
+ expect(serverFile).toMatch(/domain = url.hostname/);
269
+ const output = outputMock.info();
270
+ expect(output).toMatch("success");
271
+ expect(output).not.toMatch("warning");
272
+ expect(output).toMatch(/Markets:\s*Top-level domains/);
273
+ });
274
+ });
275
+ it("scaffolds i18n with subdomains strategy", async () => {
276
+ await temporaryDirectoryTask(async (tmpDir) => {
277
+ await runInit({
278
+ path: tmpDir,
279
+ git: false,
280
+ language: "ts",
281
+ i18n: "subdomains",
282
+ routes: true
283
+ });
284
+ const projectFiles = await glob("**/*", { cwd: tmpDir });
285
+ expect(projectFiles).toContain("app/routes/_index.tsx");
286
+ const serverFile = await readFile(`${tmpDir}/server.ts`);
287
+ expect(serverFile).toMatch(/i18n: getLocaleFromRequest\(request\),/);
288
+ expect(serverFile).toMatch(/firstSubdomain = url.hostname/);
289
+ const output = outputMock.info();
290
+ expect(output).toMatch("success");
291
+ expect(output).not.toMatch("warning");
292
+ expect(output).toMatch(/Markets:\s*Subdomains/);
293
+ });
294
+ });
295
+ it("scaffolds i18n with subfolders strategy", async () => {
296
+ await temporaryDirectoryTask(async (tmpDir) => {
297
+ await runInit({
298
+ path: tmpDir,
299
+ git: false,
300
+ language: "ts",
301
+ i18n: "subfolders",
302
+ routes: true
303
+ });
304
+ const projectFiles = await glob("**/*", { cwd: tmpDir });
305
+ expect(projectFiles).toContain("app/routes/($locale)._index.tsx");
306
+ const serverFile = await readFile(`${tmpDir}/server.ts`);
307
+ expect(serverFile).toMatch(/i18n: getLocaleFromRequest\(request\),/);
308
+ expect(serverFile).toMatch(/url.pathname/);
309
+ const output = outputMock.info();
310
+ expect(output).toMatch("success");
311
+ expect(output).not.toMatch("warning");
312
+ expect(output).toMatch(/Markets:\s*Subfolders/);
313
+ });
100
314
  });
101
- await runInit(options);
102
- expect(renderInfo).toHaveBeenCalledTimes(1);
103
- expect(renderInfo).toHaveBeenCalledWith(
104
- expect.objectContaining({
105
- body: expect.stringContaining(
106
- "To connect this project to your Shopify store\u2019s inventory"
107
- ),
108
- headline: expect.stringContaining(
109
- "Your project will display inventory from the Hydrogen Demo Store"
110
- )
111
- })
112
- );
113
315
  });
114
- });
115
- it("does not display inventory information when using non-demo-store templates", async () => {
116
- await temporaryDirectoryTask(async (tmpDir) => {
117
- const options = defaultOptions({
118
- installDeps: false,
119
- path: tmpDir,
120
- template: "pizza-store"
316
+ describe("git", () => {
317
+ it("initializes a git repository and creates initial commits", async () => {
318
+ await temporaryDirectoryTask(async (tmpDir) => {
319
+ renderTasksHook.mockImplementationOnce(async () => {
320
+ await writeFile(`${tmpDir}/package-lock.json`, "{}");
321
+ });
322
+ await runInit({
323
+ path: tmpDir,
324
+ git: true,
325
+ language: "js",
326
+ styling: "tailwind",
327
+ i18n: "domains",
328
+ routes: true,
329
+ installDeps: true
330
+ });
331
+ expect(isDirectory(`${tmpDir}/.git`)).resolves.toBeTruthy();
332
+ const { stdout: gitLog } = await execAsync(`git log --oneline`, {
333
+ cwd: tmpDir
334
+ });
335
+ expect(gitLog.split("\n")).toEqual(
336
+ expect.arrayContaining([
337
+ expect.stringContaining("Lockfile"),
338
+ expect.stringContaining("Generate routes for core functionality"),
339
+ expect.stringContaining("Setup Tailwind"),
340
+ expect.stringContaining("Scaffold Storefront")
341
+ ])
342
+ );
343
+ });
344
+ });
345
+ });
346
+ describe("project validity", () => {
347
+ it("typechecks the project", async () => {
348
+ await temporaryDirectoryTask(async (tmpDir) => {
349
+ renderTasksHook.mockImplementationOnce(async () => {
350
+ await writeFile(`${tmpDir}/package-lock.json`, "{}");
351
+ });
352
+ await runInit({
353
+ path: tmpDir,
354
+ git: true,
355
+ language: "ts",
356
+ styling: "tailwind",
357
+ i18n: "subfolders",
358
+ routes: true,
359
+ installDeps: true
360
+ });
361
+ await rmdir(joinPath(tmpDir, "node_modules")).catch(() => {
362
+ });
363
+ await symlink(
364
+ fileURLToPath(
365
+ new URL("../../../../../node_modules", import.meta.url)
366
+ ),
367
+ joinPath(tmpDir, "node_modules")
368
+ );
369
+ await expect(
370
+ exec("npm", ["run", "typecheck"], {
371
+ cwd: tmpDir
372
+ })
373
+ ).resolves.not.toThrow();
374
+ });
121
375
  });
122
- await runInit(options);
123
- expect(renderInfo).toHaveBeenCalledTimes(0);
124
376
  });
125
377
  });
126
378
  });