@shopify/cli-hydrogen 5.0.2 → 5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/commands/hydrogen/build.js +21 -6
  2. package/dist/commands/hydrogen/check.js +2 -2
  3. package/dist/commands/hydrogen/codegen-unstable.js +14 -25
  4. package/dist/commands/hydrogen/dev.js +55 -43
  5. package/dist/commands/hydrogen/env/list.js +25 -24
  6. package/dist/commands/hydrogen/env/list.test.js +46 -43
  7. package/dist/commands/hydrogen/env/pull.js +53 -25
  8. package/dist/commands/hydrogen/env/pull.test.js +123 -42
  9. package/dist/commands/hydrogen/generate/route.js +31 -132
  10. package/dist/commands/hydrogen/generate/route.test.js +34 -126
  11. package/dist/commands/hydrogen/init.js +46 -127
  12. package/dist/commands/hydrogen/init.test.js +352 -100
  13. package/dist/commands/hydrogen/link.js +70 -69
  14. package/dist/commands/hydrogen/link.test.js +72 -107
  15. package/dist/commands/hydrogen/list.js +22 -12
  16. package/dist/commands/hydrogen/list.test.js +51 -48
  17. package/dist/commands/hydrogen/login.js +31 -0
  18. package/dist/commands/hydrogen/logout.js +21 -0
  19. package/dist/commands/hydrogen/preview.js +1 -1
  20. package/dist/commands/hydrogen/setup/css.js +79 -0
  21. package/dist/commands/hydrogen/setup/markets.js +53 -0
  22. package/dist/commands/hydrogen/setup.js +133 -0
  23. package/dist/commands/hydrogen/shortcut.js +2 -45
  24. package/dist/commands/hydrogen/shortcut.test.js +10 -37
  25. package/dist/generator-templates/assets/css-modules/package.json +6 -0
  26. package/dist/generator-templates/assets/postcss/package.json +10 -0
  27. package/dist/generator-templates/assets/postcss/postcss.config.js +8 -0
  28. package/dist/generator-templates/assets/tailwind/package.json +13 -0
  29. package/dist/generator-templates/assets/tailwind/postcss.config.js +10 -0
  30. package/dist/generator-templates/assets/tailwind/tailwind.config.js +8 -0
  31. package/dist/generator-templates/assets/tailwind/tailwind.css +3 -0
  32. package/dist/generator-templates/assets/vanilla-extract/package.json +9 -0
  33. package/dist/generator-templates/starter/.eslintignore +5 -0
  34. package/dist/generator-templates/starter/.eslintrc.js +18 -0
  35. package/dist/generator-templates/starter/.graphqlrc.yml +1 -0
  36. package/dist/generator-templates/starter/README.md +40 -0
  37. package/dist/generator-templates/starter/app/components/Aside.tsx +47 -0
  38. package/dist/generator-templates/starter/app/components/Cart.tsx +340 -0
  39. package/dist/generator-templates/starter/app/components/Footer.tsx +99 -0
  40. package/dist/generator-templates/starter/app/components/Header.tsx +178 -0
  41. package/dist/generator-templates/starter/app/components/Layout.tsx +95 -0
  42. package/dist/generator-templates/starter/app/components/Search.tsx +480 -0
  43. package/dist/generator-templates/starter/app/entry.client.tsx +12 -0
  44. package/dist/generator-templates/starter/app/entry.server.tsx +33 -0
  45. package/dist/generator-templates/starter/app/root.tsx +264 -0
  46. package/dist/generator-templates/starter/app/routes/$.tsx +7 -0
  47. package/dist/generator-templates/{routes → starter/app/routes}/[robots.txt].tsx +47 -69
  48. package/dist/generator-templates/starter/app/routes/[sitemap.xml].tsx +174 -0
  49. package/dist/generator-templates/starter/app/routes/_index.tsx +145 -0
  50. package/dist/generator-templates/starter/app/routes/account.$.tsx +9 -0
  51. package/dist/generator-templates/starter/app/routes/account.addresses.tsx +563 -0
  52. package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +309 -0
  53. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +196 -0
  54. package/dist/generator-templates/starter/app/routes/account.profile.tsx +289 -0
  55. package/dist/generator-templates/starter/app/routes/account.tsx +203 -0
  56. package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +157 -0
  57. package/dist/generator-templates/starter/app/routes/account_.login.tsx +143 -0
  58. package/dist/generator-templates/starter/app/routes/account_.logout.tsx +33 -0
  59. package/dist/generator-templates/starter/app/routes/account_.recover.tsx +124 -0
  60. package/dist/generator-templates/starter/app/routes/account_.register.tsx +207 -0
  61. package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +136 -0
  62. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +342 -0
  63. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +88 -0
  64. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +162 -0
  65. package/dist/generator-templates/starter/app/routes/blogs._index.tsx +94 -0
  66. package/dist/generator-templates/starter/app/routes/cart.tsx +104 -0
  67. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +184 -0
  68. package/dist/generator-templates/starter/app/routes/collections._index.tsx +120 -0
  69. package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +57 -0
  70. package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +94 -0
  71. package/dist/generator-templates/starter/app/routes/policies._index.tsx +63 -0
  72. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +418 -0
  73. package/dist/generator-templates/starter/app/routes/search.tsx +168 -0
  74. package/dist/generator-templates/starter/app/styles/app.css +473 -0
  75. package/dist/generator-templates/starter/app/styles/reset.css +129 -0
  76. package/dist/generator-templates/starter/app/utils.ts +46 -0
  77. package/dist/generator-templates/starter/package.json +43 -0
  78. package/dist/generator-templates/starter/public/favicon.svg +28 -0
  79. package/dist/generator-templates/starter/remix.config.js +26 -0
  80. package/dist/generator-templates/starter/remix.env.d.ts +39 -0
  81. package/dist/generator-templates/starter/server.ts +253 -0
  82. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +1906 -0
  83. package/dist/generator-templates/starter/tsconfig.json +22 -0
  84. package/dist/lib/auth.js +123 -0
  85. package/dist/lib/auth.test.js +157 -0
  86. package/dist/lib/build.js +51 -0
  87. package/dist/lib/check-version.js +3 -3
  88. package/dist/lib/check-version.test.js +24 -0
  89. package/dist/lib/codegen.js +26 -17
  90. package/dist/lib/environment-variables.js +68 -0
  91. package/dist/lib/environment-variables.test.js +147 -0
  92. package/dist/lib/file.js +41 -0
  93. package/dist/lib/file.test.js +69 -0
  94. package/dist/lib/flags.js +39 -2
  95. package/dist/lib/format-code.js +26 -0
  96. package/dist/lib/gid.js +12 -0
  97. package/dist/lib/{graphql.test.js → gid.test.js} +1 -1
  98. package/dist/lib/graphql/admin/client.js +27 -0
  99. package/dist/lib/graphql/admin/client.test.js +51 -0
  100. package/dist/lib/graphql/admin/create-storefront.js +13 -15
  101. package/dist/lib/graphql/admin/create-storefront.test.js +64 -0
  102. package/dist/lib/graphql/admin/fetch-job.js +6 -15
  103. package/dist/lib/graphql/admin/link-storefront.js +7 -11
  104. package/dist/lib/graphql/admin/link-storefront.test.js +38 -0
  105. package/dist/lib/graphql/admin/list-environments.js +2 -2
  106. package/dist/lib/graphql/admin/list-environments.test.js +44 -0
  107. package/dist/lib/graphql/admin/list-storefronts.js +7 -11
  108. package/dist/lib/graphql/admin/list-storefronts.test.js +44 -0
  109. package/dist/lib/graphql/admin/pull-variables.js +3 -3
  110. package/dist/lib/graphql/admin/pull-variables.test.js +37 -0
  111. package/dist/lib/graphql/business-platform/user-account.js +83 -0
  112. package/dist/lib/graphql/business-platform/user-account.test.js +80 -0
  113. package/dist/lib/log.js +216 -9
  114. package/dist/lib/log.test.js +92 -0
  115. package/dist/lib/mini-oxygen.js +19 -9
  116. package/dist/lib/missing-routes.js +0 -2
  117. package/dist/lib/onboarding/common.js +456 -0
  118. package/dist/lib/onboarding/index.js +2 -0
  119. package/dist/lib/onboarding/local.js +229 -0
  120. package/dist/lib/onboarding/remote.js +89 -0
  121. package/dist/lib/remix-config.js +135 -0
  122. package/dist/lib/remix-version-check.js +51 -0
  123. package/dist/lib/remix-version-check.test.js +38 -0
  124. package/dist/lib/remix-version-interop.js +6 -6
  125. package/dist/lib/remix-version-interop.test.js +12 -2
  126. package/dist/lib/render-errors.js +13 -11
  127. package/dist/lib/setups/css/assets.js +89 -0
  128. package/dist/lib/setups/css/css-modules.js +22 -0
  129. package/dist/lib/setups/css/index.js +44 -0
  130. package/dist/lib/setups/css/postcss.js +34 -0
  131. package/dist/lib/setups/css/replacers.js +137 -0
  132. package/dist/lib/setups/css/tailwind.js +54 -0
  133. package/dist/lib/setups/css/vanilla-extract.js +22 -0
  134. package/dist/lib/setups/i18n/domains.test.js +25 -0
  135. package/dist/lib/setups/i18n/index.js +46 -0
  136. package/dist/lib/setups/i18n/replacers.js +227 -0
  137. package/dist/lib/setups/i18n/subdomains.test.js +25 -0
  138. package/dist/lib/setups/i18n/subfolders.test.js +25 -0
  139. package/dist/lib/setups/i18n/templates/domains.js +14 -0
  140. package/dist/lib/setups/i18n/templates/domains.ts +25 -0
  141. package/dist/lib/setups/i18n/templates/subdomains.js +14 -0
  142. package/dist/lib/setups/i18n/templates/subdomains.ts +24 -0
  143. package/dist/lib/setups/i18n/templates/subfolders.js +14 -0
  144. package/dist/lib/setups/i18n/templates/subfolders.ts +28 -0
  145. package/dist/lib/setups/routes/generate.js +244 -0
  146. package/dist/lib/setups/routes/generate.test.js +313 -0
  147. package/dist/lib/shell.js +52 -5
  148. package/dist/lib/shell.test.js +42 -16
  149. package/dist/lib/shopify-config.js +23 -18
  150. package/dist/lib/shopify-config.test.js +63 -73
  151. package/dist/lib/template-downloader.js +9 -7
  152. package/dist/lib/transpile-ts.js +9 -29
  153. package/dist/virtual-routes/routes/index.jsx +40 -19
  154. package/oclif.manifest.json +710 -1
  155. package/package.json +20 -21
  156. package/dist/commands/hydrogen/build.d.ts +0 -23
  157. package/dist/commands/hydrogen/check.d.ts +0 -15
  158. package/dist/commands/hydrogen/codegen-unstable.d.ts +0 -15
  159. package/dist/commands/hydrogen/dev.d.ts +0 -21
  160. package/dist/commands/hydrogen/env/list.d.ts +0 -18
  161. package/dist/commands/hydrogen/env/pull.d.ts +0 -22
  162. package/dist/commands/hydrogen/g.d.ts +0 -10
  163. package/dist/commands/hydrogen/generate/route.d.ts +0 -32
  164. package/dist/commands/hydrogen/generate/route.test.d.ts +0 -1
  165. package/dist/commands/hydrogen/generate/routes.d.ts +0 -16
  166. package/dist/commands/hydrogen/init.d.ts +0 -24
  167. package/dist/commands/hydrogen/init.test.d.ts +0 -1
  168. package/dist/commands/hydrogen/link.d.ts +0 -23
  169. package/dist/commands/hydrogen/link.test.d.ts +0 -1
  170. package/dist/commands/hydrogen/list.d.ts +0 -21
  171. package/dist/commands/hydrogen/list.test.d.ts +0 -1
  172. package/dist/commands/hydrogen/preview.d.ts +0 -17
  173. package/dist/commands/hydrogen/shortcut.d.ts +0 -9
  174. package/dist/commands/hydrogen/shortcut.test.d.ts +0 -1
  175. package/dist/commands/hydrogen/unlink.d.ts +0 -16
  176. package/dist/commands/hydrogen/unlink.test.d.ts +0 -1
  177. package/dist/create-app.d.ts +0 -1
  178. package/dist/generator-templates/routes/[sitemap.xml].tsx +0 -235
  179. package/dist/generator-templates/routes/account/login.tsx +0 -103
  180. package/dist/generator-templates/routes/account/register.tsx +0 -103
  181. package/dist/generator-templates/routes/cart.tsx +0 -81
  182. package/dist/generator-templates/routes/collections/$collectionHandle.tsx +0 -104
  183. package/dist/generator-templates/routes/collections/index.tsx +0 -102
  184. package/dist/generator-templates/routes/graphiql.tsx +0 -10
  185. package/dist/generator-templates/routes/index.tsx +0 -40
  186. package/dist/generator-templates/routes/pages/$pageHandle.tsx +0 -112
  187. package/dist/generator-templates/routes/policies/$policyHandle.tsx +0 -140
  188. package/dist/generator-templates/routes/policies/index.tsx +0 -117
  189. package/dist/generator-templates/routes/products/$productHandle.tsx +0 -92
  190. package/dist/hooks/init.d.ts +0 -5
  191. package/dist/lib/admin-session.d.ts +0 -6
  192. package/dist/lib/admin-session.js +0 -16
  193. package/dist/lib/admin-session.test.d.ts +0 -1
  194. package/dist/lib/admin-session.test.js +0 -27
  195. package/dist/lib/admin-urls.d.ts +0 -8
  196. package/dist/lib/check-lockfile.d.ts +0 -3
  197. package/dist/lib/check-lockfile.test.d.ts +0 -1
  198. package/dist/lib/check-version.d.ts +0 -16
  199. package/dist/lib/check-version.test.d.ts +0 -1
  200. package/dist/lib/codegen.d.ts +0 -26
  201. package/dist/lib/combined-environment-variables.d.ts +0 -8
  202. package/dist/lib/combined-environment-variables.js +0 -57
  203. package/dist/lib/combined-environment-variables.test.d.ts +0 -1
  204. package/dist/lib/combined-environment-variables.test.js +0 -111
  205. package/dist/lib/config.d.ts +0 -20
  206. package/dist/lib/config.js +0 -141
  207. package/dist/lib/flags.d.ts +0 -27
  208. package/dist/lib/flags.test.d.ts +0 -1
  209. package/dist/lib/graphql/admin/create-storefront.d.ts +0 -17
  210. package/dist/lib/graphql/admin/fetch-job.d.ts +0 -23
  211. package/dist/lib/graphql/admin/link-storefront.d.ts +0 -14
  212. package/dist/lib/graphql/admin/list-environments.d.ts +0 -21
  213. package/dist/lib/graphql/admin/list-storefronts.d.ts +0 -25
  214. package/dist/lib/graphql/admin/pull-variables.d.ts +0 -21
  215. package/dist/lib/graphql.d.ts +0 -21
  216. package/dist/lib/graphql.js +0 -18
  217. package/dist/lib/graphql.test.d.ts +0 -1
  218. package/dist/lib/log.d.ts +0 -6
  219. package/dist/lib/mini-oxygen.d.ts +0 -22
  220. package/dist/lib/missing-routes.d.ts +0 -8
  221. package/dist/lib/missing-routes.test.d.ts +0 -1
  222. package/dist/lib/missing-storefronts.d.ts +0 -5
  223. package/dist/lib/missing-storefronts.js +0 -18
  224. package/dist/lib/process.d.ts +0 -6
  225. package/dist/lib/pull-environment-variables.d.ts +0 -20
  226. package/dist/lib/pull-environment-variables.js +0 -57
  227. package/dist/lib/pull-environment-variables.test.d.ts +0 -1
  228. package/dist/lib/pull-environment-variables.test.js +0 -174
  229. package/dist/lib/remix-version-interop.d.ts +0 -11
  230. package/dist/lib/remix-version-interop.test.d.ts +0 -1
  231. package/dist/lib/render-errors.d.ts +0 -16
  232. package/dist/lib/shell.d.ts +0 -11
  233. package/dist/lib/shell.test.d.ts +0 -1
  234. package/dist/lib/shop.d.ts +0 -7
  235. package/dist/lib/shop.js +0 -32
  236. package/dist/lib/shop.test.d.ts +0 -1
  237. package/dist/lib/shop.test.js +0 -78
  238. package/dist/lib/shopify-config.d.ts +0 -35
  239. package/dist/lib/shopify-config.test.d.ts +0 -1
  240. package/dist/lib/string.d.ts +0 -3
  241. package/dist/lib/string.test.d.ts +0 -1
  242. package/dist/lib/template-downloader.d.ts +0 -6
  243. package/dist/lib/transpile-ts.d.ts +0 -16
  244. package/dist/lib/user-errors.d.ts +0 -9
  245. package/dist/lib/user-errors.js +0 -11
  246. package/dist/lib/virtual-routes.d.ts +0 -7
  247. package/dist/lib/virtual-routes.test.d.ts +0 -1
  248. /package/dist/{commands/hydrogen/env/list.test.d.ts → lib/setups/css/common.js} +0 -0
  249. /package/dist/{commands/hydrogen/env/pull.test.d.ts → lib/setups/i18n/mock-i18n-types.js} +0 -0
