@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
@@ -0,0 +1,229 @@
1
+ import { copy } from 'fs-extra/esm';
2
+ import { writeFile } from '@shopify/cli-kit/node/fs';
3
+ import { relativePath, joinPath } from '@shopify/cli-kit/node/path';
4
+ import { hyphenate } from '@shopify/cli-kit/common/string';
5
+ import colors from '@shopify/cli-kit/node/colors';
6
+ import { renderSelectPrompt, renderSuccess, renderConfirmationPrompt, renderTasks } from '@shopify/cli-kit/node/ui';
7
+ import { handleStorefrontLink, handleProjectLocation, createAbortHandler, generateProjectEntries, handleLanguage, createInitialCommit, handleCssStrategy, commitAll, handleDependencies, handleCliShortcut, handleI18n, handleRouteGeneration, renderProjectReady } from './common.js';
8
+ import { createStorefront } from '../graphql/admin/create-storefront.js';
9
+ import { waitForJob } from '../graphql/admin/fetch-job.js';
10
+ import { getStarterDir } from '../build.js';
11
+ import { replaceFileContent } from '../file.js';
12
+ import { setUserAccount, setStorefront } from '../shopify-config.js';
13
+ import { getCliCommand, ALIAS_NAME } from '../shell.js';
14
+ import { CSS_STRATEGY_NAME_MAP } from '../setups/css/index.js';
15
+
16
+ async function setupLocalStarterTemplate(options, controller) {
17
+ const templateAction = options.mockShop ? "mock" : await renderSelectPrompt({
18
+ message: "Connect to Shopify",
19
+ choices: [
20
+ {
21
+ label: "Use sample data from Mock.shop (no login required)",
22
+ value: "mock"
23
+ },
24
+ { label: "Link your Shopify account", value: "link" }
25
+ ],
26
+ defaultValue: "mock",
27
+ abortSignal: controller.signal
28
+ });
29
+ const storefrontInfo = templateAction === "link" ? await handleStorefrontLink(controller) : void 0;
30
+ const project = await handleProjectLocation({
31
+ ...options,
32
+ storefrontInfo,
33
+ controller
34
+ });
35
+ if (!project)
36
+ return;
37
+ if (templateAction === "mock")
38
+ project.storefrontTitle = "Mock.shop";
39
+ const abort = createAbortHandler(controller, project);
40
+ const createStorefrontPromise = storefrontInfo && createStorefront(storefrontInfo.session, storefrontInfo.title).then(async ({ storefront, jobId }) => {
41
+ if (jobId)
42
+ await waitForJob(storefrontInfo.session, jobId);
43
+ return storefront;
44
+ }).catch(abort);
45
+ const templateDir = getStarterDir();
46
+ let backgroundWorkPromise = copy(
47
+ templateDir,
48
+ project.directory,
49
+ {
50
+ filter: (filepath) => !/^(app|dist|node_modules)\//i.test(
51
+ relativePath(templateDir, filepath)
52
+ )
53
+ }
54
+ ).then(
55
+ () => generateProjectEntries({
56
+ rootDirectory: project.directory,
57
+ appDirectory: joinPath(project.directory, "app"),
58
+ typescript: true
59
+ })
60
+ ).catch(abort);
61
+ const tasks = [
62
+ {
63
+ title: "Creating storefront",
64
+ task: async () => {
65
+ await createStorefrontPromise;
66
+ }
67
+ },
68
+ {
69
+ title: "Setting up project",
70
+ task: async () => {
71
+ await backgroundWorkPromise;
72
+ }
73
+ }
74
+ ];
75
+ backgroundWorkPromise = backgroundWorkPromise.then(() => {
76
+ const promises = [
77
+ replaceFileContent(
78
+ joinPath(project.directory, "package.json"),
79
+ false,
80
+ (content) => content.replace(
81
+ /"name": "[^"]+"/,
82
+ `"name": "${hyphenate(storefrontInfo?.title ?? project.name)}"`
83
+ )
84
+ )
85
+ ];
86
+ const envLeadingComment = "# The variables added in this file are only available locally in MiniOxygen\n";
87
+ if (storefrontInfo && createStorefrontPromise) {
88
+ promises.push(
89
+ setUserAccount(project.directory, storefrontInfo),
90
+ createStorefrontPromise.then(
91
+ (storefront) => setStorefront(project.directory, storefront)
92
+ ),
93
+ writeFile(joinPath(project.directory, ".env"), envLeadingComment)
94
+ );
95
+ } else if (templateAction === "mock") {
96
+ promises.push(
97
+ writeFile(
98
+ joinPath(project.directory, ".env"),
99
+ envLeadingComment + "\n" + [
100
+ ["SESSION_SECRET", "foobar"],
101
+ ["PUBLIC_STORE_DOMAIN", "mock.shop"]
102
+ ].map(([key, value]) => `${key}="${value}"`).join("\n") + "\n"
103
+ )
104
+ );
105
+ }
106
+ return Promise.all(promises).catch(abort);
107
+ });
108
+ const { language, transpileProject } = await handleLanguage(
109
+ project.directory,
110
+ controller,
111
+ options.language
112
+ );
113
+ backgroundWorkPromise = backgroundWorkPromise.then(() => transpileProject().catch(abort)).then(
114
+ () => options.git ? createInitialCommit(project.directory) : void 0
115
+ );
116
+ const { setupCss, cssStrategy } = await handleCssStrategy(
117
+ project.directory,
118
+ controller,
119
+ options.styling
120
+ );
121
+ if (cssStrategy) {
122
+ backgroundWorkPromise = backgroundWorkPromise.then(() => setupCss().catch(abort)).then(
123
+ () => options.git ? commitAll(
124
+ project.directory,
125
+ "Setup " + CSS_STRATEGY_NAME_MAP[cssStrategy]
126
+ ) : void 0
127
+ );
128
+ }
129
+ const { packageManager, shouldInstallDeps, installDeps } = await handleDependencies(
130
+ project.directory,
131
+ controller,
132
+ options.installDeps
133
+ );
134
+ const setupSummary = {
135
+ language,
136
+ packageManager,
137
+ cssStrategy,
138
+ depsInstalled: false,
139
+ hasCreatedShortcut: false
140
+ };
141
+ if (shouldInstallDeps) {
142
+ const installingDepsPromise = backgroundWorkPromise.then(async () => {
143
+ try {
144
+ await installDeps();
145
+ setupSummary.depsInstalled = true;
146
+ } catch (error) {
147
+ setupSummary.depsError = error;
148
+ }
149
+ });
150
+ tasks.push({
151
+ title: "Installing dependencies. This could take a few minutes",
152
+ task: async () => {
153
+ await installingDepsPromise;
154
+ }
155
+ });
156
+ }
157
+ const pkgManagerCommand = await getCliCommand("", packageManager);
158
+ const { createShortcut, showShortcutBanner } = await handleCliShortcut(
159
+ controller,
160
+ pkgManagerCommand,
161
+ options.shortcut
162
+ );
163
+ if (createShortcut) {
164
+ backgroundWorkPromise = backgroundWorkPromise.then(async () => {
165
+ setupSummary.hasCreatedShortcut = await createShortcut();
166
+ });
167
+ showShortcutBanner();
168
+ }
169
+ const cliCommand = createShortcut ? ALIAS_NAME : pkgManagerCommand;
170
+ renderSuccess({
171
+ headline: [
172
+ { userInput: storefrontInfo?.title ?? project.name },
173
+ "is ready to build."
174
+ ]
175
+ });
176
+ const continueWithSetup = (options.i18n ?? options.routes) !== void 0 || await renderConfirmationPrompt({
177
+ message: "Do you want to scaffold routes and core functionality?",
178
+ confirmationMessage: "Yes, set up now",
179
+ cancellationMessage: "No, set up later " + colors.dim(`(run \`${cliCommand} setup\`)`),
180
+ abortSignal: controller.signal
181
+ });
182
+ if (continueWithSetup) {
183
+ const { i18nStrategy, setupI18n } = await handleI18n(
184
+ controller,
185
+ cliCommand,
186
+ options.i18n
187
+ );
188
+ const { setupRoutes } = await handleRouteGeneration(
189
+ controller,
190
+ options.routes || true
191
+ );
192
+ setupSummary.i18n = i18nStrategy;
193
+ backgroundWorkPromise = backgroundWorkPromise.then(async () => {
194
+ await setupI18n({
195
+ rootDirectory: project.directory,
196
+ serverEntryPoint: language === "ts" ? "server.ts" : "server.js"
197
+ }).then(
198
+ () => options.git ? commitAll(
199
+ project.directory,
200
+ `Setup markets support using ${i18nStrategy}`
201
+ ) : void 0
202
+ ).catch((error) => {
203
+ setupSummary.i18nError = error;
204
+ });
205
+ await setupRoutes(project.directory, language, i18nStrategy).then((routes) => {
206
+ setupSummary.routes = routes;
207
+ if (options.git && routes) {
208
+ return commitAll(
209
+ project.directory,
210
+ `Generate routes for core functionality`
211
+ );
212
+ }
213
+ }).catch((error) => {
214
+ setupSummary.routesError = error;
215
+ });
216
+ });
217
+ }
218
+ await renderTasks(tasks);
219
+ if (options.git) {
220
+ await commitAll(project.directory, "Lockfile");
221
+ }
222
+ await renderProjectReady(project, setupSummary);
223
+ return {
224
+ ...project,
225
+ ...setupSummary
226
+ };
227
+ }
228
+
229
+ export { setupLocalStarterTemplate };
@@ -0,0 +1,89 @@
1
+ import { AbortError } from '@shopify/cli-kit/node/error';
2
+ import { copyFile } from '@shopify/cli-kit/node/fs';
3
+ import { joinPath } from '@shopify/cli-kit/node/path';
4
+ import { renderTasks, renderInfo } from '@shopify/cli-kit/node/ui';
5
+ import { getLatestTemplates } from '../template-downloader.js';
6
+ import { handleProjectLocation, createAbortHandler, handleLanguage, createInitialCommit, handleDependencies, renderProjectReady } from './common.js';
7
+
8
+ async function setupRemoteTemplate(options, controller) {
9
+ const isOfficialTemplate = options.template === "demo-store" || options.template === "hello-world";
10
+ if (!isOfficialTemplate) {
11
+ throw new AbortError(
12
+ "Only `demo-store` and `hello-world` are supported in --template flag for now.",
13
+ "Skip the --template flag to run the setup flow."
14
+ );
15
+ }
16
+ const appTemplate = options.template;
17
+ const backgroundDownloadPromise = getLatestTemplates({
18
+ signal: controller.signal
19
+ }).catch((error) => {
20
+ throw abort(error);
21
+ });
22
+ const project = await handleProjectLocation({ ...options, controller });
23
+ if (!project)
24
+ return;
25
+ const abort = createAbortHandler(controller, project);
26
+ let backgroundWorkPromise = backgroundDownloadPromise.then(
27
+ ({ templatesDir }) => copyFile(joinPath(templatesDir, appTemplate), project.directory).catch(
28
+ abort
29
+ )
30
+ );
31
+ const { language, transpileProject } = await handleLanguage(
32
+ project.directory,
33
+ controller,
34
+ options.language
35
+ );
36
+ backgroundWorkPromise = backgroundWorkPromise.then(() => transpileProject().catch(abort)).then(() => createInitialCommit(project.directory));
37
+ const { packageManager, shouldInstallDeps, installDeps } = await handleDependencies(
38
+ project.directory,
39
+ controller,
40
+ options.installDeps
41
+ );
42
+ const setupSummary = {
43
+ language,
44
+ packageManager,
45
+ depsInstalled: false,
46
+ hasCreatedShortcut: false
47
+ };
48
+ const tasks = [
49
+ {
50
+ title: "Downloading template",
51
+ task: async () => {
52
+ await backgroundDownloadPromise;
53
+ }
54
+ },
55
+ {
56
+ title: "Setting up project",
57
+ task: async () => {
58
+ await backgroundWorkPromise;
59
+ }
60
+ }
61
+ ];
62
+ if (shouldInstallDeps) {
63
+ tasks.push({
64
+ title: "Installing dependencies. This could take a few minutes",
65
+ task: async () => {
66
+ try {
67
+ await installDeps();
68
+ setupSummary.depsInstalled = true;
69
+ } catch (error) {
70
+ setupSummary.depsError = error;
71
+ }
72
+ }
73
+ });
74
+ }
75
+ await renderTasks(tasks);
76
+ await renderProjectReady(project, setupSummary);
77
+ if (isOfficialTemplate) {
78
+ renderInfo({
79
+ headline: `Your project will display inventory from the Hydrogen Demo Store.`,
80
+ body: `To connect this project to your Shopify store\u2019s inventory, update \`${project.name}/.env\` with your store ID and Storefront API key.`
81
+ });
82
+ }
83
+ return {
84
+ ...project,
85
+ ...setupSummary
86
+ };
87
+ }
88
+
89
+ export { setupRemoteTemplate };
@@ -10,10 +10,10 @@ function isRemixV2() {
10
10
  return false;
11
11
  }
