@shopify/cli-hydrogen 7.1.2 → 8.0.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 (145) hide show
  1. package/dist/commands/hydrogen/build-vite.js +19 -10
  2. package/dist/commands/hydrogen/build.js +10 -2
  3. package/dist/commands/hydrogen/check.js +1 -0
  4. package/dist/commands/hydrogen/codegen.js +1 -0
  5. package/dist/commands/hydrogen/customer-account/push.js +170 -0
  6. package/dist/commands/hydrogen/debug/cpu.js +10 -1
  7. package/dist/commands/hydrogen/deploy.js +110 -36
  8. package/dist/commands/hydrogen/dev-vite.js +128 -59
  9. package/dist/commands/hydrogen/dev.js +108 -51
  10. package/dist/commands/hydrogen/env/list.js +20 -28
  11. package/dist/commands/hydrogen/env/pull.js +29 -19
  12. package/dist/commands/hydrogen/env/{push__unstable.js → push.js} +34 -68
  13. package/dist/commands/hydrogen/generate/route.js +1 -0
  14. package/dist/commands/hydrogen/init.js +39 -21
  15. package/dist/commands/hydrogen/link.js +25 -6
  16. package/dist/commands/hydrogen/list.js +1 -0
  17. package/dist/commands/hydrogen/login.js +1 -0
  18. package/dist/commands/hydrogen/logout.js +1 -0
  19. package/dist/commands/hydrogen/preview.js +31 -16
  20. package/dist/commands/hydrogen/setup/css.js +8 -1
  21. package/dist/commands/hydrogen/setup/markets.js +1 -0
  22. package/dist/commands/hydrogen/setup/vite.js +244 -138
  23. package/dist/commands/hydrogen/setup.js +21 -23
  24. package/dist/commands/hydrogen/shortcut.js +10 -0
  25. package/dist/commands/hydrogen/unlink.js +1 -0
  26. package/dist/commands/hydrogen/upgrade.js +2 -1
  27. package/dist/generator-templates/assets/vite/package.json +3 -4
  28. package/dist/generator-templates/assets/vite/vite.config.js +15 -2
  29. package/dist/generator-templates/starter/CHANGELOG.md +129 -0
  30. package/dist/generator-templates/starter/README.md +3 -44
  31. package/dist/generator-templates/starter/app/components/Footer.tsx +1 -1
  32. package/dist/generator-templates/starter/app/components/Header.tsx +1 -1
  33. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -0
  34. package/dist/generator-templates/starter/app/lib/fragments.ts +2 -0
  35. package/dist/generator-templates/starter/app/lib/root-data.ts +11 -0
  36. package/dist/generator-templates/starter/app/root.tsx +4 -20
  37. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +1 -1
  38. package/dist/generator-templates/starter/app/routes/account.tsx +1 -1
  39. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +3 -3
  40. package/dist/generator-templates/starter/app/routes/cart.tsx +1 -1
  41. package/dist/generator-templates/starter/app/routes/collections.all.tsx +160 -0
  42. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +1 -2
  43. package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +6 -3
  44. package/dist/generator-templates/starter/{remix.env.d.ts → env.d.ts} +8 -2
  45. package/dist/generator-templates/starter/package.json +14 -9
  46. package/dist/generator-templates/starter/server.ts +2 -1
  47. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +59 -3
  48. package/dist/generator-templates/starter/vite.config.ts +26 -0
  49. package/dist/{commands/hydrogen/init.d.ts → init.d.ts} +11 -4
  50. package/dist/lib/check-lockfile.js +12 -18
  51. package/dist/lib/codegen.js +37 -13
  52. package/dist/lib/common.js +50 -0
  53. package/dist/lib/cpu-profiler.js +4 -1
  54. package/dist/lib/dev-shared.js +99 -0
  55. package/dist/lib/environment-variables.js +51 -30
  56. package/dist/lib/file.js +8 -1
  57. package/dist/lib/flags.js +37 -26
  58. package/dist/lib/get-oxygen-deployment-data.js +10 -17
  59. package/dist/lib/graphql/admin/customer-application-update.js +29 -0
  60. package/dist/lib/graphql/admin/get-oxygen-data.js +1 -0
  61. package/dist/lib/graphql/admin/list-environments.js +1 -0
  62. package/dist/lib/graphql/admin/pull-variables.js +4 -4
  63. package/dist/lib/graphql/admin/test-helper.js +37 -0
  64. package/dist/lib/log.js +86 -13
  65. package/dist/lib/mini-oxygen/common.js +19 -33
  66. package/dist/lib/mini-oxygen/index.js +6 -2
  67. package/dist/lib/mini-oxygen/node.js +43 -31
  68. package/dist/lib/mini-oxygen/workerd.js +72 -165
  69. package/dist/lib/missing-routes.js +1 -1
  70. package/dist/lib/onboarding/common.js +85 -70
  71. package/dist/lib/onboarding/local.js +19 -9
  72. package/dist/lib/onboarding/remote.js +35 -30
  73. package/dist/lib/package-managers.js +24 -0
  74. package/dist/lib/remix-config.js +17 -1
  75. package/dist/lib/render-errors.js +17 -10
  76. package/dist/lib/request-events.js +6 -1
  77. package/dist/lib/setups/i18n/replacers.js +9 -6
  78. package/dist/lib/setups/routes/generate.js +1 -0
  79. package/dist/lib/shell.js +2 -1
  80. package/dist/lib/shopify-config.js +19 -1
  81. package/dist/lib/template-diff.js +36 -15
  82. package/dist/lib/template-downloader.js +35 -5
  83. package/dist/lib/transpile/morph/functions.js +26 -8
  84. package/dist/lib/transpile/morph/typedefs.js +6 -4
  85. package/dist/lib/transpile/project.js +8 -4
  86. package/dist/lib/tunneling.js +44 -0
  87. package/dist/lib/verify-linked-storefront.js +24 -0
  88. package/dist/lib/virtual-routes.js +1 -1
  89. package/dist/lib/vite-config.js +39 -9
  90. package/oclif.manifest.json +704 -508
  91. package/package.json +32 -24
  92. package/dist/commands/hydrogen/deploy.test.js +0 -553
  93. package/dist/commands/hydrogen/env/list.test.js +0 -148
  94. package/dist/commands/hydrogen/env/pull.test.js +0 -207
  95. package/dist/commands/hydrogen/env/push__unstable.test.js +0 -383
  96. package/dist/commands/hydrogen/generate/route.test.js +0 -43
  97. package/dist/commands/hydrogen/init.test.js +0 -641
  98. package/dist/commands/hydrogen/link.test.js +0 -187
  99. package/dist/commands/hydrogen/list.test.js +0 -111
  100. package/dist/commands/hydrogen/setup.test.js +0 -61
  101. package/dist/commands/hydrogen/shortcut.test.js +0 -30
  102. package/dist/commands/hydrogen/unlink.test.js +0 -36
  103. package/dist/commands/hydrogen/upgrade.test.js +0 -786
  104. package/dist/generator-templates/starter/remix.config.js +0 -24
  105. package/dist/lib/auth.test.js +0 -157
  106. package/dist/lib/check-lockfile.test.js +0 -81
  107. package/dist/lib/check-version.test.js +0 -86
  108. package/dist/lib/environment-variables.test.js +0 -149
  109. package/dist/lib/file.test.js +0 -68
  110. package/dist/lib/flags.test.js +0 -43
  111. package/dist/lib/get-oxygen-deployment-data.test.js +0 -120
  112. package/dist/lib/gid.test.js +0 -15
  113. package/dist/lib/graphql/admin/client.test.js +0 -76
  114. package/dist/lib/graphql/admin/create-storefront.test.js +0 -64
  115. package/dist/lib/graphql/admin/link-storefront.test.js +0 -38
  116. package/dist/lib/graphql/admin/list-environments.test.js +0 -44
  117. package/dist/lib/graphql/admin/list-storefronts.test.js +0 -44
  118. package/dist/lib/graphql/admin/pull-variables.test.js +0 -43
  119. package/dist/lib/graphql/business-platform/user-account.test.js +0 -80
  120. package/dist/lib/log.test.js +0 -92
  121. package/dist/lib/mini-oxygen/assets.js +0 -134
  122. package/dist/lib/mini-oxygen/mini-oxygen.test.js +0 -214
  123. package/dist/lib/mini-oxygen/workerd-inspector-logs.js +0 -227
  124. package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +0 -200
  125. package/dist/lib/mini-oxygen/workerd-inspector.js +0 -219
  126. package/dist/lib/missing-routes.test.js +0 -45
  127. package/dist/lib/remix-version-check.test.js +0 -39
  128. package/dist/lib/remix-version-interop.test.js +0 -13
  129. package/dist/lib/setups/i18n/domains.test.js +0 -39
  130. package/dist/lib/setups/i18n/replacers.test.js +0 -261
  131. package/dist/lib/setups/i18n/subdomains.test.js +0 -39
  132. package/dist/lib/setups/i18n/subfolders.test.js +0 -39
  133. package/dist/lib/setups/routes/generate.test.js +0 -296
  134. package/dist/lib/shell.test.js +0 -111
  135. package/dist/lib/shopify-config.test.js +0 -199
  136. package/dist/lib/string.test.js +0 -16
  137. package/dist/lib/virtual-routes.test.js +0 -49
  138. package/dist/lib/vite/hydrogen-middleware.js +0 -82
  139. package/dist/lib/vite/mini-oxygen.js +0 -152
  140. package/dist/lib/vite/plugins.d.ts +0 -27
  141. package/dist/lib/vite/plugins.js +0 -139
  142. package/dist/lib/vite/shared.js +0 -10
  143. package/dist/lib/vite/utils.js +0 -55
  144. package/dist/lib/vite/worker-entry.js +0 -1518
  145. /package/dist/generator-templates/starter/{.eslintrc.js → .eslintrc.cjs} +0 -0
