@shopify/cli-hydrogen 7.1.2 → 8.0.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 (136) 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 +3 -0
  7. package/dist/commands/hydrogen/deploy.js +121 -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 +7 -8
  11. package/dist/commands/hydrogen/env/pull.js +17 -1
  12. package/dist/commands/hydrogen/env/{push__unstable.js → push.js} +23 -50
  13. package/dist/commands/hydrogen/generate/route.js +1 -0
  14. package/dist/commands/hydrogen/init.js +45 -17
  15. package/dist/commands/hydrogen/link.js +20 -4
  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 -22
  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 +10 -2
  29. package/dist/generator-templates/starter/CHANGELOG.md +89 -0
  30. package/dist/generator-templates/starter/README.md +3 -44
  31. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -0
  32. package/dist/generator-templates/starter/app/lib/fragments.ts +2 -0
  33. package/dist/generator-templates/starter/app/root.tsx +2 -5
  34. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +1 -1
  35. package/dist/generator-templates/starter/app/routes/account.tsx +1 -1
  36. package/dist/generator-templates/starter/app/routes/collections.all.tsx +160 -0
  37. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +1 -2
  38. package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +6 -3
  39. package/dist/generator-templates/starter/{remix.env.d.ts → env.d.ts} +8 -2
  40. package/dist/generator-templates/starter/package.json +14 -9
  41. package/dist/generator-templates/starter/server.ts +2 -1
  42. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +59 -3
  43. package/dist/generator-templates/starter/vite.config.ts +21 -0
  44. package/dist/{commands/hydrogen/init.d.ts → init.d.ts} +11 -3
  45. package/dist/lib/check-lockfile.js +12 -18
  46. package/dist/lib/codegen.js +37 -13
  47. package/dist/lib/common.js +50 -0
  48. package/dist/lib/cpu-profiler.js +4 -1
  49. package/dist/lib/dev-shared.js +97 -0
  50. package/dist/lib/environment-variables.js +51 -30
  51. package/dist/lib/file.js +8 -1
  52. package/dist/lib/flags.js +37 -16
  53. package/dist/lib/graphql/admin/customer-application-update.js +29 -0
  54. package/dist/lib/graphql/admin/get-oxygen-data.js +1 -0
  55. package/dist/lib/graphql/admin/list-environments.js +1 -0
  56. package/dist/lib/graphql/admin/pull-variables.js +4 -4
  57. package/dist/lib/graphql/admin/test-helper.js +37 -0
  58. package/dist/lib/log.js +86 -13
  59. package/dist/lib/mini-oxygen/common.js +19 -33
  60. package/dist/lib/mini-oxygen/index.js +6 -2
  61. package/dist/lib/mini-oxygen/node.js +43 -31
  62. package/dist/lib/mini-oxygen/workerd.js +72 -165
  63. package/dist/lib/missing-routes.js +1 -1
  64. package/dist/lib/onboarding/common.js +82 -70
  65. package/dist/lib/onboarding/local.js +19 -9
  66. package/dist/lib/onboarding/remote.js +35 -30
  67. package/dist/lib/package-managers.js +24 -0
  68. package/dist/lib/remix-config.js +17 -1
  69. package/dist/lib/request-events.js +6 -1
  70. package/dist/lib/setups/i18n/replacers.js +9 -6
  71. package/dist/lib/setups/routes/generate.js +1 -0
  72. package/dist/lib/shell.js +2 -1
  73. package/dist/lib/shopify-config.js +19 -1
  74. package/dist/lib/template-diff.js +36 -15
  75. package/dist/lib/template-downloader.js +35 -5
  76. package/dist/lib/transpile/morph/typedefs.js +5 -2
  77. package/dist/lib/transpile/project.js +8 -4
  78. package/dist/lib/tunneling.js +44 -0
  79. package/dist/lib/virtual-routes.js +1 -1
  80. package/dist/lib/vite-config.js +39 -9
  81. package/oclif.manifest.json +711 -498
  82. package/package.json +32 -24
  83. package/dist/commands/hydrogen/deploy.test.js +0 -553
  84. package/dist/commands/hydrogen/env/list.test.js +0 -148
  85. package/dist/commands/hydrogen/env/pull.test.js +0 -207
  86. package/dist/commands/hydrogen/env/push__unstable.test.js +0 -383
  87. package/dist/commands/hydrogen/generate/route.test.js +0 -43
  88. package/dist/commands/hydrogen/init.test.js +0 -641
  89. package/dist/commands/hydrogen/link.test.js +0 -187
  90. package/dist/commands/hydrogen/list.test.js +0 -111
  91. package/dist/commands/hydrogen/setup.test.js +0 -61
  92. package/dist/commands/hydrogen/shortcut.test.js +0 -30
  93. package/dist/commands/hydrogen/unlink.test.js +0 -36
  94. package/dist/commands/hydrogen/upgrade.test.js +0 -786
  95. package/dist/generator-templates/starter/remix.config.js +0 -24
  96. package/dist/lib/auth.test.js +0 -157
  97. package/dist/lib/check-lockfile.test.js +0 -81
  98. package/dist/lib/check-version.test.js +0 -86
  99. package/dist/lib/environment-variables.test.js +0 -149
  100. package/dist/lib/file.test.js +0 -68
  101. package/dist/lib/flags.test.js +0 -43
  102. package/dist/lib/get-oxygen-deployment-data.test.js +0 -120
  103. package/dist/lib/gid.test.js +0 -15
  104. package/dist/lib/graphql/admin/client.test.js +0 -76
  105. package/dist/lib/graphql/admin/create-storefront.test.js +0 -64
  106. package/dist/lib/graphql/admin/link-storefront.test.js +0 -38
  107. package/dist/lib/graphql/admin/list-environments.test.js +0 -44
  108. package/dist/lib/graphql/admin/list-storefronts.test.js +0 -44
  109. package/dist/lib/graphql/admin/pull-variables.test.js +0 -43
  110. package/dist/lib/graphql/business-platform/user-account.test.js +0 -80
  111. package/dist/lib/log.test.js +0 -92
  112. package/dist/lib/mini-oxygen/assets.js +0 -134
  113. package/dist/lib/mini-oxygen/mini-oxygen.test.js +0 -214
  114. package/dist/lib/mini-oxygen/workerd-inspector-logs.js +0 -227
  115. package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +0 -200
  116. package/dist/lib/mini-oxygen/workerd-inspector.js +0 -219
  117. package/dist/lib/missing-routes.test.js +0 -45
  118. package/dist/lib/remix-version-check.test.js +0 -39
  119. package/dist/lib/remix-version-interop.test.js +0 -13
  120. package/dist/lib/setups/i18n/domains.test.js +0 -39
  121. package/dist/lib/setups/i18n/replacers.test.js +0 -261
  122. package/dist/lib/setups/i18n/subdomains.test.js +0 -39
  123. package/dist/lib/setups/i18n/subfolders.test.js +0 -39
  124. package/dist/lib/setups/routes/generate.test.js +0 -296
  125. package/dist/lib/shell.test.js +0 -111
  126. package/dist/lib/shopify-config.test.js +0 -199
  127. package/dist/lib/string.test.js +0 -16
  128. package/dist/lib/virtual-routes.test.js +0 -49
  129. package/dist/lib/vite/hydrogen-middleware.js +0 -82
  130. package/dist/lib/vite/mini-oxygen.js +0 -152
  131. package/dist/lib/vite/plugins.d.ts +0 -27
  132. package/dist/lib/vite/plugins.js +0 -139
  133. package/dist/lib/vite/shared.js +0 -10
  134. package/dist/lib/vite/utils.js +0 -55
  135. package/dist/lib/vite/worker-entry.js +0 -1518
  136. /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';
