@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,83 +1,86 @@
1
1
  import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
2
2
  import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
3
3
  import { getStorefrontsWithDeployment } from '../../lib/graphql/admin/list-storefronts.js';
4
- import { listStorefronts, formatDeployment } from './list.js';
4
+ import { runList, formatDeployment } from './list.js';
5
+ import { login } from '../../lib/auth.js';
5
6
 
6
- const SHOP_NAME = "my-shop";
7
- vi.mock("../../lib/graphql/admin/list-storefronts.js", async () => {
8
- return { getStorefrontsWithDeployment: vi.fn() };
9
- });
10
- vi.mock("../../lib/shop.js", () => ({
11
- getHydrogenShop: () => SHOP_NAME
12
- }));
7
+ vi.mock("../../lib/auth.js");
8
+ vi.mock("../../lib/graphql/admin/list-storefronts.js");
13
9
  describe("list", () => {
14
10
  const ADMIN_SESSION = {
15
11
  token: "abc123",
16
- storeFqdn: SHOP_NAME
12
+ storeFqdn: "my-shop"
13
+ };
14
+ const SHOPIFY_CONFIG = {
15
+ shop: "my-shop.myshopify.com",
16
+ shopName: "My Shop",
17
+ email: "email",
18
+ storefront: {
19
+ id: "gid://shopify/HydrogenStorefront/1",
20
+ title: "Hydrogen"
21
+ }
17
22
  };
18
23
  beforeEach(async () => {
19
- vi.mocked(getStorefrontsWithDeployment).mockResolvedValue({
20
- adminSession: ADMIN_SESSION,
21
- storefronts: []
24
+ vi.mocked(login).mockResolvedValue({
25
+ session: ADMIN_SESSION,
26
+ config: SHOPIFY_CONFIG
22
27
  });
28
+ vi.mocked(getStorefrontsWithDeployment).mockResolvedValue([]);
23
29
  });
24
30
  afterEach(() => {
25
31
  vi.resetAllMocks();
26
32
  mockAndCaptureOutput().clear();
27
33
  });
28
- it("makes a GraphQL call to fetch the storefronts", async () => {
29
- await listStorefronts({});
30
- expect(getStorefrontsWithDeployment).toHaveBeenCalledWith(SHOP_NAME);
34
+ it("fetches the storefronts", async () => {
35
+ await runList({});
36
+ expect(getStorefrontsWithDeployment).toHaveBeenCalledWith(ADMIN_SESSION);
31
37
  });
32
38
  describe("and there are storefronts", () => {
33
39
  beforeEach(() => {
34
- vi.mocked(getStorefrontsWithDeployment).mockResolvedValue({
35
- adminSession: ADMIN_SESSION,
36
- storefronts: [
37
- {
38
- id: "gid://shopify/HydrogenStorefront/1",
39
- parsedId: "1",
40
- title: "Hydrogen",
41
- productionUrl: "https://example.com",
42
- currentProductionDeployment: null
43
- },
44
- {
45
- id: "gid://shopify/HydrogenStorefront/2",
46
- parsedId: "2",
47
- title: "Demo Store",
48
- productionUrl: "https://demo.example.com",
49
- currentProductionDeployment: {
50
- id: "gid://shopify/HydrogenStorefrontDeployment/1",
51
- createdAt: "2023-03-22T22:28:38Z",
52
- commitMessage: "Update README.md"
53
- }
40
+ vi.mocked(getStorefrontsWithDeployment).mockResolvedValue([
41
+ {
42
+ id: "gid://shopify/HydrogenStorefront/1",
43
+ parsedId: "1",
44
+ title: "Hydrogen",
45
+ productionUrl: "https://example.com",
46
+ currentProductionDeployment: null
47
+ },
48
+ {
49
+ id: "gid://shopify/HydrogenStorefront/2",
50
+ parsedId: "2",
51
+ title: "Demo Store",
52
+ productionUrl: "https://demo.example.com",
53
+ currentProductionDeployment: {
54
+ id: "gid://shopify/HydrogenStorefrontDeployment/1",
55
+ createdAt: "2023-03-22T22:28:38Z",
56
+ commitMessage: "Update README.md"
54
57
  }
55
- ]
56
- });
58
+ }
59
+ ]);
57
60
  });
58
61
  it("renders a list of storefronts", async () => {
59
62
  const outputMock = mockAndCaptureOutput();
60
- await listStorefronts({});
63
+ await runList({});
61
64
  expect(outputMock.info()).toMatch(
62
- /Showing 2 Hydrogen storefronts for the store my-shop/g
65
+ /Showing 2 Hydrogen storefronts for the store my-shop/i
63
66
  );
64
- expect(outputMock.info()).toMatch(/Hydrogen \(id: 1\)/g);
65
- expect(outputMock.info()).toMatch(/https:\/\/example.com/g);
66
- expect(outputMock.info()).toMatch(/Demo Store \(id: 2\)/g);
67
- expect(outputMock.info()).toMatch(/https:\/\/demo.example.com/g);
68
- expect(outputMock.info()).toMatch(/3\/22\/2023, Update README.md/g);
67
+ expect(outputMock.info()).toMatch(/Hydrogen \(id: 1\)/);
68
+ expect(outputMock.info()).toMatch(/https:\/\/example.com/);
69
+ expect(outputMock.info()).toMatch(/Demo Store \(id: 2\)/);
70
+ expect(outputMock.info()).toMatch(/https:\/\/demo.example.com/);
71
+ expect(outputMock.info()).toMatch(/3\/22\/2023, Update README.md/);
69
72
  });
70
73
  });
71
74
  describe("and there are no storefronts", () => {
72
75
  it("prompts the user to create a storefront", async () => {
73
76
  const outputMock = mockAndCaptureOutput();
74
- await listStorefronts({});
77
+ await runList({});
75
78
  expect(outputMock.info()).toMatch(
76
- /There are no Hydrogen storefronts on your Shop\./g
79
+ /There are no Hydrogen storefronts on your Shop\./i
77
80
  );
78
- expect(outputMock.info()).toMatch(/Create a new Hydrogen storefront/g);
81
+ expect(outputMock.info()).toMatch(/Create a new Hydrogen storefront/i);
79
82
  expect(outputMock.info()).toMatch(
80
- /https:\/\/my\-shop\/admin\/custom_storefronts\/new/g
83
+ /https:\/\/my\-shop\/admin\/custom_storefronts\/new/
81
84
  );
82
85
  });
83
86
  });
@@ -0,0 +1,31 @@
1
+ import { Flags } from '@oclif/core';
2
+ import Command from '@shopify/cli-kit/node/base-command';
3
+ import { normalizeStoreFqdn } from '@shopify/cli-kit/node/context/fqdn';
4
+ import { commonFlags } from '../../lib/flags.js';
5
+ import { login, renderLoginSuccess } from '../../lib/auth.js';
6
+
7
+ class Login extends Command {
8
+ static description = "Login to your Shopify account.";
9
+ static flags = {
10
+ path: commonFlags.path,
11
+ shop: Flags.string({
12
+ char: "s",
13
+ description: "Shop URL. It can be the shop prefix (janes-apparel) or the full myshopify.com URL (janes-apparel.myshopify.com, https://janes-apparel.myshopify.com).",
14
+ env: "SHOPIFY_SHOP",
15
+ parse: async (input) => normalizeStoreFqdn(input)
16
+ })
17
+ };
18
+ async run() {
19
+ const { flags } = await this.parse(Login);
20
+ await runLogin(flags);
21
+ }
22
+ }
23
+ async function runLogin({
24
+ path: root = process.cwd(),
25
+ shop: shopFlag
26
+ }) {
27
+ const { config } = await login(root, shopFlag ?? true);
28
+ renderLoginSuccess(config);
29
+ }
30
+
31
+ export { Login as default };
@@ -0,0 +1,21 @@
1
+ import Command from '@shopify/cli-kit/node/base-command';
2
+ import { renderSuccess } from '@shopify/cli-kit/node/ui';
3
+ import { commonFlags } from '../../lib/flags.js';
4
+ import { logout } from '../../lib/auth.js';
5
+
6
+ class Logout extends Command {
7
+ static description = "Logout of your local session.";
8
+ static flags = {
9
+ path: commonFlags.path
10
+ };
11
+ async run() {
12
+ const { flags } = await this.parse(Logout);
13
+ await runLogout(flags);
14
+ }
15
+ }
16
+ async function runLogout({ path: root = process.cwd() }) {
17
+ await logout(root);
18
+ renderSuccess({ body: "You are logged out from Shopify." });
19
+ }
20
+
21
+ export { Logout as default };
@@ -0,0 +1,79 @@
1
+ import { resolvePath } from '@shopify/cli-kit/node/path';
2
+ import { commonFlags, overrideFlag, flagsToCamelObject } from '../../../lib/flags.js';
3
+ import Command from '@shopify/cli-kit/node/base-command';
4
+ import { renderTasks, renderSuccess } from '@shopify/cli-kit/node/ui';
5
+ import { getPackageManager, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
6
+ import { Args } from '@oclif/core';
7
+ import { getRemixConfig } from '../../../lib/config.js';
8
+ import { SETUP_CSS_STRATEGIES, renderCssPrompt, setupCssStrategy, CSS_STRATEGY_NAME_MAP } from '../../../lib/setups/css/index.js';
9
+
10
+ class SetupCSS extends Command {
11
+ static description = "Setup CSS strategies for your project.";
12
+ static flags = {
13
+ path: commonFlags.path,
14
+ force: commonFlags.force,
15
+ "install-deps": overrideFlag(commonFlags.installDeps, { default: true })
16
+ };
17
+ static args = {
18
+ strategy: Args.string({
19
+ name: "strategy",
20
+ description: `The CSS strategy to setup. One of ${SETUP_CSS_STRATEGIES.join()}`,
21
+ options: SETUP_CSS_STRATEGIES
22
+ })
23
+ };
24
+ async run() {
25
+ const { flags, args } = await this.parse(SetupCSS);
26
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
27
+ await runSetupCSS({
28
+ ...flagsToCamelObject(flags),
29
+ strategy: args.strategy,
30
+ directory
31
+ });
32
+ }
33
+ }
34
+ async function runSetupCSS({
35
+ strategy: flagStrategy,
36
+ directory,
37
+ force = false,
38
+ installDeps = true
39
+ }) {
40
+ const remixConfigPromise = getRemixConfig(directory);
41
+ const strategy = flagStrategy ? flagStrategy : await renderCssPrompt();
42
+ const remixConfig = await remixConfigPromise;
43
+ const setupOutput = await setupCssStrategy(strategy, remixConfig, force);
44
+ if (!setupOutput)
45
+ return;
46
+ const { workPromise, generatedAssets, helpUrl } = setupOutput;
47
+ const tasks = [
48
+ {
49
+ title: "Updating files",
50
+ task: async () => {
51
+ await workPromise;
52
+ }
53
+ }
54
+ ];
55
+ if (installDeps) {
56
+ const gettingPkgManagerPromise = getPackageManager(
57
+ remixConfig.rootDirectory
58
+ );
59
+ tasks.push({
60
+ title: "Installing new dependencies",
61
+ task: async () => {
62
+ const packageManager = await gettingPkgManagerPromise;
63
+ await installNodeModules({
64
+ directory: remixConfig.rootDirectory,
65
+ packageManager,
66
+ args: []
67
+ });
68
+ }
69
+ });
70
+ }
71
+ await renderTasks(tasks);
72
+ renderSuccess({
73
+ headline: `${CSS_STRATEGY_NAME_MAP[strategy]} setup complete.`,
74
+ body: (generatedAssets.length > 0 ? "You can now modify CSS configuration in the following files:\n" + generatedAssets.map((file) => ` - ${file}`).join("\n") + "\n" : "") + `
75
+ For more information, visit ${helpUrl}.`
76
+ });
77
+ }
78
+
79
+ export { SetupCSS as default, runSetupCSS };
@@ -0,0 +1,53 @@
1
+ import { resolvePath } from '@shopify/cli-kit/node/path';
2
+ import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
3
+ import Command from '@shopify/cli-kit/node/base-command';
4
+ import { renderTasks, renderSuccess } from '@shopify/cli-kit/node/ui';
5
+ import { Args } from '@oclif/core';
6
+ import { getRemixConfig } from '../../../lib/config.js';
7
+ import { SETUP_I18N_STRATEGIES, renderI18nPrompt, setupI18nStrategy, I18N_STRATEGY_NAME_MAP } from '../../../lib/setups/i18n/index.js';
8
+
9
+ class SetupMarkets extends Command {
10
+ static description = "Setup support for multiple markets in your project.";
11
+ static flags = {
12
+ path: commonFlags.path
13
+ };
14
+ static args = {
15
+ strategy: Args.string({
16
+ name: "strategy",
17
+ description: `The URL structure strategy to setup multiple markets. One of ${SETUP_I18N_STRATEGIES.join()}`,
18
+ options: SETUP_I18N_STRATEGIES
19
+ })
20
+ };
21
+ async run() {
22
+ const { flags, args } = await this.parse(SetupMarkets);
23
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
24
+ await runSetupI18n({
25
+ ...flagsToCamelObject(flags),
26
+ strategy: args.strategy,
27
+ directory
28
+ });
29
+ }
30
+ }
31
+ async function runSetupI18n({
32
+ strategy: flagStrategy,
33
+ directory
34
+ }) {
35
+ const remixConfigPromise = getRemixConfig(directory);
36
+ const strategy = flagStrategy ? flagStrategy : await renderI18nPrompt();
37
+ const remixConfig = await remixConfigPromise;
38
+ await renderTasks([
39
+ {
40
+ title: "Updating files",
41
+ task: async () => {
42
+ await setupI18nStrategy(strategy, remixConfig);
43
+ }
44
+ }
45
+ ]);
46
+ renderSuccess({
47
+ headline: `Makerts support setup complete with strategy ${I18N_STRATEGY_NAME_MAP[strategy].toLocaleLowerCase()}.`,
48
+ body: `You can now modify the supported locales in ${remixConfig.serverEntryPoint ?? "your server entry file."}
49
+ `
50
+ });
51
+ }
52
+
53
+ export { SetupMarkets as default, runSetupI18n };
@@ -0,0 +1,133 @@
1
+ import Command from '@shopify/cli-kit/node/base-command';
2
+ import { AbortController } from '@shopify/cli-kit/node/abort';
3
+ import { renderTasks } from '@shopify/cli-kit/node/ui';
4
+ import { resolvePath, basename } from '@shopify/cli-kit/node/path';
5
+ import { copyFile } from '@shopify/cli-kit/node/fs';
6
+ import { commonFlags, overrideFlag, flagsToCamelObject } from '../../lib/flags.js';
7
+ import { renderI18nPrompt, setupI18nStrategy } from '../../lib/setups/i18n/index.js';
8
+ import { getRemixConfig } from '../../lib/config.js';
9
+ import { handleRouteGeneration, generateProjectEntries, handleCliShortcut, renderProjectReady } from '../../lib/onboarding/common.js';
10
+ import { getCliCommand } from '../../lib/shell.js';
11
+ import { generateProjectFile } from '../../lib/setups/routes/generate.js';
12
+ import { getTemplateAppFile } from '../../lib/build.js';
13
+
14
+ class Setup extends Command {
15
+ static description = "Scaffold routes and core functionality.";
16
+ static flags = {
17
+ path: commonFlags.path,
18
+ force: commonFlags.force,
19
+ styling: commonFlags.styling,
20
+ markets: commonFlags.markets,
21
+ shortcut: commonFlags.shortcut,
22
+ "install-deps": overrideFlag(commonFlags.installDeps, { default: true })
23
+ };
24
+ async run() {
25
+ const { flags } = await this.parse(Setup);
26
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
27
+ await runSetup({
28
+ ...flagsToCamelObject(flags),
29
+ directory
30
+ });
31
+ }
32
+ }
33
+ async function runSetup(options) {
34
+ const controller = new AbortController();
35
+ const remixConfig = await getRemixConfig(options.directory);
36
+ const location = basename(remixConfig.rootDirectory);
37
+ const cliCommandPromise = getCliCommand();
38
+ let backgroundWorkPromise = Promise.resolve();
39
+ const tasks = [
40
+ {
41
+ title: "Setting up project",
42
+ task: async () => {
43
+ await backgroundWorkPromise;
44
+ }
45
+ }
46
+ ];
47
+ const i18nStrategy = options.i18n ? options.i18n : await renderI18nPrompt({
48
+ abortSignal: controller.signal,
49
+ extraChoices: { none: "Set up later" }
50
+ });
51
+ const i18n = i18nStrategy === "none" ? void 0 : i18nStrategy;
52
+ const { needsRouteGeneration, setupRoutes } = await handleRouteGeneration(
53
+ controller
54
+ );
55
+ let routes;
56
+ if (needsRouteGeneration) {
57
+ const typescript = !!remixConfig.tsconfigPath;
58
+ backgroundWorkPromise = backgroundWorkPromise.then(
59
+ () => Promise.all([
60
+ generateProjectFile("../server.ts", { ...remixConfig, typescript }),
61
+ ...typescript ? [
62
+ copyFile(
63
+ getTemplateAppFile("../remix.env.d.ts"),
64
+ resolvePath(remixConfig.rootDirectory, "remix.env.d.ts")
65
+ ),
66
+ copyFile(
67
+ getTemplateAppFile("../storefrontapi.generated.d.ts"),
68
+ resolvePath(
69
+ remixConfig.rootDirectory,
70
+ "storefrontapi.generated.d.ts"
71
+ )
72
+ )
73
+ ] : [],
74
+ generateProjectEntries({
75
+ rootDirectory: remixConfig.rootDirectory,
76
+ appDirectory: remixConfig.appDirectory,
77
+ typescript,
78
+ v2Flags: {
79
+ isV2RouteConvention: remixConfig.future?.v2_routeConvention,
80
+ isV2ErrorBoundary: remixConfig.future?.v2_errorBoundary,
81
+ isV2Meta: remixConfig.future?.v2_meta
82
+ }
83
+ })
84
+ ])
85
+ ).then(async () => {
86
+ routes = await setupRoutes(
87
+ remixConfig.rootDirectory,
88
+ typescript ? "ts" : "js",
89
+ i18n
90
+ );
91
+ });
92
+ }
93
+ if (i18n) {
94
+ backgroundWorkPromise = backgroundWorkPromise.then(
95
+ () => setupI18nStrategy(i18n, remixConfig)
96
+ );
97
+ }
98
+ let hasCreatedShortcut = false;
99
+ const cliCommand = await cliCommandPromise;
100
+ const needsAlias = cliCommand !== "h2";
101
+ if (needsAlias) {
102
+ const { createShortcut, showShortcutBanner } = await handleCliShortcut(
103
+ controller,
104
+ await cliCommandPromise,
105
+ options.shortcut
106
+ );
107
+ if (createShortcut) {
108
+ backgroundWorkPromise = backgroundWorkPromise.then(async () => {
109
+ hasCreatedShortcut = await createShortcut();
110
+ });
111
+ showShortcutBanner();
112
+ }
113
+ }
114
+ if (!i18n && !needsRouteGeneration && !needsAlias)
115
+ return;
116
+ await renderTasks(tasks);
117
+ await renderProjectReady(
118
+ {
119
+ location,
120
+ name: location,
121
+ directory: remixConfig.rootDirectory
122
+ },
123
+ {
124
+ hasCreatedShortcut,
125
+ depsInstalled: true,
126
+ packageManager: "npm",
127
+ i18n,
128
+ routes
129
+ }
130
+ );
131
+ }
132
+
133
+ export { Setup as default };
@@ -1,6 +1,6 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
2
  import { renderSuccess, renderFatalError } from '@shopify/cli-kit/node/ui';
3
- import { ALIAS_NAME, isWindows, isGitBash, shellWriteAlias, shellRunScript } from '../../lib/shell.js';
3
+ import { ALIAS_NAME, createPlatformShortcut } from '../../lib/shell.js';
4
4
 
5
5
  class Shortcut extends Command {
6
6
  static description = `Creates a global \`${ALIAS_NAME}\` shortcut for the Hydrogen CLI`;
@@ -9,7 +9,7 @@ class Shortcut extends Command {
9
9
  }
10
10
  }
11
11
  async function runCreateShortcut() {
12
- const shortcuts = isWindows() && !isGitBash() ? await createShortcutsForWindows() : await createShortcutsForUnix();
12
+ const shortcuts = await createPlatformShortcut();
13
13
  if (shortcuts.length > 0) {
14
14
  renderSuccess({
15
15
  headline: `Shortcut ready for the following shells: ${shortcuts.join(
@@ -26,48 +26,5 @@ Restart your terminal session and run \`${ALIAS_NAME}\` from your local project.
26
26
  });
27
27
  }
28
28
  }
29
- const BASH_ZSH_COMMAND = `
30
- # Shopify Hydrogen alias to local projects
31
- alias ${ALIAS_NAME}='$(npm prefix -s)/node_modules/.bin/shopify hydrogen'`;
32
- const FISH_FUNCTION = `
33
- function ${ALIAS_NAME} --wraps='shopify hydrogen' --description 'Shortcut for the Hydrogen CLI'
34
- set npmPrefix (npm prefix -s)
35
- $npmPrefix/node_modules/.bin/shopify hydrogen $argv
36
- end
37
- `;
38
- async function createShortcutsForUnix() {
39
- const shells = [];
40
- if (await shellWriteAlias("zsh", ALIAS_NAME, BASH_ZSH_COMMAND)) {
41
- shells.push("zsh");
42
- }
43
- if (await shellWriteAlias("bash", ALIAS_NAME, BASH_ZSH_COMMAND)) {
44
- shells.push("bash");
45
- }
46
- if (await shellWriteAlias("fish", ALIAS_NAME, FISH_FUNCTION)) {
47
- shells.push("fish");
48
- }
49
- return shells;
50
- }
51
- const PS_FUNCTION = `function Invoke-Local-H2 {$npmPrefix = npm prefix -s; Invoke-Expression "$npmPrefix\\node_modules\\.bin\\shopify.ps1 hydrogen $Args"}; Set-Alias -Name ${ALIAS_NAME} -Value Invoke-Local-H2`;
52
- const PS_APPEND_PROFILE_COMMAND = `
53
- if (!(Test-Path -Path $PROFILE)) {
54
- New-Item -ItemType File -Path $PROFILE -Force
55
- }
56
-
57
- $profileContent = Get-Content -Path $PROFILE
58
- if (!$profileContent -or $profileContent -NotLike '*Invoke-Local-H2*') {
59
- Add-Content -Path $PROFILE -Value '${PS_FUNCTION}'
60
- }
61
- `;
62
- async function createShortcutsForWindows() {
63
- const shells = [];
64
- if (await shellRunScript(PS_APPEND_PROFILE_COMMAND, "powershell.exe")) {
65
- shells.push("PowerShell");
66
- }
67
- if (await shellRunScript(PS_APPEND_PROFILE_COMMAND, "pwsh.exe")) {
68
- shells.push("PowerShell 7+");
69
- }
70
- return shells;
71
- }
72
29
 
73
30
  export { Shortcut as default, runCreateShortcut };
@@ -1,55 +1,28 @@
1
- import { describe, beforeEach, vi, afterEach, expect, it } from 'vitest';
1
+ import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
2
2
  import { runCreateShortcut } from './shortcut.js';
3
3
  import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
4
- import { shellWriteAlias, isWindows, isGitBash } from '../../lib/shell.js';
5
- import { execSync, exec } from 'child_process';
4
+ import { createPlatformShortcut } from '../../lib/shell.js';
6
5
 
6
+ vi.mock("../../lib/shell.js");
7
7
  describe("shortcut", () => {
8
8
  const outputMock = mockAndCaptureOutput();
9
9
  beforeEach(() => {
10
10
  vi.resetAllMocks();
11
- vi.mock("child_process");
12
- vi.mock("../../lib/shell.js", async () => {
13
- const original = await vi.importActual("../../lib/shell.js");
14
- return {
15
- ...original,
16
- isWindows: vi.fn(),
17
- isGitBash: vi.fn(),
18
- shellWriteAlias: vi.fn(),
19
- shellRunScript: async () => true
20
- };
21
- });
22
- vi.mocked(shellWriteAlias).mockImplementation(
23
- async (shell) => !isWindows() || shell === "bash"
24
- );
25
11
  });
26
12
  afterEach(() => {
27
13
  outputMock.clear();
28
- expect(execSync).toHaveBeenCalledTimes(0);
29
- expect(exec).toHaveBeenCalledTimes(0);
30
14
  });
31
- it("creates aliases for Unix", async () => {
32
- vi.mocked(isWindows).mockReturnValue(false);
15
+ it("shows created aliases", async () => {
16
+ vi.mocked(createPlatformShortcut).mockResolvedValue([
17
+ "zsh",
18
+ "bash",
19
+ "fish"
20
+ ]);
33
21
  await runCreateShortcut();
34
22
  expect(outputMock.info()).toMatch(`zsh, bash, fish`);
35
- expect(outputMock.error()).toBeFalsy();
36
- });
37
- it("creates aliases for Windows", async () => {
38
- vi.mocked(isWindows).mockReturnValue(true);
39
- await runCreateShortcut();
40
- expect(outputMock.info()).toMatch(`PowerShell, PowerShell 7+`);
41
- expect(outputMock.error()).toBeFalsy();
42
- });
43
- it("creates aliases for Windows in Git Bash", async () => {
44
- vi.mocked(isWindows).mockReturnValue(true);
45
- vi.mocked(isGitBash).mockReturnValueOnce(true);
46
- await runCreateShortcut();
47
- expect(outputMock.info()).toMatch("bash");
48
- expect(outputMock.error()).toBeFalsy();
49
23
  });
50
24
  it("warns when not finding shells", async () => {
51
- vi.mocked(isWindows).mockReturnValue(false);
52
- vi.mocked(shellWriteAlias).mockResolvedValue(false);
25
+ vi.mocked(createPlatformShortcut).mockResolvedValue([]);
53
26
  await runCreateShortcut();
54
27
  expect(outputMock.info()).toBeFalsy();
55
28
  expect(outputMock.error()).toBeTruthy();
@@ -0,0 +1,6 @@
1
+ {
2
+ "comment": "Remix version is automatically updated by the CLI",
3
+ "dependencies": {
4
+ "@remix-run/css-bundle": "^1"
5
+ }
6
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "browserslist": [
3
+ "defaults"
4
+ ],
5
+ "devDependencies": {
6
+ "postcss": "^8.4.21",
7
+ "postcss-import": "^15.1.0",
8
+ "postcss-preset-env": "^8.2.0"
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ plugins: {
3
+ 'postcss-import': {},
4
+ 'postcss-preset-env': {
5
+ features: {'nesting-rules': false},
6
+ },
7
+ },
8
+ };
@@ -0,0 +1,13 @@
1
+ {
2
+ "browserslist": [
3
+ "defaults"
4
+ ],
5
+ "devDependencies": {
6
+ "@tailwindcss/forms": "^0.5.3",
7
+ "@tailwindcss/typography": "^0.5.9",
8
+ "postcss": "^8.4.21",
9
+ "postcss-import": "^15.1.0",
10
+ "postcss-preset-env": "^8.2.0",
11
+ "tailwindcss": "^3.3.0"
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ plugins: {
3
+ 'postcss-import': {},
4
+ 'tailwindcss/nesting': {},
5
+ tailwindcss: {},
6
+ 'postcss-preset-env': {
7
+ features: {'nesting-rules': false},
8
+ },
9
+ },
10
+ };
@@ -0,0 +1,8 @@
1
+ import formsPlugin from '@tailwindcss/forms';
2
+ import typographyPlugin from '@tailwindcss/typography';
3
+
4
+ /** @type {import('tailwindcss').Config} */
5
+ export default {
6
+ content: ['./{src-dir}/**/*.{js,ts,jsx,tsx}'],
7
+ plugins: [formsPlugin, typographyPlugin],
8
+ };
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
@@ -0,0 +1,9 @@
1
+ {
2
+ "comment": "Remix version is automatically updated by the CLI",
3
+ "dependencies": {
4
+ "@remix-run/css-bundle": "^1"
5
+ },
6
+ "devDependencies": {
7
+ "@vanilla-extract/css": "^1.11.0"
8
+ }
9
+ }