12
12
  }
13
- async function getV2Flags(root) {
13
+ async function getV2Flags(root, remixConfigFuture) {
14
14
  const isV2 = isRemixV2();
15
15
  const futureFlags = {
16
- ...!isV2 && (await getRemixConfig(root, true)).future
16
+ ...!isV2 && (remixConfigFuture ?? (await getRemixConfig(root, true)).future)
17
17
  };
18
18
  return {
19
19
  isV2Meta: isV2 || !!futureFlags.v2_meta,
@@ -21,8 +21,8 @@ async function getV2Flags(root) {
21
21
  isV2RouteConvention: isV2 ? !isV1RouteConventionInstalled() : !!futureFlags.v2_routeConvention
22
22
  };
23
23
  }
24
- function convertRouteToV2(route) {
25
- return route.replace(/\/index$/, "/_index").replace(/(?<!^)\//g, ".");
24
+ function convertRouteToV1(route) {
25
+ return route.replace(/(^|\.)_index$/, "$1index").replace(/\.(?!\w+\])/g, "/");
26
26
  }
27
27
  function convertTemplateToRemixVersion(template, { isV2Meta, isV2ErrorBoundary }) {
28
28
  template = isV2Meta ? convertToMetaV2(template) : convertToMetaV1(template);
@@ -51,4 +51,4 @@ function isV1RouteConventionInstalled() {
51
51
  }
52
52
  }
53
53
 
54
- export { convertRouteToV2, convertTemplateToRemixVersion, getV2Flags, isRemixV2 };
54
+ export { convertRouteToV1, convertTemplateToRemixVersion, getV2Flags, isRemixV2 };
@@ -1,7 +1,17 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { convertTemplateToRemixVersion } from './remix-version-interop.js';
2
+ import { convertRouteToV1, convertTemplateToRemixVersion } from './remix-version-interop.js';
3
3
 
4
4
  describe("remix-version-interop", () => {
5
+ describe("v2_routeConvention", () => {
6
+ it("converts routes to v1", () => {
7
+ expect(convertRouteToV1("_index")).toEqual("index");
8
+ expect(convertRouteToV1("path.to.file")).toEqual("path/to/file");
9
+ expect(convertRouteToV1("path.to._index")).toEqual("path/to/index");
10
+ expect(convertRouteToV1("patht.to.[sitemap.xml]")).toEqual(
11
+ "patht/to/[sitemap.xml]"
12
+ );
13
+ });
14
+ });
5
15
  describe("v2_meta", () => {
6
16
  const META_TEMPLATE = `
7
17
  import {type MetaFunction} from '@shopify/remix-oxygen';
@@ -1,11 +1,12 @@
1
1
  import { renderFatalError } from '@shopify/cli-kit/node/ui';
2
2
  import { outputContent, outputToken } from '@shopify/cli-kit/node/output';
3
3
  import { hydrogenStorefrontsUrl } from './admin-urls.js';
4
- import { parseGid } from './graphql.js';
4
+ import { parseGid } from './gid.js';
5
5
 
6
6
  function renderMissingStorefront({
7
- adminSession,
8
- storefront
7
+ session,
8
+ storefront,
9
+ cliCommand
9
10
  }) {
10
11
  renderFatalError({
11
12
  name: "NoStorefrontError",
@@ -15,22 +16,23 @@ function renderMissingStorefront({
15
16
  )}`.value,
16
17
  tryMessage: outputContent`Couldn’t find ${storefront.title} (ID: ${parseGid(
17
18
  storefront.id
18
- )}) on ${adminSession.storeFqdn}. Check that the storefront exists and run ${outputToken.genericShellCommand(
19
- `npx shopify hydrogen link`
19
+ )}) on ${session.storeFqdn}. Check that the storefront exists and run ${outputToken.genericShellCommand(
20
+ `${cliCommand} link`
20
21
  )} to link this project to it.\n\n${outputToken.link(
21
22
  "Hydrogen Storefronts Admin",
22
- hydrogenStorefrontsUrl(adminSession)
23
+ hydrogenStorefrontsUrl(session)
23
24
  )}`.value
24
25
  });
25
26
  }
26
- function renderMissingLink({ adminSession }) {
27
+ function renderMissingLink({ session, cliCommand }) {
27
28
  renderFatalError({
28
29
  name: "NoLinkedStorefrontError",
29
30
  type: 0,
30
- message: `No linked Hydrogen storefront on ${adminSession.storeFqdn}`,
31
- tryMessage: outputContent`To pull environment variables, link this project to a Hydrogen storefront. To select a storefront to link, run ${outputToken.genericShellCommand(
32
- `npx shopify hydrogen link`
33
- )}.`.value
31
+ message: `No linked Hydrogen storefront on ${session.storeFqdn}`,
32
+ tryMessage: [
33
+ "To pull environment variables, link this project to a Hydrogen storefront. To select a storefront to link, run",
34
+ { command: `${cliCommand} link` }
35
+ ]
34
36
  });
35
37
  }
36
38
 
@@ -0,0 +1,89 @@
1
+ import { readFile, writeFile, fileExists } from '@shopify/cli-kit/node/fs';
2
+ import { joinPath } from '@shopify/cli-kit/node/path';
3
+ import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
4
+ import { readAndParsePackageJson, writePackageJSON } from '@shopify/cli-kit/node/node-package-manager';
5
+ import { GENERATOR_SETUP_ASSETS_SUB_DIRS, getAssetDir } from '../../build.js';
6
+
7
+ const SETUP_CSS_STRATEGIES = GENERATOR_SETUP_ASSETS_SUB_DIRS;
8
+ function copyAssets(feature, assets, rootDirectory, replacer = (content, filename) => content) {
9
+ const setupAssetsPath = getAssetDir(feature);
10
+ return Promise.all(
11
+ Object.entries(assets).map(async ([source, destination]) => {
12
+ const content = await readFile(joinPath(setupAssetsPath, source));
13
+ await writeFile(
14
+ joinPath(rootDirectory, destination),
15
+ replacer(content, source)
16
+ );
17
+ })
18
+ );
19
+ }
20
+ async function canWriteFiles(assetMap, directory, force) {
21
+ const fileExistPromises = Object.values(assetMap).map(
22
+ (file) => fileExists(joinPath(directory, file)).then(
23
+ (exists) => exists ? file : null
24
+ )
25
+ );
26
+ const existingFiles = (await Promise.all(fileExistPromises)).filter(
27
+ Boolean
28
+ );
29
+ if (existingFiles.length > 0) {
30
+ if (!force) {
31
+ const overwrite = await renderConfirmationPrompt({
32
+ message: `Some files already exist (${existingFiles.join(
33
+ ", "
34
+ )}). Overwrite?`,
35
+ defaultValue: false
36
+ });
37
+ if (!overwrite) {
38
+ return false;
39
+ }
40
+ }
41
+ }
42
+ return true;
43
+ }
44
+ const MANAGED_PACKAGE_JSON_KEYS = Object.freeze([
45
+ "dependencies",
46
+ "devDependencies",
47
+ "peerDependencies"
48
+ ]);
49
+ async function mergePackageJson(feature, projectDir) {
50
+ const targetPkgJson = await readAndParsePackageJson(
51
+ joinPath(projectDir, "package.json")
52
+ );
53
+ const sourcePkgJson = await readAndParsePackageJson(
54
+ joinPath(getAssetDir(feature), "package.json")
55
+ );
56
+ delete sourcePkgJson.comment;
57
+ const unmanagedKeys = Object.keys(sourcePkgJson).filter(
58
+ (key) => !MANAGED_PACKAGE_JSON_KEYS.includes(key)
59
+ );
60
+ for (const key of unmanagedKeys) {
61
+ const sourceValue = sourcePkgJson[key];
62
+ const targetValue = targetPkgJson[key];
63
+ const newValue = Array.isArray(sourceValue) && Array.isArray(targetValue) ? [...targetValue, ...sourceValue] : typeof sourceValue === "object" && typeof targetValue === "object" ? { ...targetValue, ...sourceValue } : sourceValue;
64
+ targetPkgJson[key] = newValue;
65
+ }
66
+ const remixVersion = Object.entries(targetPkgJson.dependencies || {}).find(
67
+ ([dep]) => dep.startsWith("@remix-run/")
68
+ )?.[1];
69
+ for (const key of MANAGED_PACKAGE_JSON_KEYS) {
70
+ if (sourcePkgJson[key]) {
71
+ targetPkgJson[key] = [
72
+ .../* @__PURE__ */ new Set([
73
+ ...Object.keys(targetPkgJson[key] ?? {}),
74
+ ...Object.keys(sourcePkgJson[key] ?? {})
75
+ ])
76
+ ].sort().reduce((acc, dep) => {
77
+ let version = sourcePkgJson[key]?.[dep] ?? targetPkgJson[key]?.[dep];
78
+ if (dep.startsWith("@remix-run/") && remixVersion) {
79
+ version = remixVersion;
80
+ }
81
+ acc[dep] = version;
82
+ return acc;
83
+ }, {});
84
+ }
85
+ }
86
+ await writePackageJSON(projectDir, targetPkgJson);
87
+ }
88
+
89
+ export { SETUP_CSS_STRATEGIES, canWriteFiles, copyAssets, mergePackageJson };
@@ -0,0 +1,22 @@
1
+ import { mergePackageJson } from './assets.js';
2
+ import { getCodeFormatOptions } from '../../format-code.js';
3
+ import { injectCssBundlingLink } from './replacers.js';
4
+
5
+ async function setupCssModules({
6
+ rootDirectory,
7
+ appDirectory
8
+ }) {
9
+ const workPromise = Promise.all([
10
+ mergePackageJson("css-modules", rootDirectory),
11
+ getCodeFormatOptions(rootDirectory).then(
12
+ (formatConfig) => injectCssBundlingLink(appDirectory, formatConfig)
13
+ )
14
+ ]);
15
+ return {
16
+ workPromise,
17
+ generatedAssets: [],
18
+ helpUrl: "https://github.com/css-modules/css-modules"
19
+ };
20
+ }
21
+
22
+ export { setupCssModules };
@@ -0,0 +1,44 @@
1
+ import { renderSelectPrompt } from '@shopify/cli-kit/node/ui';
2
+ import { setupTailwind } from './tailwind.js';
3
+ import { setupPostCss } from './postcss.js';
4
+ import { setupCssModules } from './css-modules.js';
5
+ import { setupVanillaExtract } from './vanilla-extract.js';
6
+ export { SETUP_CSS_STRATEGIES } from './assets.js';
7
+
8
+ const CSS_STRATEGY_NAME_MAP = {
9
+ tailwind: "Tailwind",
10
+ "css-modules": "CSS Modules",
11
+ "vanilla-extract": "Vanilla Extract",
12
+ postcss: "CSS"
13
+ };
14
+ function setupCssStrategy(strategy, options, force) {
15
+ switch (strategy) {
16
+ case "tailwind":
17
+ return setupTailwind(options, force);
18
+ case "postcss":
19
+ return setupPostCss(options, force);
20
+ case "css-modules":
21
+ return setupCssModules(options);
22
+ case "vanilla-extract":
23
+ return setupVanillaExtract(options);
24
+ default:
25
+ throw new Error("Unknown strategy");
26
+ }
27
+ }
28
+ async function renderCssPrompt(options) {
29
+ const cssStrategies = Object.entries({
30
+ ...CSS_STRATEGY_NAME_MAP,
31
+ ...options?.extraChoices
32
+ });
33
+ return renderSelectPrompt({
34
+ message: "Select a styling library",
35
+ ...options,
36
+ choices: cssStrategies.map(([value, label]) => ({
37
+ value,
38
+ label
39
+ })),
40
+ defaultValue: "tailwind"
41
+ });
42
+ }
43
+
44
+ export { CSS_STRATEGY_NAME_MAP, renderCssPrompt, setupCssStrategy };
@@ -0,0 +1,34 @@
1
+ import { outputInfo } from '@shopify/cli-kit/node/output';
2
+ import { canWriteFiles, mergePackageJson, copyAssets } from './assets.js';
3
+ import { getCodeFormatOptions } from '../../format-code.js';
4
+ import { replaceRemixConfig } from './replacers.js';
5
+
6
+ async function setupPostCss({ rootDirectory, appDirectory, ...futureOptions }, force = false) {
7
+ const assetMap = {
8
+ "postcss.config.js": "postcss.config.js"
9
+ };
10
+ if (futureOptions.postcss) {
11
+ outputInfo(`PostCSS is already setup in ${rootDirectory}.`);
12
+ return;
13
+ }
14
+ if (!await canWriteFiles(assetMap, appDirectory, force)) {
15
+ outputInfo(
16
+ `Skipping CSS setup as some files already exist. You may use \`--force\` or \`-f\` to override it.`
17
+ );
18
+ return;
19
+ }
20
+ const workPromise = Promise.all([
21
+ mergePackageJson("postcss", rootDirectory),
22
+ copyAssets("postcss", assetMap, rootDirectory),
23
+ getCodeFormatOptions(rootDirectory).then(
24
+ (formatConfig) => replaceRemixConfig(rootDirectory, formatConfig, { postcss: true })
25
+ )
26
+ ]);
27
+ return {
28
+ workPromise,
29
+ generatedAssets: Object.values(assetMap),
30
+ helpUrl: "https://postcss.org/"
31
+ };
32
+ }
33
+
34
+ export { setupPostCss };