@@ -1,14 +1,17 @@
1
- import { resolvePath } from '@shopify/cli-kit/node/path';
1
+ import { resolvePath, joinPath } from '@shopify/cli-kit/node/path';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
- import { renderConfirmationPrompt, renderTasks, renderSuccess } from '@shopify/cli-kit/node/ui';
4
- import { moveFile, removeFile, copyFile, writeFile } from '@shopify/cli-kit/node/fs';
5
- import { installNodeModules, getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
3
+ import { renderTasks, renderSuccess } from '@shopify/cli-kit/node/ui';
4
+ import { removeFile, moveFile, readFile, writeFile, fileExists } from '@shopify/cli-kit/node/fs';
5
+ import { readAndParsePackageJson, installNodeModules, getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
6
6
  import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
7
- import { getRemixConfig } from '../../../lib/remix-config.js';
7
+ import { getRawRemixConfig } from '../../../lib/remix-config.js';
8
8
  import { replaceFileContent, mergePackageJson } from '../../../lib/file.js';
9
9
  import { importLangAstGrep } from '../../../lib/ast.js';
10
10
  import { getAssetDir } from '../../../lib/build.js';
11
- import { getCodeFormatOptions } from '../../../lib/format-code.js';
11
+ import { getCodeFormatOptions, formatCode } from '../../../lib/format-code.js';
12
+ import { hasViteConfig } from '../../../lib/vite-config.js';
13
+ import { AbortError } from '@shopify/cli-kit/node/error';
14
+ import { outputNewline } from '@shopify/cli-kit/node/output';
12
15
 
13
16
  class SetupVite extends Command {
14
17
  static description = "EXPERIMENTAL: Upgrades the project to use Vite.";
@@ -24,16 +27,35 @@ class SetupVite extends Command {
24
27
  });
25
28
  }
26
29
  }
