@shopify/cli-hydrogen 7.0.0 → 7.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 (40) hide show
  1. package/dist/commands/hydrogen/build.js +3 -8
  2. package/dist/commands/hydrogen/deploy.js +79 -32
  3. package/dist/commands/hydrogen/deploy.test.js +162 -5
  4. package/dist/commands/hydrogen/generate/route.js +6 -1
  5. package/dist/commands/hydrogen/init.test.js +2 -1
  6. package/dist/commands/hydrogen/setup.js +17 -19
  7. package/dist/generator-templates/starter/CHANGELOG.md +45 -0
  8. package/dist/generator-templates/starter/README.md +25 -2
  9. package/dist/generator-templates/starter/app/components/Cart.tsx +2 -2
  10. package/dist/generator-templates/starter/app/components/Layout.tsx +9 -1
  11. package/dist/generator-templates/starter/app/components/Search.tsx +44 -15
  12. package/dist/generator-templates/starter/app/lib/search.ts +29 -0
  13. package/dist/generator-templates/starter/app/root.tsx +0 -2
  14. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +2 -2
  15. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +1 -21
  16. package/dist/generator-templates/starter/app/routes/cart.tsx +1 -5
  17. package/dist/generator-templates/starter/app/routes/search.tsx +8 -2
  18. package/dist/generator-templates/starter/app/styles/app.css +10 -15
  19. package/dist/generator-templates/starter/package.json +7 -7
  20. package/dist/generator-templates/starter/remix.config.js +1 -0
  21. package/dist/generator-templates/starter/remix.env.d.ts +6 -2
  22. package/dist/generator-templates/starter/server.ts +1 -0
  23. package/dist/hooks/init.js +3 -3
  24. package/dist/lib/codegen.js +1 -8
  25. package/dist/lib/file.js +4 -1
  26. package/dist/lib/flags.js +6 -0
  27. package/dist/lib/graphiql-url.js +3 -0
  28. package/dist/lib/mini-oxygen/assets.js +17 -1
  29. package/dist/lib/onboarding/common.js +4 -3
  30. package/dist/lib/onboarding/local.js +7 -7
  31. package/dist/lib/onboarding/remote.js +2 -1
  32. package/dist/lib/setups/i18n/replacers.test.js +3 -2
  33. package/dist/lib/setups/routes/generate.js +58 -10
  34. package/dist/lib/setups/routes/templates/locale-check.js +9 -0
  35. package/dist/lib/setups/routes/templates/locale-check.ts +16 -0
  36. package/dist/lib/shell.js +1 -1
  37. package/dist/lib/template-diff.js +13 -3
  38. package/dist/virtual-routes/components/RequestDetails.jsx +2 -2
  39. package/oclif.manifest.json +47 -8
  40. package/package.json +5 -5