@@ -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 };
@@ -0,0 +1,137 @@
1
+ import { AbortError } from '@shopify/cli-kit/node/error';
2
+ import { ts, tsx, js, jsx } from '@ast-grep/napi';
3
+ import { findFileWithExtension, replaceFileContent } from '../../file.js';
4
+
5
+ const astGrep = { ts, tsx, js, jsx };
6
+ async function replaceRemixConfig(rootDirectory, formatConfig, newProperties) {
7
+ const { filepath, astType } = await findFileWithExtension(
8
+ rootDirectory,
9
+ "remix.config"
10
+ );
11
+ if (!filepath || !astType) {
12
+ throw new AbortError(
13
+ `Could not find remix.config.js file in ${rootDirectory}`
14
+ );
15
+ }
16
+ await replaceFileContent(filepath, formatConfig, async (content) => {
17
+ const root = astGrep[astType].parse(content).root();
18
+ const remixConfigNode = root.find({
19
+ rule: {
20
+ kind: "object",
21
+ inside: {
22
+ any: [
23
+ {
24
+ kind: "export_statement"
25
+ },
26
+ {
27
+ kind: "assignment_expression",
28
+ has: {
29
+ kind: "member_expression",
30
+ field: "left",
31
+ pattern: "module.exports"
32
+ }
33
+ }
34
+ ]
35
+ }
36
+ }
37
+ });
38
+ if (!remixConfigNode) {
39
+ throw new AbortError(
40
+ "Could not find a default export in remix.config.js"
41
+ );
42
+ }
43
+ newProperties = { ...newProperties };
44
+ for (const key of Object.keys(newProperties)) {
45
+ const propertyNode = remixConfigNode.find({
46
+ rule: {
47
+ kind: "pair",
48
+ has: {
49
+ field: "key",
50
+ regex: `^${key}$`
51
+ }
52
+ }
53
+ });
54
+ if (propertyNode?.text().endsWith(" " + JSON.stringify(newProperties[key]))) {
55
+ delete newProperties[key];
56
+ }
57
+ }
58
+ if (Object.keys(newProperties).length === 0) {
59
+ return;
60
+ }
61
+ const childrenNodes = remixConfigNode.children();
62
+ const lastNode = childrenNodes.find((node) => node.text().startsWith("future:")) ?? childrenNodes.pop();
63
+ if (!lastNode) {
64
+ throw new AbortError("Could not add properties to Remix config");
65
+ }
66
+ const { start } = lastNode.range();
67
+ return content.slice(0, start.index) + JSON.stringify(newProperties).slice(1, -1) + "," + content.slice(start.index);
68
+ });
69
+ }
70
+ async function replaceRootLinks(appDirectory, formatConfig, importer) {
71
+ const { filepath, astType } = await findFileWithExtension(appDirectory, "root");
72
+ if (!filepath || !astType) {
73
+ throw new AbortError(`Could not find root file in ${appDirectory}`);
74
+ }
75
+ await replaceFileContent(filepath, formatConfig, async (content) => {
76
+ const importStatement = `import ${importer.isDefault ? importer.name : `{${importer.name}}`} from '${(importer.isAbsolute ? "" : "./") + importer.path}';`;
77
+ if (content.includes(importStatement.split("from")[0])) {
78
+ return;
79
+ }
80
+ const root = astGrep[astType].parse(content).root();
81
+ const lastImportNode = root.findAll({ rule: { kind: "import_statement" } }).pop();
82
+ const linksReturnNode = root.find({
83
+ utils: {
84
+ "has-links-id": {
85
+ has: {
86
+ kind: "identifier",
87
+ pattern: "links"
88
+ }
89
+ }
90
+ },
91
+ rule: {
92
+ kind: "return_statement",
93
+ pattern: "return [$$$]",
94
+ inside: {
95
+ any: [
96
+ {
97
+ kind: "function_declaration",
98
+ matches: "has-links-id"
99
+ },
100
+ {
101
+ kind: "variable_declarator",
102
+ matches: "has-links-id"
103
+ }
104
+ ],
105
+ stopBy: "end",
106
+ inside: {
107
+ stopBy: "end",
108
+ kind: "export_statement"
109
+ }
110
+ }
111
+ }
112
+ });
113
+ if (!lastImportNode || !linksReturnNode) {
114
+ throw new AbortError(
115
+ 'Could not find a "links" export in root file. Please add one and try again.'
116
+ );
117
+ }
118
+ const lastImportContent = lastImportNode.text();
119
+ const linksExportReturnContent = linksReturnNode.text();
120
+ const newLinkReturnItem = importer.isConditional ? `...(${importer.name} ? [{ rel: 'stylesheet', href: ${importer.name} }] : [])` : `{rel: 'stylesheet', href: ${importer.name}}`;
121
+ return content.replace(lastImportContent, lastImportContent + "\n" + importStatement).replace(
122
+ linksExportReturnContent,
123
+ linksExportReturnContent.replace("[", `[${newLinkReturnItem},`)
124
+ );
125
+ });
126
+ }
127
+ function injectCssBundlingLink(appDirectory, formatConfig) {
128
+ return replaceRootLinks(appDirectory, formatConfig, {
129
+ name: "cssBundleHref",
130
+ path: "@remix-run/css-bundle",
131
+ isDefault: false,
132
+ isConditional: true,
133
+ isAbsolute: true
134
+ });
135
+ }
136
+
137
+ export { injectCssBundlingLink, replaceRemixConfig, replaceRootLinks };
@@ -0,0 +1,54 @@
1
+ import { outputInfo } from '@shopify/cli-kit/node/output';
2
+ import { relativePath, joinPath } from '@shopify/cli-kit/node/path';
3
+ import { canWriteFiles, mergePackageJson, copyAssets } from './assets.js';
4
+ import { getCodeFormatOptions } from '../../format-code.js';
5
+ import { replaceRemixConfig, replaceRootLinks } from './replacers.js';
6
+
7
+ const tailwindCssPath = "styles/tailwind.css";
8
+ async function setupTailwind({ rootDirectory, appDirectory, ...futureOptions }, force = false) {
9
+ const relativeAppDirectory = relativePath(rootDirectory, appDirectory);
10
+ const assetMap = {
11
+ "tailwind.config.js": "tailwind.config.js",
12
+ "postcss.config.js": "postcss.config.js",
13
+ "tailwind.css": joinPath(relativeAppDirectory, tailwindCssPath)
14
+ };
15
+ if (futureOptions.tailwind && futureOptions.postcss) {
16
+ outputInfo(`Tailwind and PostCSS are already setup in ${rootDirectory}.`);
17
+ return;
18
+ }
19
+ if (!await canWriteFiles(assetMap, appDirectory, force)) {
20
+ outputInfo(
21
+ `Skipping CSS setup as some files already exist. You may use \`--force\` or \`-f\` to override it.`
22
+ );
23
+ return;
24
+ }
25
+ const workPromise = Promise.all([
26
+ mergePackageJson("tailwind", rootDirectory),
27
+ copyAssets(
28
+ "tailwind",
29
+ assetMap,
30
+ rootDirectory,
31
+ (content, filepath) => filepath === "tailwind.config.js" ? content.replace("{src-dir}", relativeAppDirectory) : content
32
+ ),
33
+ getCodeFormatOptions(rootDirectory).then(
34
+ (formatConfig) => Promise.all([
35
+ replaceRemixConfig(rootDirectory, formatConfig, {
36
+ tailwind: true,
37
+ postcss: true
38
+ }),
39
+ replaceRootLinks(appDirectory, formatConfig, {
40
+ name: "tailwindCss",
41
+ path: tailwindCssPath,
42
+ isDefault: true
43
+ })
44
+ ])
45
+ )
46
+ ]);
47
+ return {
48
+ workPromise,
49
+ generatedAssets: Object.values(assetMap),
50
+ helpUrl: "https://tailwindcss.com/docs/configuration"
51
+ };
52
+ }
53
+
54
+ export { setupTailwind };
@@ -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 setupVanillaExtract({
6
+ rootDirectory,
7
+ appDirectory
8
+ }) {
9
+ const workPromise = Promise.all([
10
+ mergePackageJson("vanilla-extract", rootDirectory),
11
+ getCodeFormatOptions(rootDirectory).then(
12
+ (formatConfig) => injectCssBundlingLink(appDirectory, formatConfig)
13
+ )
14
+ ]);
15
+ return {
16
+ workPromise,
17
+ generatedAssets: [],
18
+ helpUrl: "https://vanilla-extract.style/documentation/styling/"
19
+ };
20
+ }
21
+
22
+ export { setupVanillaExtract };
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getLocaleFromRequest } from './templates/domains.js';
3
+
4
+ describe("Setup i18n with domains", () => {
5
+ it("extracts the locale from the domain", () => {
6
+ expect(
7
+ getLocaleFromRequest(new Request("https://example.com"))
8
+ ).toMatchObject({
9
+ language: "EN",
10
+ country: "US"
11
+ });
12
+ expect(
13
+ getLocaleFromRequest(new Request("https://example.jp"))
14
+ ).toMatchObject({
15
+ language: "JA",
16
+ country: "JP"
17
+ });
18
+ expect(
19
+ getLocaleFromRequest(new Request("https://www.example.es"))
20
+ ).toMatchObject({
21
+ language: "ES",
22
+ country: "ES"
23
+ });
24
+ });
25
+ });
@@ -0,0 +1,46 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { renderSelectPrompt } from '@shopify/cli-kit/node/ui';
3
+ import { fileExists, readFile } from '@shopify/cli-kit/node/fs';
4
+ import { getCodeFormatOptions } from '../../format-code.js';
5
+ import { replaceServerI18n, replaceRemixEnv } from './replacers.js';
6
+
7
+ const SETUP_I18N_STRATEGIES = [
8
+ "subfolders",
9
+ "domains",
10
+ "subdomains"
11
+ ];
12
+ const I18N_STRATEGY_NAME_MAP = {
13
+ subfolders: "Subfolders (example.com/fr-ca/...)",
14
+ subdomains: "Subdomains (de.example.com/...)",
15
+ domains: "Top-level domains (example.jp/...)"
16
+ };
17
+ const I18N_CHOICES = [...SETUP_I18N_STRATEGIES, "none"];
18
+ async function setupI18nStrategy(strategy, options) {
19
+ const isTs = options.serverEntryPoint?.endsWith(".ts") ?? false;
20
+ const templatePath = fileURLToPath(
21
+ new URL(`./templates/${strategy}${isTs ? ".ts" : ".js"}`, import.meta.url)
22
+ );
23
+ if (!await fileExists(templatePath)) {
24
+ throw new Error("Unknown strategy");
25
+ }
26
+ const template = await readFile(templatePath);
27
+ const formatConfig = await getCodeFormatOptions(options.rootDirectory);
28
+ await replaceServerI18n(options, formatConfig, template);
29
+ await replaceRemixEnv(options, formatConfig, template);
30
+ }
31
+ async function renderI18nPrompt(options) {
32
+ const i18nStrategies = Object.entries({
33
+ ...I18N_STRATEGY_NAME_MAP,
34
+ ...options?.extraChoices
35
+ });
36
+ return renderSelectPrompt({
37
+ message: "Select a URL structure to support multiple markets",
38
+ ...options,
39
+ choices: i18nStrategies.map(([value, label]) => ({
40
+ value,
41
+ label
42
+ }))
43
+ });
44
+ }
45
+
46
+ export { I18N_CHOICES, I18N_STRATEGY_NAME_MAP, SETUP_I18N_STRATEGIES, renderI18nPrompt, setupI18nStrategy };