@@ -33,8 +33,10 @@ class Setup extends Command {
33
33
  }
34
34
  async function runSetup(options) {
35
35
  const controller = new AbortController();
36
- const remixConfig = await getRemixConfig(options.directory);
37
- const location = basename(remixConfig.rootDirectory);
36
+ const { rootDirectory, appDirectory, serverEntryPoint } = await getRemixConfig(
37
+ options.directory
38
+ );
39
+ const location = basename(rootDirectory);
38
40
  const cliCommandPromise = getCliCommand();
39
41
  let backgroundWorkPromise = Promise.resolve();
40
42
  const tasks = [
@@ -55,32 +57,29 @@ async function runSetup(options) {
55
57
  );
56
58
  let routes;
57
59
  if (needsRouteGeneration) {
58
- const typescript = !!remixConfig.tsconfigPath;
60
+ const templateRoot = getTemplateAppFile("..");
61
+ const [typescript, dtsFiles] = await Promise.all([
62
+ fileExists(joinPath(rootDirectory, "tsconfig.json")),
63
+ glob("*.d.ts", { cwd: templateRoot })
64
+ ]);
59
65
  backgroundWorkPromise = backgroundWorkPromise.then(
60
66
  () => 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
- )
67
+ ...dtsFiles.map(
68
+ (filename) => copyFile(
69
+ joinPath(templateRoot, filename),
70
+ resolvePath(rootDirectory, filename)
72
71
  )
73
- ] : [],
72
+ ),
74
73
  // Copy app entries
75
74
  generateProjectEntries({
76
- rootDirectory: remixConfig.rootDirectory,
77
- appDirectory: remixConfig.appDirectory,
75
+ rootDirectory,
76
+ appDirectory,
78
77
  typescript
79
78
  })
80
79
  ])
81
80
  ).then(async () => {
82
81
  routes = await setupRoutes(
83
- remixConfig.rootDirectory,
82
+ rootDirectory,
84
83
  typescript ? "ts" : "js",
85
84
  i18n
86
85
  );
@@ -88,7 +87,7 @@ async function runSetup(options) {
88
87
  }
89
88
  if (i18n) {
90
89
  backgroundWorkPromise = backgroundWorkPromise.then(
91
- () => setupI18nStrategy(i18n, remixConfig)
90
+ () => setupI18nStrategy(i18n, { rootDirectory, serverEntryPoint })
92
91
  );
93
92
  }
94
93
  let cliCommand = await Promise.resolve(cliCommandPromise);
@@ -112,7 +111,7 @@ async function runSetup(options) {
112
111
  {
113
112
  location,
114
113
  name: location,
115
- directory: remixConfig.rootDirectory
114
+ directory: rootDirectory
116
115
  },
117
116
  {
118
117
  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,14 @@ 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
  ],
13
21
  });