@@ -50,7 +50,7 @@ describe("i18n replacers", () => {
50
50
 
51
51
  import type {
52
52
  Storefront,
53
- CustomerClient,
53
+ CustomerAccount,
54
54
  HydrogenCart,
55
55
  } from "@shopify/hydrogen";
56
56
  import type {
@@ -92,7 +92,7 @@ describe("i18n replacers", () => {
92
92
  env: Env;
93
93
  cart: HydrogenCart;
94
94
  storefront: Storefront<I18nLocale>;
95
- customerAccount: CustomerClient;
95
+ customerAccount: CustomerAccount;
96
96
  session: AppSession;
97
97
  waitUntil: ExecutionContext["waitUntil"];
98
98
  }
@@ -192,6 +192,7 @@ describe("i18n replacers", () => {
192
192
  */
193
193
  const cart = createCartHandler({
194
194
  storefront,
195
+ customerAccount,
195
196
  getCartId: cartGetIdDefault(request.headers),
196
197
  setCartId: cartSetIdDefault(),
197
198
  cartQueryFragment: CART_QUERY_FRAGMENT,
@@ -1,4 +1,5 @@
1
- import { readdir } from 'fs/promises';
1
+ import { readdir } from 'node:fs/promises';
2
+ import { fileURLToPath } from 'node:url';
2
3
  import { fileExists, mkdir, copyFile, readFile, writeFile } from '@shopify/cli-kit/node/fs';
3
4
  import { joinPath, relativizePath, dirname, relativePath, resolvePath, basename } from '@shopify/cli-kit/node/path';
4
5
  import { AbortError } from '@shopify/cli-kit/node/error';
@@ -58,7 +59,8 @@ async function generateRoutes(options, remixConfig) {
58
59
  (item) => GENERATOR_ROUTE_DIR + "/" + item
59
60
  );
60
61
  const formatOptions = await getCodeFormatOptions(rootDirectory);
61
- const localePrefix = await getLocalePrefix(appDirectory, options);
62
+ const routesDirectory = joinPath(appDirectory, GENERATOR_ROUTE_DIR);
63
+ const localePrefix = await getLocalePrefix(routesDirectory, options);
62
64
  const typescript = !!(options.typescript ?? await fileExists(joinPath(rootDirectory, "tsconfig.json")));
63
65
  const routes = [];
64
66
  for (const route of routesArray) {
@@ -73,6 +75,15 @@ async function generateRoutes(options, remixConfig) {
73
75
  })
74
76
  );
75
77
  }
78
+ if (localePrefix) {
79
+ await copyLocaleNamelessRoute({
80
+ typescript,
81
+ localePrefix,
82
+ routesDirectory,
83
+ formatOptions,
84
+ adapter: options.adapter
85
+ });
86
+ }
76
87
  return {
77
88
  routes,
78
89
  routeGroups,
@@ -80,14 +91,12 @@ async function generateRoutes(options, remixConfig) {
80
91
  formatOptions
81
92
  };
82
93
  }
83
- async function getLocalePrefix(appDirectory, { localePrefix, routeName, v1RouteConvention }) {
94
+ async function getLocalePrefix(routesDirectory, { localePrefix, routeName, v1RouteConvention }) {
84
95
  if (localePrefix)
85
96
  return localePrefix;
86
97
  if (localePrefix !== void 0 || routeName === "all")
87
98
  return;
88
- const existingFiles = await readdir(joinPath(appDirectory, "routes")).catch(
89
- () => []
90
- );
99
+ const existingFiles = await readdir(routesDirectory).catch(() => []);
91
100
  const coreRouteWithLocaleRE = v1RouteConvention ? /^\(\$(\w+)\)$/ : /^\(\$(\w+)\)\.(_index|\$|cart).[jt]sx?$/;
92
101
  const coreRouteWithLocale = existingFiles.find(
93
102
  (file) => coreRouteWithLocaleRE.test(file)
@@ -161,10 +170,7 @@ async function generateProjectFile(routeFrom, {
161
170
  );
162
171
  }
163
172
  if (adapter) {
164
- templateContent = templateContent.replace(
165
- /@shopify\/remix-oxygen/g,
166
- adapter
167
- );
173
+ templateContent = replaceAdapters(templateContent, adapter);
168
174
  }
169
175
  templateContent = await formatCode(
170
176
  templateContent,
@@ -175,6 +181,9 @@ async function generateProjectFile(routeFrom, {
175
181
  }
176
182
  return result;
177
183
  }
184
+ function replaceAdapters(templateContent, adapter) {
185
+ return templateContent.replace(/@shopify\/remix-oxygen/g, adapter);
186
+ }
178
187
  function getDestinationRoute(routeFrom, localePrefix, options) {
179
188
  const routePath = routeFrom.replace(GENERATOR_ROUTE_DIR + "/", "");
180
189
  const filePrefix = localePrefix && !NO_LOCALE_PATTERNS.some((pattern) => pattern.test(routePath)) ? `($${localePrefix})` + (options.v1RouteConvention ? "/" : ".") : "";
@@ -219,5 +228,44 @@ async function renderRoutePrompt(options) {
219
228
  });
220
229
  return generateAll ? "all" : [];
221
230
  }
231
+ function copyLocaleNamelessRoute({
232
+ typescript,
233
+ localePrefix,
234
+ ...options
235
+ }) {
236
+ return copyRouteTemplate({
237
+ ...options,
238
+ typescript,
239
+ templateName: "locale-check.ts",
240
+ routeName: `($${localePrefix})${typescript ? ".tsx" : ".jsx"}`
241
+ });
242
+ }
243
+ async function copyRouteTemplate({
244
+ templateName,
245
+ routeName,
246
+ routesDirectory,
247
+ formatOptions,
248
+ typescript,
249
+ adapter
250
+ }) {
251
+ const routePath = joinPath(routesDirectory, routeName);
252
+ if (await fileExists(routePath))
253
+ return;
254
+ const templatePath = fileURLToPath(
255
+ new URL(`./templates/${templateName}`, import.meta.url)
256
+ );
257
+ if (!await fileExists(templatePath)) {
258
+ throw new Error("Unknown strategy");
259
+ }
260
+ let templateContent = await readFile(templatePath);
261
+ if (adapter) {
262
+ templateContent = replaceAdapters(templateContent, adapter);
263
+ }
264
+ if (!typescript) {
265
+ templateContent = await transpileFile(templateContent, templatePath);
266
+ }
267
+ templateContent = await formatCode(templateContent, formatOptions, routePath);
268
+ await writeFile(routePath, templateContent);
269
+ }
222
270
 
223
271
  export { ALL_ROUTE_CHOICES, generateProjectFile, generateRoutes, getResolvedRoutes, renderRoutePrompt };
@@ -0,0 +1,9 @@
1
+ async function loader({ params, context }) {
2
+ const { language, country } = context.storefront.i18n;
3
+ if (params.locale && params.locale.toLowerCase() !== `${language}-${country}`.toLowerCase()) {
4
+ throw new Response(null, { status: 404 });
5
+ }
6
+ return null;
7
+ }
8
+
9
+ export { loader };
@@ -0,0 +1,16 @@
1
+ import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
+
3
+ export async function loader({params, context}: LoaderFunctionArgs) {
4
+ const {language, country} = context.storefront.i18n;
5
+
6
+ if (
7
+ params.locale &&
8
+ params.locale.toLowerCase() !== `${language}-${country}`.toLowerCase()
9
+ ) {
10
+ // If the locale URL param is defined, yet we still are still at the default locale
11
+ // then the the locale param must be invalid, send to the 404 page
12
+ throw new Response(null, {status: 404});
13
+ }
14
+
15
+ return null;
16
+ }
package/dist/lib/shell.js CHANGED
@@ -155,7 +155,7 @@ async function createShortcutsForWindows() {
155
155
  return shells;
156
156
  }
157
157
  async function getCliCommand(directory = process.cwd(), forcePkgManager) {
158
- if (!forcePkgManager && await hasCliAlias()) {
158
+ if (await hasCliAlias()) {
159
159
  return ALIAS_NAME;
160
160
  }
161
161
  let cli = "npx";
@@ -67,15 +67,25 @@ ${colors.dim(
67
67
  async function applyTemplateDiff(targetDirectory, diffDirectory, templateDir = getStarterDir()) {
68
68
  const createFilter = (re) => (filepath) => !re.test(relativePath(templateDir, filepath));
69
69
  await copy(templateDir, targetDirectory, {
70
- filter: createFilter(/[\/\\](dist|node_modules|\.cache)(\/|\\|$)/i)
70
+ filter: createFilter(
71
+ /(^|\/|\\)(dist|node_modules|\.cache|CHANGELOG\.md)(\/|\\|$)/i
72
+ )
71
73
  });
72
74
  await copy(diffDirectory, targetDirectory, {
73
75
  filter: createFilter(
74
- /[\/\\](dist|node_modules|\.cache|package\.json|tsconfig\.json)(\/|\\|$)/i
76
+ /(^|\/|\\)(dist|node_modules|\.cache|package\.json|tsconfig\.json)(\/|\\|$)/i
75
77
  )
76
78
  });
77
79
  await mergePackageJson(diffDirectory, targetDirectory, {
78
- ignoredKeys: ["scripts"]
80
+ onResult: (pkgJson) => {
81
+ for (const key of ["build", "dev"]) {
82
+ const scriptLine = pkgJson.scripts?.[key];
83
+ if (pkgJson.scripts?.[key] && typeof scriptLine === "string") {
84
+ pkgJson.scripts[key] = scriptLine.replace(/\s+--diff/, "");
85
+ }
86
+ }
87
+ return pkgJson;
88
+ }
79
89
  });
80
90
  }
81
91
  async function copyDiffBuild(targetDirectory, diffDirectory) {
@@ -64,13 +64,13 @@ function RequestDetails({
64
64
  /* @__PURE__ */ jsx("div", { id: "tab1-panel", className: `tabPanel${activeTabClass(1)}`, children: /* @__PURE__ */ jsxs("div", { className: "grid-layout", children: [
65
65
  /* @__PURE__ */ jsx(DetailsRow, { rowName: "Name", value: requestInfo.displayName }),
66
66
  /* @__PURE__ */ jsx(DetailsRow, { rowName: "Request URL", value: requestInfo.url }),
67
- /* @__PURE__ */ jsx(
67
+ requestInfo.responseInit ? /* @__PURE__ */ jsx(
68
68
  DetailsRow,
69
69
  {
70
70
  rowName: "Status",
71
71
  value: `${requestInfo.responseInit?.status} ${requestInfo.responseInit?.statusText}`
72
72
  }
73
- ),
73
+ ) : null,
74
74
  /* @__PURE__ */ jsx(
75
75
  DetailsRow,
76
76
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "7.0.0",
2
+ "version": "7.1.0",
3
3
  "commands": {
4
4
  "hydrogen:build": {
5
5
  "id": "hydrogen:build",
@@ -19,19 +19,19 @@
19
19
  "sourcemap": {
20
20
  "name": "sourcemap",
21
21
  "type": "boolean",
22
- "description": "Generate sourcemaps for the build.",
22
+ "description": "Controls whether sourcemaps are generated. Default to true, use `--no-sourcemaps` to disable.",
23
23
  "allowNo": true
24
24
  },
25
25
  "bundle-stats": {
26
26
  "name": "bundle-stats",
27
27
  "type": "boolean",
28
- "description": "Show a bundle size summary after building.",
28
+ "description": "Show a bundle size summary after building. Defaults to true, use `--no-bundle-stats` to disable.",
29
29
  "allowNo": true
30
30
  },
31
31
  "lockfile-check": {
32
32
  "name": "lockfile-check",
33
33
  "type": "boolean",
34
- "description": "Checks that there is exactly 1 valid lockfile in the project.",
34
+ "description": "Checks that there is exactly 1 valid lockfile in the project. Defaults to true, use `--no-lockfile-check` to disable.",
35
35
  "allowNo": true
36
36
  },
37
37
  "disable-route-warning": {
@@ -149,6 +149,13 @@
149
149
  "required": false,
150
150
  "multiple": false
151
151
  },
152
+ "env-file": {
153
+ "name": "env-file",
154
+ "type": "option",
155
+ "description": "Path to an environment file to override existing environment variables for the deployment.",
156
+ "required": false,
157
+ "multiple": false
158
+ },
152
159
  "preview": {
153
160
  "name": "preview",
154
161
  "type": "boolean",
@@ -164,6 +171,13 @@
164
171
  "required": false,
165
172
  "allowNo": false
166
173
  },
174
+ "no-verify": {
175
+ "name": "no-verify",
176
+ "type": "boolean",
177
+ "description": "Skip the routability verification step after deployment.",
178
+ "required": false,
179
+ "allowNo": false
180
+ },
167
181
  "auth-bypass-token": {
168
182
  "name": "auth-bypass-token",
169
183
  "type": "boolean",
@@ -171,6 +185,19 @@
171
185
  "required": false,
172
186
  "allowNo": false
173
187
  },
188
+ "build-command": {
189
+ "name": "build-command",
190
+ "type": "option",
191
+ "description": "Specify a build command to run before deploying. If not specified, `shopify hydrogen build` will be used.",
192
+ "required": false,
193
+ "multiple": false
194
+ },
195
+ "lockfile-check": {
196
+ "name": "lockfile-check",
197
+ "type": "boolean",
198
+ "description": "Checks that there is exactly 1 valid lockfile in the project. Defaults to true, use `--no-lockfile-check` to disable.",
199
+ "allowNo": true
200
+ },
174
201
  "path": {
175
202
  "name": "path",
176
203
  "type": "option",
@@ -184,12 +211,12 @@
184
211
  "description": "Shop URL. It can be the shop prefix (janes-apparel) or the full myshopify.com URL (janes-apparel.myshopify.com, https://janes-apparel.myshopify.com).",
185
212
  "multiple": false
186
213
  },
187
- "no-json-output": {
188
- "name": "no-json-output",
214
+ "json-output": {
215
+ "name": "json-output",
189
216
  "type": "boolean",
190
- "description": "Prevents the command from creating a JSON file containing the deployment URL in CI environments.",
217
+ "description": "Create a JSON file containing the deployment details in CI environments. Defaults to true, use `--no-json-output` to disable.",
191
218
  "required": false,
192
- "allowNo": false
219
+ "allowNo": true
193
220
  },
194
221
  "token": {
195
222
  "name": "token",
@@ -777,6 +804,12 @@
777
804
  "description": "Generate TypeScript files",
778
805
  "allowNo": false
779
806
  },
807
+ "locale-param": {
808
+ "name": "locale-param",
809
+ "type": "option",
810
+ "description": "The param name in Remix routes for the i18n locale, if any. Example: `locale` becomes ($locale).",
811
+ "multiple": false
812
+ },
780
813
  "force": {
781
814
  "name": "force",
782
815
  "type": "boolean",
@@ -834,6 +867,12 @@
834
867
  "description": "Generate TypeScript files",
835
868
  "allowNo": false
836
869
  },
870
+ "locale-param": {
871
+ "name": "locale-param",
872
+ "type": "option",
873
+ "description": "The param name in Remix routes for the i18n locale, if any. Example: `locale` becomes ($locale).",
874
+ "multiple": false
875
+ },
837
876
  "force": {
838
877
  "name": "force",
839
878
  "type": "boolean",
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "@shopify:registry": "https://registry.npmjs.org"
6
6
  },
7
- "version": "7.0.0",
7
+ "version": "7.1.0",
8
8
  "license": "MIT",
9
9
  "type": "module",
10
10
  "scripts": {
@@ -16,7 +16,7 @@
16
16
  "test:watch": "cross-env SHOPIFY_UNIT_TEST=1 vitest --test-timeout=20000"
17
17
  },
18
18
  "devDependencies": {
19
- "@remix-run/dev": "^2.5.1",
19
+ "@remix-run/dev": "^2.6.0",
20
20
  "@types/diff": "^5.0.2",
21
21
  "@types/fs-extra": "^11.0.1",
22
22
  "@types/gunzip-maybe": "^1.4.0",
@@ -34,12 +34,12 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@ast-grep/napi": "0.11.0",
37
- "@graphql-codegen/cli": "5.0.0",
37
+ "@graphql-codegen/cli": "5.0.1",
38
38
  "@oclif/core": "2.11.7",
39
39
  "@shopify/cli-kit": "3.52.0",
40
- "@shopify/hydrogen-codegen": "^0.2.0",
40
+ "@shopify/hydrogen-codegen": "^0.2.1",
41
41
  "@shopify/mini-oxygen": "^2.2.5",
42
- "@shopify/oxygen-cli": "^4.0.0",
42
+ "@shopify/oxygen-cli": "^4.1.0",
43
43
  "ansi-escapes": "^6.2.0",
44
44
  "cli-truncate": "^4.0.0",
45
45
  "diff": "^5.1.0",