30
+ const tailwindPostCSSConfig = `export default {
31
+ plugins: {
32
+ tailwindcss: {},
33
+ autoprefixer: {},
34
+ },
35
+ };
36
+ `;
27
37
  async function runSetupVite({ directory }) {
28
- const remixConfigPromise = getRemixConfig(directory);
29
- const shouldContinue = await renderConfirmationPrompt({
30
- message: "Are you sure you want to upgrade to Vite?\nThis is still an experimental feature and may not work as expected"
31
- });
32
- if (!shouldContinue)
33
- return;
38
+ outputNewline();
39
+ if (await hasViteConfig(directory)) {
40
+ throw new AbortError("This project already has a Vite config file.");
41
+ }
42
+ const [rawRemixConfig, pkgJson, formatOptions] = await Promise.all([
43
+ getRawRemixConfig(directory),
44
+ readAndParsePackageJson(joinPath(directory, "package.json")),
45
+ getCodeFormatOptions(directory)
46
+ ]);
47
+ const serverEntry = rawRemixConfig.server || "server.js";
48
+ const isTS = serverEntry.endsWith(".ts");
49
+ const fileExt = isTS ? "tsx" : "jsx";
50
+ const viteAssets = getAssetDir("vite");
51
+ const usesTailwind = !!rawRemixConfig.tailwind;
52
+ const postCssConfigPath = resolvePath(directory, "postcss.config.js");
34
53
  const handlePartialIssue = () => {
35
54
  };
36
55
  const backgroundWorkPromise = Promise.all([
56
+ removeFile(resolvePath(directory, "remix.config.js")).catch(
57
+ handlePartialIssue
58
+ ),
37
59
  moveFile(
38
60
  resolvePath(directory, "remix.env.d.ts"),
39
61
  resolvePath(directory, "env.d.ts")
@@ -48,137 +70,217 @@ async function runSetupVite({ directory }) {
48
70
  resolvePath(directory, ".eslintrc.js"),
49
71
  resolvePath(directory, ".eslintrc.cjs")
50
72
  ).catch(handlePartialIssue),
51
- moveFile(
52
- resolvePath(directory, "postcss.config.js"),
53
- resolvePath(directory, "postcss.config.cjs")
54
- ).catch(handlePartialIssue),
55
- remixConfigPromise.then((config) => {
56
- const serverEntry = config.serverEntryPoint || "server.js";
57
- const isTS = serverEntry.endsWith(".ts");
58
- const fileExt = isTS ? "tsx" : "jsx";
59
- const viteAssets = getAssetDir("vite");
60
- return Promise.all([
61
- removeFile(resolvePath(directory, "remix.config.js")).catch(
62
- handlePartialIssue
63
- ),
64
- copyFile(
65
- resolvePath(viteAssets, "vite.config.js"),
66
- resolvePath(directory, "vite.config." + fileExt.slice(0, 2))
67
- ),
68
- mergePackageJson(viteAssets, directory),
69
- replaceFileContent(
70
- resolvePath(directory, serverEntry),
71
- false,
72
- (content) => (isTS ? "// @ts-ignore\n" : "") + content.replace(
73
- "@remix-run/dev/server-build",
74
- "virtual:remix/server-build"
75
- )
76
- ),
77
- importLangAstGrep(fileExt).then(async (astGrep) => {
78
- const codeFormatOpt = getCodeFormatOptions(directory);
79
- const rootFilepath = resolvePath(
80
- config.appDirectory,
81
- "root." + fileExt
73
+ // Adjust PostCSS configuration:
74
+ readFile(resolvePath(directory, "postcss.config.js")).then(async (postCssContent) => {
75
+ const hasTailwindPlugin = postCssContent.includes("tailwindcss");
76
+ if (!hasTailwindPlugin && usesTailwind) {
77
+ postCssContent = await formatCode(
78
+ postCssContent.replace(
79
+ /(plugins:\s+{)/,
80
+ "$1\n tailwindcss: {},"
81
+ ),
82
+ formatOptions
83
+ );
84
+ }
85
+ const isCJS = postCssContent.includes("module.exports") || postCssContent.includes("exports.") || postCssContent.includes("require(");
86
+ return writeFile(
87
+ isCJS ? postCssConfigPath.replace(".js", ".cjs") : postCssConfigPath,
88
+ postCssContent
89
+ );
90
+ }).catch(async () => {
91
+ if (usesTailwind && !await fileExists(postCssConfigPath) && !await fileExists(postCssConfigPath.replace(".js", ".cjs"))) {
92
+ return writeFile(postCssConfigPath, tailwindPostCSSConfig);
93
+ }
94
+ }).catch(handlePartialIssue),
95
+ // Adjust dependencies:
96
+ mergePackageJson(viteAssets, directory, {
97
+ onResult(pkgJson2) {
98
+ if (pkgJson2.dependencies) {
99
+ delete pkgJson2.dependencies["@remix-run/css-bundle"];
100
+ }
101
+ if (pkgJson2.devDependencies) {
102
+ if (pkgJson2.devDependencies["@vanilla-extract/css"]) {
103
+ pkgJson2.devDependencies["@vanilla-extract/vite-plugin"] = "^4.0.0";
104
+ pkgJson2.devDependencies = Object.keys(pkgJson2.devDependencies).sort().reduce((acc, key) => {
105
+ acc[key] = pkgJson2.devDependencies?.[key];
106
+ return acc;
107
+ }, {});
108
+ }
109
+ }
110
+ return pkgJson2;
111
+ }
112
+ }),
113
+ // Build `vite.config.js` for the project:
114
+ readFile(resolvePath(viteAssets, "vite.config.js")).then(
115
+ async (viteConfigContent) => {
116
+ const hasVanillaExtract = !!pkgJson.devDependencies?.["@vanilla-extract/css"];
117
+ if (hasVanillaExtract) {
118
+ viteConfigContent = viteConfigContent.replace(
119
+ /\n\n/g,
120
+ `
121
+ import {vanillaExtractPlugin} from '@vanilla-extract/vite-plugin';
122
+
123
+ `
124
+ ).replace(/^(\s+)\],/m, "$1 vanillaExtractPlugin(),\n$1],");
125
+ }
126
+ const { future, appDirectory, ignoredRouteFiles, routes } = rawRemixConfig;
127
+ for (const flag of [
128
+ "v3_fetcherPersist",
129
+ "v3_throwAbortReason",
130
+ "v3_relativeSplatPath"
131
+ ]) {
132
+ if (!future?.[flag]) {
133
+ viteConfigContent = viteConfigContent.replace(
134
+ `${flag}: true`,
135
+ `${flag}: false`
136
+ );
137
+ }
138
+ }
139
+ if (appDirectory && appDirectory !== "app") {
140
+ viteConfigContent = viteConfigContent.replace(
141
+ /^(\s+)(future:)/m,
142
+ `$1appDirectory: '${appDirectory}',
143
+ $1$2`
82
144
  );
83
- await new Promise(async (resolve, reject) => {
84
- const fileNumber = await astGrep.findInFiles(
85
- {
86
- paths: [
87
- rootFilepath,
88
- resolvePath(config.appDirectory, "routes")
89
- ],
90
- matcher: {
91
- rule: {
92
- kind: "string_fragment",
93
- regex: "\\.css$",
94
- inside: {
95
- kind: "import_statement",
96
- stopBy: "end"
97
- }
98
- }
145
+ }
146
+ if (ignoredRouteFiles) {
147
+ viteConfigContent = viteConfigContent.replace(
148
+ /^(\s+)(future:)/m,
149
+ `$1ignoredRouteFiles: ${JSON.stringify(ignoredRouteFiles)},
150
+ $1$2`
151
+ );
152
+ }
153
+ if (routes) {
154
+ viteConfigContent = viteConfigContent.replace(
155
+ /^(\s+)(future:)/m,
156
+ `$1routes: ${routes.toString()},
157
+ $1$2`
158
+ );
159
+ }
160
+ const viteConfigPath = resolvePath(
161
+ directory,
162
+ "vite.config." + fileExt.slice(0, 2)
163
+ );
164
+ viteConfigContent = await formatCode(
165
+ viteConfigContent,
166
+ formatOptions,
167
+ viteConfigPath
168
+ );
169
+ return writeFile(viteConfigPath, viteConfigContent);
170
+ }
171
+ ),
172
+ // Adjust server entry:
173
+ replaceFileContent(
174
+ resolvePath(directory, serverEntry),
175
+ false,
176
+ (content) => (isTS ? "// @ts-ignore\n" : "") + content.replace(
177
+ "@remix-run/dev/server-build",
178
+ "virtual:remix/server-build"
179
+ )
180
+ ),
181
+ // Adjust CSS imports in the project routes:
182
+ importLangAstGrep(fileExt).then(async (astGrep) => {
183
+ const appDirectory = resolvePath(
184
+ directory,
185
+ rawRemixConfig.appDirectory ?? "app"
186
+ );
187
+ const rootFilepath = joinPath(appDirectory, "root." + fileExt);
188
+ await new Promise(async (resolve, reject) => {
189
+ const fileNumber = await astGrep.findInFiles(
190
+ {
191
+ paths: [rootFilepath, joinPath(appDirectory, "routes")],
192
+ matcher: {
193
+ rule: {
194
+ kind: "string_fragment",
195
+ regex: "\\.css$",
196
+ inside: {
197
+ kind: "import_statement",
198
+ stopBy: "end"
99
199
  }
100
- },
101
- async (err, nodes) => {
102
- if (err)
103
- reject(err);
104
- const nodeMap = {};
105
- nodes.forEach((node) => {
106
- const filename = node.getRoot().filename();
107
- const content = node.getRoot().root().text();
108
- const position = node.range().end.index;
109
- const tmp = nodeMap[filename] || { content, positions: [] };
110
- tmp.positions.push(position);
111
- nodeMap[filename] = tmp;
112
- });
113
- await Promise.all(
114
- Object.entries(nodeMap).flatMap(
115
- ([filename, { content, positions }]) => {
116
- positions.reverse();
117
- for (const position of positions) {
118
- content = content.slice(0, position) + "?url" + content.slice(position);
119
- }
120
- return writeFile(filename, content);
121
- }
122
- )
123
- );
124
- resolve(null);
125
200
  }
126
- );
127
- if (fileNumber === 0)
128
- resolve(null);
129
- });
130
- await replaceFileContent(
131
- rootFilepath,
132
- await codeFormatOpt,
133
- (content) => {
134
- const root = astGrep.parse(content).root();
135
- const liveReloadRegex = "LiveReload";
136
- const hasLiveReloadRule = {
137
- kind: "identifier",
138
- regex: liveReloadRegex
139
- };
140
- const liveReloadImport = root.find({
141
- rule: {
142
- kind: "import_specifier",
143
- regex: liveReloadRegex,
144
- inside: {
145
- kind: "import_statement",
146
- stopBy: "end"
201
+ }
202
+ },
203
+ async (err, nodes) => {
204
+ if (err)
205
+ reject(err);
206
+ const nodeMap = {};
207
+ nodes.forEach((node) => {
208
+ if (node.text().endsWith(".module.css")) {
209
+ return;
210
+ }
211
+ const filename = node.getRoot().filename();
212
+ const content = node.getRoot().root().text();
213
+ const position = node.range().end.index;
214
+ const tmp = nodeMap[filename] || { content, positions: [] };
215
+ tmp.positions.push(position);
216
+ nodeMap[filename] = tmp;
217
+ });
218
+ await Promise.all(
219
+ Object.entries(nodeMap).flatMap(
220
+ ([filename, { content, positions }]) => {
221
+ positions.reverse();
222
+ for (const position of positions) {
223
+ content = content.slice(0, position) + "?url" + content.slice(position);
147
224
  }
225
+ return writeFile(filename, content);
148
226
  }
149
- });
150
- const liveReloadElements = root.findAll({
151
- rule: {
152
- any: [
153
- {
154
- kind: "jsx_self_closing_element",
155
- has: hasLiveReloadRule
156
- },
157
- {
158
- kind: "jsx_element",
159
- has: {
160
- kind: "jsx_opening_element",
161
- has: hasLiveReloadRule
162
- }
163
- }
164
- ]
227
+ )
228
+ );
229
+ resolve(null);
230
+ }
231
+ );
232
+ if (fileNumber === 0)
233
+ resolve(null);
234
+ });
235
+ await replaceFileContent(rootFilepath, formatOptions, (content) => {
236
+ const root = astGrep.parse(content).root();
237
+ const liveReloadRegex = "LiveReload";
238
+ const hasLiveReloadRule = {
239
+ kind: "identifier",
240
+ regex: liveReloadRegex
241
+ };
242
+ const liveReloadImport = root.find({
243
+ rule: {
244
+ kind: "import_specifier",
245
+ regex: liveReloadRegex,
246
+ inside: {
247
+ kind: "import_statement",
248
+ stopBy: "end"
249
+ }
250
+ }
251
+ });
252
+ const liveReloadElements = root.findAll({
253
+ rule: {
254
+ any: [
255
+ {
256
+ kind: "jsx_self_closing_element",
257
+ has: hasLiveReloadRule
258
+ },
259
+ {
260
+ kind: "jsx_element",
261
+ has: {
262
+ kind: "jsx_opening_element",
263
+ has: hasLiveReloadRule
165
264
  }
166
- });
167
- for (const node of [
168
- // From bottom to top
169
- ...liveReloadElements.reverse(),
170
- liveReloadImport
171
- ]) {
172
- if (!node)
173
- continue;
174
- const { start, end } = node.range();
175
- content = content.slice(0, start.index) + content.slice(end.index);
176
265
  }
177
- return content.replace(/,\s*,/g, ",");
178
- }
179
- );
180
- })
181
- ]);
266
+ ]
267
+ }
268
+ });
269
+ for (const node of [
270
+ // From bottom to top
271
+ ...liveReloadElements.reverse(),
272
+ liveReloadImport
273
+ ]) {
274
+ if (!node)
275
+ continue;
276
+ const { start, end } = node.range();
277
+ content = content.slice(0, start.index) + content.slice(end.index);
278
+ }
279
+ return content.replace(/,\s*,/g, ",").replace(
280
+ /import\s+{\s+cssBundleHref\s+}\s+from\s+['"]@remix-run\/css-bundle['"];?\n/,
281
+ ""
282
+ ).replace(/\.\.\.\(\s*cssBundleHref[^)]+\),?/, "");
283
+ });
182
284
  })
183
285
  ]);
184
286
  await renderTasks([
@@ -201,8 +303,12 @@ async function runSetupVite({ directory }) {
201
303
  ]);
202
304
  renderSuccess({
203
305
  headline: `Your Vite project is ready!`,
204
- body: `We've modified your project to use Vite experimental.
205
- Please use git to review the changes.`
306
+ body: `We've modified your project to use Vite.
307
+ Please use Git to review the changes.`,
308
+ nextSteps: [
309
+ rawRemixConfig.mdx ? "Setup MDX support in Vite: https://remix.run/docs/en/main/future/vite#add-mdx-plugin" : "",
310
+ `See more information about Vite in Remix at https://remix.run/docs/en/main/future/vite`
311
+ ].filter(Boolean)
206
312
  });
207
313
  }
208
314
 
@@ -1,8 +1,8 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
2
  import { AbortController } from '@shopify/cli-kit/node/abort';
3
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';
4
+ import { resolvePath, basename, joinPath } from '@shopify/cli-kit/node/path';
5
+ import { fileExists, glob, copyFile } from '@shopify/cli-kit/node/fs';
6
6
  import { commonFlags, overrideFlag, flagsToCamelObject } from '../../lib/flags.js';
7
7
  import { renderI18nPrompt, setupI18nStrategy } from '../../lib/setups/i18n/index.js';
8
8
  import { getRemixConfig } from '../../lib/remix-config.js';
@@ -15,7 +15,6 @@ class Setup extends Command {
15
15
  static flags = {
16
16
  ...commonFlags.path,
17
17
  ...commonFlags.force,
18
- ...commonFlags.styling,
19
18
  ...commonFlags.markets,
20
19
  ...commonFlags.shortcut,
21
20
  ...overrideFlag(commonFlags.installDeps, {
@@ -33,8 +32,10 @@ class Setup extends Command {
33
32
  }
34
33
  async function runSetup(options) {
35
34
  const controller = new AbortController();
36
- const remixConfig = await getRemixConfig(options.directory);
37
- const location = basename(remixConfig.rootDirectory);
35
+ const { rootDirectory, appDirectory, serverEntryPoint } = await getRemixConfig(
36
+ options.directory
37
+ );
38
+ const location = basename(rootDirectory);
38
39
  const cliCommandPromise = getCliCommand();
39
40
  let backgroundWorkPromise = Promise.resolve();
40
41
  const tasks = [
@@ -55,32 +56,29 @@ async function runSetup(options) {
55
56
  );
56
57
  let routes;
57
58
  if (needsRouteGeneration) {
58
- const typescript = !!remixConfig.tsconfigPath;
59
+ const templateRoot = getTemplateAppFile("..");
60
+ const [typescript, dtsFiles] = await Promise.all([
61
+ fileExists(joinPath(rootDirectory, "tsconfig.json")),
62
+ glob("*.d.ts", { cwd: templateRoot })
63
+ ]);
59
64
  backgroundWorkPromise = backgroundWorkPromise.then(
60
65
  () => Promise.all([
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
- )
66
+ ...dtsFiles.map(
67
+ (filename) => copyFile(
68
+ joinPath(templateRoot, filename),
69
+ resolvePath(rootDirectory, filename)
72
70
  )
73
- ] : [],
71
+ ),
74
72
  // Copy app entries
75
73
  generateProjectEntries({
76
- rootDirectory: remixConfig.rootDirectory,
77
- appDirectory: remixConfig.appDirectory,
74
+ rootDirectory,
75
+ appDirectory,
78
76
  typescript
79
77
  })
80
78
  ])
81
79
  ).then(async () => {
82
80
  routes = await setupRoutes(
83
- remixConfig.rootDirectory,
81
+ rootDirectory,
84
82
  typescript ? "ts" : "js",
85
83
  i18n
86
84
  );
@@ -88,7 +86,7 @@ async function runSetup(options) {
88
86
  }
89
87
  if (i18n) {
90
88
  backgroundWorkPromise = backgroundWorkPromise.then(
91
- () => setupI18nStrategy(i18n, remixConfig)
89
+ () => setupI18nStrategy(i18n, { rootDirectory, serverEntryPoint })
92
90
  );
93
91
  }
94
92
  let cliCommand = await Promise.resolve(cliCommandPromise);
@@ -112,7 +110,7 @@ async function runSetup(options) {
112
110
  {
113
111
  location,
114
112
  name: location,
115
- directory: remixConfig.rootDirectory
113
+ directory: rootDirectory
116
114
  },
117
115
  {
118
116
  cliCommand,
@@ -3,6 +3,16 @@ import { renderSuccess, renderFatalError } from '@shopify/cli-kit/node/ui';
3
3
  import { ALIAS_NAME, createPlatformShortcut } from '../../lib/shell.js';
4
4
 
5
5
  class Shortcut extends Command {
6
+ static descriptionWithMarkdown = `Creates a global h2 shortcut for Shopify CLI using shell aliases.
7
+
8
+ The following shells are supported:
9
+
10
+ - Bash (using \`~/.bashrc\`)
11
+ - ZSH (using \`~/.zshrc\`)
12
+ - Fish (using \`~/.config/fish/functions\`)
13
+ - PowerShell (added to \`$PROFILE\`)
14
+
15
+ After the alias is created, you can call Shopify CLI from anywhere in your project using \`h2 <command>\`.`;
6
16
  static description = `Creates a global \`${ALIAS_NAME}\` shortcut for the Hydrogen CLI`;
7
17
  async run() {
8
18
  await runCreateShortcut();
@@ -5,6 +5,7 @@ import { commonFlags } from '../../lib/flags.js';
5
5
  import { getConfig, unsetStorefront } from '../../lib/shopify-config.js';
6
6
 
7
7
  class Unlink extends Command {
8
+ static descriptionWithMarkdown = "Unlinks your local development environment from a remote Hydrogen storefront.";
8
9
  static description = "Unlink a local project from a Hydrogen storefront.";
9
10
  static flags = {
10
11
  ...commonFlags.path
@@ -16,6 +16,7 @@ import { getProjectPaths } from '../../lib/remix-config.js';
16
16
 
17
17
  const INSTRUCTIONS_FOLDER = ".hydrogen";
18
18
  class Upgrade extends Command {
19
+ static descriptionWithMarkdown = "Upgrade Hydrogen project dependencies, preview features, fixes and breaking changes. The command also generates an instruction file for each upgrade.";
19
20
  static description = "Upgrade Remix and Hydrogen npm dependencies.";
20
21
  static flags = {
21
22
  ...commonFlags.path,
@@ -655,7 +656,7 @@ async function displayDevUpgradeNotice({
655
656
  const appPath = targetPath ? path.resolve(targetPath) : process.cwd();
656
657
  const { currentVersion } = await getHydrogenVersion({ appPath });
657
658
  const isPrerelease = semver.prerelease(currentVersion);
658
- if (isPrerelease) {
659
+ if (isPrerelease || /^[a-z]+$/i.test(currentVersion)) {
659
660
  return;
660
661
  }
661
662
  const changelog = await getChangelog();
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "type": "module",
3
3
  "scripts": {
4
- "build": "shopify hydrogen build-vite --codegen",
5
- "dev": "shopify hydrogen dev-vite --codegen"
4
+ "build": "shopify hydrogen build --codegen",
5
+ "dev": "shopify hydrogen dev --codegen"
6
6
  },
7
7
  "dependencies": {
8
- "@remix-run/node": "^2.8.0",
9
8
  "isbot": "^3.8.0"
10
9
  },
11
10
  "devDependencies": {
12
- "vite": "~5.1.0",
11
+ "vite": "^5.1.0",
13
12
  "vite-tsconfig-paths": "^4.3.1"
14
13
  }
15
14
  }
@@ -1,5 +1,6 @@
1
1
  import {defineConfig} from 'vite';
2
- import {hydrogen, oxygen} from '@shopify/cli-hydrogen/experimental-vite';
2
+ import {hydrogen} from '@shopify/hydrogen/vite';
3
+ import {oxygen} from '@shopify/mini-oxygen/vite';
3
4
  import {vitePlugin as remix} from '@remix-run/dev';
4
5
  import tsconfigPaths from 'vite-tsconfig-paths';
5
6
 
@@ -7,7 +8,19 @@ export default defineConfig({
7
8
  plugins: [
8
9
  hydrogen(),
9
10
  oxygen(),
10
- remix({buildDirectory: 'dist'}),
11
+ remix({
12
+ presets: [hydrogen.preset()],
13
+ future: {
14
+ v3_fetcherPersist: true,
15
+ v3_relativeSplatPath: true,
16
+ v3_throwAbortReason: true,
17
+ },
18
+ }),
11
19
  tsconfigPaths(),
12
20
  ],
21
+ build: {
22
+ // Allow a strict Content-Security-Policy
23
+ // withtout inlining assets as base64:
24
+ assetsInlineLimit: 0,
25
+ },
13
26
  });