@shopify/cli-hydrogen 6.1.0 → 7.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 (97) hide show
  1. package/dist/commands/hydrogen/build.js +40 -78
  2. package/dist/commands/hydrogen/codegen.js +8 -3
  3. package/dist/commands/hydrogen/deploy.js +107 -35
  4. package/dist/commands/hydrogen/deploy.test.js +83 -13
  5. package/dist/commands/hydrogen/dev.js +30 -15
  6. package/dist/commands/hydrogen/init.js +1 -1
  7. package/dist/commands/hydrogen/init.test.js +155 -53
  8. package/dist/commands/hydrogen/link.js +5 -21
  9. package/dist/commands/hydrogen/link.test.js +10 -10
  10. package/dist/commands/hydrogen/preview.js +7 -6
  11. package/dist/commands/hydrogen/setup.js +0 -4
  12. package/dist/commands/hydrogen/setup.test.js +0 -1
  13. package/dist/commands/hydrogen/upgrade.js +15 -0
  14. package/dist/generator-templates/starter/.graphqlrc.yml +12 -1
  15. package/dist/generator-templates/starter/CHANGELOG.md +56 -0
  16. package/dist/generator-templates/starter/README.md +23 -0
  17. package/dist/generator-templates/starter/app/components/Cart.tsx +1 -1
  18. package/dist/generator-templates/starter/app/components/Header.tsx +5 -1
  19. package/dist/generator-templates/starter/app/components/Layout.tsx +1 -1
  20. package/dist/generator-templates/starter/app/components/Search.tsx +1 -1
  21. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerAddressMutations.ts +61 -0
  22. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +39 -0
  23. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerOrderQuery.ts +87 -0
  24. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +58 -0
  25. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +24 -0
  26. package/dist/generator-templates/starter/app/lib/fragments.ts +102 -0
  27. package/dist/generator-templates/starter/app/lib/session.ts +67 -0
  28. package/dist/generator-templates/starter/app/root.tsx +11 -45
  29. package/dist/generator-templates/starter/app/routes/account.$.tsx +8 -4
  30. package/dist/generator-templates/starter/app/routes/account._index.tsx +5 -0
  31. package/dist/generator-templates/starter/app/routes/account.addresses.tsx +215 -206
  32. package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +56 -163
  33. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +32 -109
  34. package/dist/generator-templates/starter/app/routes/account.profile.tsx +40 -180
  35. package/dist/generator-templates/starter/app/routes/account.tsx +20 -135
  36. package/dist/generator-templates/starter/app/routes/account_.authorize.tsx +5 -0
  37. package/dist/generator-templates/starter/app/routes/account_.login.tsx +3 -140
  38. package/dist/generator-templates/starter/app/routes/account_.logout.tsx +5 -24
  39. package/dist/generator-templates/starter/app/routes/cart.tsx +7 -5
  40. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +1 -1
  41. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +2 -2
  42. package/dist/generator-templates/starter/app/routes/search.tsx +1 -1
  43. package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +506 -0
  44. package/dist/generator-templates/starter/package.json +11 -11
  45. package/dist/generator-templates/starter/remix.config.js +4 -0
  46. package/dist/generator-templates/starter/remix.env.d.ts +4 -11
  47. package/dist/generator-templates/starter/server.ts +24 -167
  48. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +104 -881
  49. package/dist/hooks/init.js +4 -4
  50. package/dist/lib/auth.js +5 -10
  51. package/dist/lib/build.js +6 -1
  52. package/dist/lib/bundle/analyzer.js +36 -26
  53. package/dist/lib/codegen.js +58 -18
  54. package/dist/lib/defer.js +12 -0
  55. package/dist/lib/file.js +52 -3
  56. package/dist/lib/flags.js +15 -8
  57. package/dist/lib/get-oxygen-deployment-data.test.js +4 -2
  58. package/dist/lib/graphql/admin/client.test.js +2 -2
  59. package/dist/lib/graphql/admin/get-oxygen-data.js +1 -0
  60. package/dist/lib/log.js +31 -14
  61. package/dist/lib/mini-oxygen/index.js +4 -5
  62. package/dist/lib/mini-oxygen/mini-oxygen.test.js +214 -0
  63. package/dist/lib/mini-oxygen/node.js +4 -2
  64. package/dist/lib/mini-oxygen/workerd-inspector-logs.js +2 -2
  65. package/dist/lib/mini-oxygen/workerd.js +27 -10
  66. package/dist/lib/missing-routes.js +6 -3
  67. package/dist/lib/onboarding/common.js +40 -9
  68. package/dist/lib/onboarding/local.js +19 -11
  69. package/dist/lib/onboarding/remote.js +48 -28
  70. package/dist/lib/request-events.js +65 -31
  71. package/dist/lib/setups/css/assets.js +1 -46
  72. package/dist/lib/setups/css/css-modules.js +3 -2
  73. package/dist/lib/setups/css/postcss.js +4 -2
  74. package/dist/lib/setups/css/tailwind.js +4 -2
  75. package/dist/lib/setups/css/vanilla-extract.js +3 -2
  76. package/dist/lib/setups/i18n/replacers.test.js +54 -38
  77. package/dist/lib/template-diff.js +89 -0
  78. package/dist/lib/template-downloader.js +3 -2
  79. package/dist/lib/transpile/project.js +1 -1
  80. package/dist/virtual-routes/assets/debug-network.css +592 -0
  81. package/dist/virtual-routes/assets/favicon-dark.svg +20 -0
  82. package/dist/virtual-routes/components/FlameChartWrapper.jsx +8 -10
  83. package/dist/virtual-routes/components/IconClose.jsx +38 -0
  84. package/dist/virtual-routes/components/IconDiscard.jsx +44 -0
  85. package/dist/virtual-routes/components/RequestDetails.jsx +179 -0
  86. package/dist/virtual-routes/components/RequestTable.jsx +92 -0
  87. package/dist/virtual-routes/components/RequestWaterfall.jsx +151 -0
  88. package/dist/virtual-routes/lib/useDebugNetworkServer.jsx +176 -0
  89. package/dist/virtual-routes/routes/subrequest-profiler.jsx +243 -0
  90. package/oclif.manifest.json +54 -61
  91. package/package.json +14 -11
  92. package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +0 -161
  93. package/dist/generator-templates/starter/app/routes/account_.recover.tsx +0 -129
  94. package/dist/generator-templates/starter/app/routes/account_.register.tsx +0 -207
  95. package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +0 -136
  96. package/dist/virtual-routes/routes/debug-network.jsx +0 -289
  97. /package/dist/generator-templates/starter/app/{utils.ts → lib/variants.ts} +0 -0
@@ -1,14 +1,15 @@
1
+ import path from 'node:path';
1
2
  import { EventEmitter } from 'node:events';
2
3
  import { ReadableStream } from 'node:stream/web';
3
4
  import { getGraphiQLUrl } from './graphiql-url.js';
4
- import { Response } from '@shopify/mini-oxygen';
5
+ import { mapSourcePosition } from 'source-map-support';
5
6
 
6
7
  const H2O_BINDING_NAME = "H2O_LOG_EVENT";
7
- let ResponseConstructor = Response;
8
+ let ResponseConstructor;
8
9
  function setConstructors(constructors) {
9
10
  ResponseConstructor = constructors.Response;
10
11
  }
11
- const DEV_ROUTES = /* @__PURE__ */ new Set(["/graphiql", "/debug-network"]);
12
+ const DEV_ROUTES = /* @__PURE__ */ new Set(["/graphiql", "/subrequest-profiler"]);
12
13
  const EVENT_MAP = {
13
14
  request: "Request",
14
15
  subrequest: "Sub request"
@@ -16,9 +17,9 @@ const EVENT_MAP = {
16
17
  async function getRequestInfo(request) {
17
18
  const data = await request.json();
18
19
  return {
19
- id: data.requestId ?? "",
20
+ ...data,
21
+ requestId: data.requestId ?? "",
20
22
  eventType: data.eventType || "unknown",
21
- startTime: data.startTime,
22
23
  endTime: data.endTime || Date.now(),
23
24
  purpose: data.purpose === "prefetch" ? "(prefetch)" : "",
24
25
  cacheStatus: data.cacheStatus ?? "",
@@ -34,33 +35,66 @@ async function clearHistory(request) {
34
35
  eventHistory.length = 0;
35
36
  return createResponse();
36
37
  }
37
- async function logRequestEvent(request) {
38
- const url = new URL(request.url);
39
- if (DEV_ROUTES.has(url.pathname)) {
40
- return createResponse();
41
- }
42
- const { eventType, purpose, graphql, ...data } = await getRequestInfo(request);
43
- let graphiqlLink = "";
44
- let description = request.url;
45
- if (eventType === "subrequest") {
46
- description = graphql?.query.match(/(query|mutation)\s+(\w+)/)?.[0]?.replace(/\s+/, " ") || decodeURIComponent(url.search.slice(1));
47
- if (graphql) {
48
- graphiqlLink = getGraphiQLUrl({ graphql });
38
+ function createLogRequestEvent(options) {
39
+ return async function logRequestEvent(request) {
40
+ const url = new URL(request.url);
41
+ if (DEV_ROUTES.has(url.pathname)) {
42
+ return createResponse();
43
+ }
44
+ const {
45
+ url: displayUrl,
46
+ displayName: displayNameData,
47
+ eventType,
48
+ purpose,
49
+ graphql,
50
+ stackInfo,
51
+ ...data
52
+ } = await getRequestInfo(request);
53
+ let graphiqlLink = "";
54
+ let descriptionUrl = request.url;
55
+ let displayName = displayNameData;
56
+ if (eventType === "subrequest") {
57
+ displayName = displayName || graphql?.query.match(/(query|mutation)\s+(\w+)/)?.[0]?.replace(/\s+/, " ");
58
+ descriptionUrl = displayUrl || request.url;
59
+ if (graphql) {
60
+ graphiqlLink = getGraphiQLUrl({ graphql });
61
+ }
62
+ }
63
+ let stackLine = null;
64
+ let stackLink = null;
65
+ if (stackInfo?.file) {
66
+ if (!path.isAbsolute(stackInfo.file) && options?.absoluteBundlePath) {
67
+ stackInfo.file = options.absoluteBundlePath;
68
+ }
69
+ const { source, line, column } = mapSourcePosition({
70
+ source: stackInfo.file,
71
+ line: stackInfo.line ?? 0,
72
+ column: stackInfo.column ?? 0
73
+ });
74
+ stackLine = `${source}:${line}:${column + 1}`;
75
+ stackLink = `vscode://${path.join("file", stackLine)}`;
76
+ stackLine = stackLine.split(path.sep + "app" + path.sep)[1] ?? stackLine;
77
+ if (stackInfo.func) {
78
+ stackLine = `${stackInfo.func.replace(/\d+$/, "")} (${stackLine})`;
79
+ }
49
80
  }
50
- }
51
- const event = {
52
- event: EVENT_MAP[eventType] || eventType,
53
- data: JSON.stringify({
54
- ...data,
55
- url: `${purpose} ${description}`.trim(),
56
- graphiqlLink
57
- })
81
+ const event = {
82
+ event: EVENT_MAP[eventType] || eventType,
83
+ data: JSON.stringify({
84
+ ...data,
85
+ displayName,
86
+ url: `${purpose} ${descriptionUrl}`.trim(),
87
+ graphiqlLink,
88
+ stackLine,
89
+ stackLink
90
+ })
91
+ };
92
+ eventHistory.push(event);
93
+ if (eventHistory.length > 100)
94
+ eventHistory.shift();
95
+ eventEmitter.emit("request", event);
96
+ return createResponse();
58
97
  };
59
- eventHistory.push(event);
60
- if (eventHistory.length > 100)
61
- eventHistory.shift();
62
- eventEmitter.emit("request", event);
63
- return createResponse();
64
98
  }
65
99
  function streamRequestEvents(request) {
66
100
  const stream = new ReadableStream({
@@ -101,4 +135,4 @@ function handleDebugNetworkRequest(request) {
101
135
  return request.method === "DELETE" ? clearHistory() : streamRequestEvents(request);
102
136
  }
103
137
 
104
- export { DEV_ROUTES, H2O_BINDING_NAME, handleDebugNetworkRequest, logRequestEvent, setConstructors };
138
+ export { DEV_ROUTES, H2O_BINDING_NAME, createLogRequestEvent, handleDebugNetworkRequest, setConstructors };
@@ -1,7 +1,6 @@
1
1
  import { readFile, writeFile, fileExists } from '@shopify/cli-kit/node/fs';
2
2
  import { joinPath } from '@shopify/cli-kit/node/path';
3
3
  import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
4
- import { readAndParsePackageJson, writePackageJSON } from '@shopify/cli-kit/node/node-package-manager';
5
4
  import { GENERATOR_SETUP_ASSETS_SUB_DIRS, getAssetDir } from '../../build.js';
6
5
 
7
6
  const SETUP_CSS_STRATEGIES = GENERATOR_SETUP_ASSETS_SUB_DIRS;
@@ -41,49 +40,5 @@ async function canWriteFiles(assetMap, directory, force) {
41
40
  }
42
41
  return true;
43
42
  }
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
43
 
89
- export { SETUP_CSS_STRATEGIES, canWriteFiles, copyAssets, mergePackageJson };
44
+ export { SETUP_CSS_STRATEGIES, canWriteFiles, copyAssets };
@@ -1,13 +1,14 @@
1
- import { mergePackageJson } from './assets.js';
1
+ import { mergePackageJson } from '../../file.js';
2
2
  import { getCodeFormatOptions } from '../../format-code.js';
3
3
  import { injectCssBundlingLink } from './replacers.js';
4
+ import { getAssetDir } from '../../build.js';
4
5
 
5
6
  async function setupCssModules({
6
7
  rootDirectory,
7
8
  appDirectory
8
9
  }) {
9
10
  const workPromise = Promise.all([
10
- mergePackageJson("css-modules", rootDirectory),
11
+ mergePackageJson(getAssetDir("css-modules"), rootDirectory),
11
12
  getCodeFormatOptions(rootDirectory).then(
12
13
  (formatConfig) => injectCssBundlingLink(appDirectory, formatConfig)
13
14
  )
@@ -1,5 +1,7 @@
1
1
  import { outputInfo } from '@shopify/cli-kit/node/output';
2
- import { canWriteFiles, mergePackageJson, copyAssets } from './assets.js';
2
+ import { mergePackageJson } from '../../file.js';
3
+ import { canWriteFiles, copyAssets } from './assets.js';
4
+ import { getAssetDir } from '../../build.js';
3
5
 
4
6
  async function setupPostCss({ rootDirectory, appDirectory, ...futureOptions }, force = false) {
5
7
  const assetMap = {
@@ -16,7 +18,7 @@ async function setupPostCss({ rootDirectory, appDirectory, ...futureOptions }, f
16
18
  return;
17
19
  }
18
20
  const workPromise = Promise.all([
19
- mergePackageJson("postcss", rootDirectory),
21
+ mergePackageJson(getAssetDir("postcss"), rootDirectory),
20
22
  copyAssets("postcss", assetMap, rootDirectory)
21
23
  ]);
22
24
  return {
@@ -1,8 +1,10 @@
1
1
  import { outputInfo } from '@shopify/cli-kit/node/output';
2
2
  import { relativePath, joinPath } from '@shopify/cli-kit/node/path';
3
- import { canWriteFiles, mergePackageJson, copyAssets } from './assets.js';
3
+ import { mergePackageJson } from '../../file.js';
4
+ import { canWriteFiles, copyAssets } from './assets.js';
4
5
  import { getCodeFormatOptions } from '../../format-code.js';
5
6
  import { replaceRootLinks } from './replacers.js';
7
+ import { getAssetDir } from '../../build.js';
6
8
 
7
9
  const tailwindCssPath = "styles/tailwind.css";
8
10
  async function setupTailwind({ rootDirectory, appDirectory, ...futureOptions }, force = false) {
@@ -23,7 +25,7 @@ async function setupTailwind({ rootDirectory, appDirectory, ...futureOptions },
23
25
  return;
24
26
  }
25
27
  const workPromise = Promise.all([
26
- mergePackageJson("tailwind", rootDirectory),
28
+ mergePackageJson(getAssetDir("tailwind"), rootDirectory),
27
29
  copyAssets(
28
30
  "tailwind",
29
31
  assetMap,
@@ -1,13 +1,14 @@
1
- import { mergePackageJson } from './assets.js';
1
+ import { mergePackageJson } from '../../file.js';
2
2
  import { getCodeFormatOptions } from '../../format-code.js';
3
3
  import { injectCssBundlingLink } from './replacers.js';
4
+ import { getAssetDir } from '../../build.js';
4
5
 
5
6
  async function setupVanillaExtract({
6
7
  rootDirectory,
7
8
  appDirectory
8
9
  }) {
9
10
  const workPromise = Promise.all([
10
- mergePackageJson("vanilla-extract", rootDirectory),
11
+ mergePackageJson(getAssetDir("vanilla-extract"), rootDirectory),
11
12
  getCodeFormatOptions(rootDirectory).then(
12
13
  (formatConfig) => injectCssBundlingLink(appDirectory, formatConfig)
13
14
  )
@@ -41,26 +41,29 @@ describe("i18n replacers", () => {
41
41
  const newContent = await readFile(joinPath(tmpDir, remixDts));
42
42
  expect(() => checkTypes(newContent)).not.toThrow();
43
43
  expect(newContent).toMatchInlineSnapshot(`
44
- "/// <reference types=\\"@remix-run/dev\\" />
45
- /// <reference types=\\"@shopify/remix-oxygen\\" />
46
- /// <reference types=\\"@shopify/oxygen-workers-types\\" />
44
+ "/// <reference types="@remix-run/dev" />
45
+ /// <reference types="@shopify/remix-oxygen" />
46
+ /// <reference types="@shopify/oxygen-workers-types" />
47
47
 
48
48
  // Enhance TypeScript's built-in typings.
49
- import \\"@total-typescript/ts-reset\\";
49
+ import "@total-typescript/ts-reset";
50
50
 
51
- import type { Storefront, HydrogenCart } from \\"@shopify/hydrogen\\";
51
+ import type {
52
+ Storefront,
53
+ CustomerClient,
54
+ HydrogenCart,
55
+ } from "@shopify/hydrogen";
52
56
  import type {
53
57
  LanguageCode,
54
58
  CountryCode,
55
- } from \\"@shopify/hydrogen/storefront-api-types\\";
56
- import type { CustomerAccessToken } from \\"@shopify/hydrogen/storefront-api-types\\";
57
- import type { HydrogenSession } from \\"./server\\";
59
+ } from "@shopify/hydrogen/storefront-api-types";
60
+ import type { AppSession } from "~/lib/session";
58
61
 
59
62
  declare global {
60
63
  /**
61
64
  * A global \`process\` object is only available during build to access NODE_ENV.
62
65
  */
63
- const process: { env: { NODE_ENV: \\"production\\" | \\"development\\" } };
66
+ const process: { env: { NODE_ENV: "production" | "development" } };
64
67
 
65
68
  /**
66
69
  * Declare expected Env parameter in fetch handler.
@@ -81,7 +84,7 @@ describe("i18n replacers", () => {
81
84
  type I18nLocale = { language: LanguageCode; country: CountryCode };
82
85
  }
83
86
 
84
- declare module \\"@shopify/remix-oxygen\\" {
87
+ declare module "@shopify/remix-oxygen" {
85
88
  /**
86
89
  * Declare local additions to the Remix loader context.
87
90
  */
@@ -89,15 +92,9 @@ describe("i18n replacers", () => {
89
92
  env: Env;
90
93
  cart: HydrogenCart;
91
94
  storefront: Storefront<I18nLocale>;
92
- session: HydrogenSession;
93
- waitUntil: ExecutionContext[\\"waitUntil\\"];
94
- }
95
-
96
- /**
97
- * Declare the data we expect to access via \`context.session\`.
98
- */
99
- export interface SessionData {
100
- customerAccessToken: CustomerAccessToken;
95
+ customerAccount: CustomerClient;
96
+ session: AppSession;
97
+ waitUntil: ExecutionContext["waitUntil"];
101
98
  }
102
99
  }
103
100
  "
@@ -109,7 +106,7 @@ describe("i18n replacers", () => {
109
106
  const skeletonDir = getSkeletonSourceDir();
110
107
  await writeFile(
111
108
  joinPath(tmpDir, serverTs),
112
- // Remove the part that is not needed for this test (HydrogenSession, Cart query, etc);
109
+ // Remove the part that is not needed for this test (AppSession, Cart query, etc);
113
110
  (await readFile(joinPath(skeletonDir, serverTs))).replace(/^};$.*/ms, "};")
114
111
  );
115
112
  await replaceServerI18n(
@@ -124,21 +121,22 @@ describe("i18n replacers", () => {
124
121
  expect(() => checkTypes(newContent)).not.toThrow();
125
122
  expect(newContent).toMatchInlineSnapshot(`
126
123
  "// Virtual entry point for the app
127
- import * as remixBuild from \\"@remix-run/dev/server-build\\";
124
+ import * as remixBuild from "@remix-run/dev/server-build";
128
125
  import {
129
126
  cartGetIdDefault,
130
127
  cartSetIdDefault,
131
128
  createCartHandler,
132
129
  createStorefrontClient,
133
130
  storefrontRedirect,
134
- } from \\"@shopify/hydrogen\\";
131
+ createCustomerAccountClient,
132
+ } from "@shopify/hydrogen";
135
133
  import {
136
134
  createRequestHandler,
137
135
  getStorefrontHeaders,
138
- createCookieSessionStorage,
139
- type SessionStorage,
140
- type Session,
141
- } from \\"@shopify/remix-oxygen\\";
136
+ type AppLoadContext,
137
+ } from "@shopify/remix-oxygen";
138
+ import { AppSession } from "~/lib/session";
139
+ import { CART_QUERY_FRAGMENT } from "~/lib/fragments";
142
140
 
143
141
  /**
144
142
  * Export a fetch handler in module format.
@@ -154,13 +152,13 @@ describe("i18n replacers", () => {
154
152
  * Open a cache instance in the worker and a custom session instance.
155
153
  */
156
154
  if (!env?.SESSION_SECRET) {
157
- throw new Error(\\"SESSION_SECRET environment variable is not set\\");
155
+ throw new Error("SESSION_SECRET environment variable is not set");
158
156
  }
159
157
 
160
158
  const waitUntil = executionContext.waitUntil.bind(executionContext);
161
159
  const [cache, session] = await Promise.all([
162
- caches.open(\\"hydrogen\\"),
163
- HydrogenSession.init(request, [env.SESSION_SECRET]),
160
+ caches.open("hydrogen"),
161
+ AppSession.init(request, [env.SESSION_SECRET]),
164
162
  ]);
165
163
 
166
164
  /**
@@ -177,6 +175,17 @@ describe("i18n replacers", () => {
177
175
  storefrontHeaders: getStorefrontHeaders(request),
178
176
  });
179
177
 
178
+ /**
179
+ * Create a client for Customer Account API.
180
+ */
181
+ const customerAccount = createCustomerAccountClient({
182
+ waitUntil,
183
+ request,
184
+ session,
185
+ customerAccountId: env.PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID,
186
+ customerAccountUrl: env.PUBLIC_CUSTOMER_ACCOUNT_API_URL,
187
+ });
188
+
180
189
  /*
181
190
  * Create a cart handler that will be used to
182
191
  * create and update the cart in the session.
@@ -195,7 +204,14 @@ describe("i18n replacers", () => {
195
204
  const handleRequest = createRequestHandler({
196
205
  build: remixBuild,
197
206
  mode: process.env.NODE_ENV,
198
- getLoadContext: () => ({ session, storefront, cart, env, waitUntil }),
207
+ getLoadContext: (): AppLoadContext => ({
208
+ session,
209
+ storefront,
210
+ customerAccount,
211
+ cart,
212
+ env,
213
+ waitUntil,
214
+ }),
199
215
  });
200
216
 
201
217
  const response = await handleRequest(request);
@@ -213,23 +229,23 @@ describe("i18n replacers", () => {
213
229
  } catch (error) {
214
230
  // eslint-disable-next-line no-console
215
231
  console.error(error);
216
- return new Response(\\"An unexpected error occurred\\", { status: 500 });
232
+ return new Response("An unexpected error occurred", { status: 500 });
217
233
  }
218
234
  },
219
235
  };
220
236
 
221
237
  function getLocaleFromRequest(request: Request): I18nLocale {
222
- const defaultLocale: I18nLocale = { language: \\"EN\\", country: \\"US\\" };
238
+ const defaultLocale: I18nLocale = { language: "EN", country: "US" };
223
239
  const supportedLocales = {
224
- ES: \\"ES\\",
225
- FR: \\"FR\\",
226
- DE: \\"DE\\",
227
- JP: \\"JA\\",
228
- } as Record<I18nLocale[\\"country\\"], I18nLocale[\\"language\\"]>;
240
+ ES: "ES",
241
+ FR: "FR",
242
+ DE: "DE",
243
+ JP: "JA",
244
+ } as Record<I18nLocale["country"], I18nLocale["language"]>;
229
245
 
230
246
  const url = new URL(request.url);
231
247
  const domain = url.hostname
232
- .split(\\".\\")
248
+ .split(".")
233
249
  .pop()
234
250
  ?.toUpperCase() as keyof typeof supportedLocales;
235
251
 
@@ -0,0 +1,89 @@
1
+ import { rmdirSync } from 'node:fs';
2
+ import { temporaryDirectory } from 'tempy';
3
+ import { createSymlink, copy, remove } from 'fs-extra/esm';
4
+ import { copyFile, removeFile } from '@shopify/cli-kit/node/fs';
5
+ import { joinPath, relativePath } from '@shopify/cli-kit/node/path';
6
+ import colors from '@shopify/cli-kit/node/colors';
7
+ import { getRepoNodeModules, getStarterDir } from './build.js';
8
+ import { mergePackageJson } from './file.js';
9
+
10
+ async function prepareDiffDirectory(diffDirectory, watch) {
11
+ const targetDirectory = temporaryDirectory({ prefix: "tmp-hydrogen-diff-" });
12
+ process.on("exit", () => rmdirSync(targetDirectory, { recursive: true }));
13
+ console.info(
14
+ `
15
+ -- Applying diff to starter template in
16
+ ${colors.dim(
17
+ targetDirectory
18
+ )}
19
+ `
20
+ );
21
+ await applyTemplateDiff(targetDirectory, diffDirectory);
22
+ await createSymlink(
23
+ await getRepoNodeModules(),
24
+ joinPath(targetDirectory, "node_modules")
25
+ );
26
+ if (watch) {
27
+ const pw = await import('@parcel/watcher').catch((error) => {
28
+ console.log("Could not watch for file changes.", error);
29
+ });
30
+ pw?.subscribe(
31
+ targetDirectory,
32
+ (error, events) => {
33
+ if (error) {
34
+ console.error(error);
35
+ return;
36
+ }
37
+ events.map((event) => {
38
+ return copyFile(
39
+ event.path,
40
+ joinPath(diffDirectory, relativePath(targetDirectory, event.path))
41
+ );
42
+ });
43
+ },
44
+ { ignore: ["!*.generated.d.ts"] }
45
+ );
46
+ pw?.subscribe(
47
+ diffDirectory,
48
+ async (error, events) => {
49
+ if (error) {
50
+ console.error(error);
51
+ return;
52
+ }
53
+ await events.map((event) => {
54
+ const targetFile = joinPath(
55
+ targetDirectory,
56
+ relativePath(diffDirectory, event.path)
57
+ );
58
+ return event.type === "delete" ? removeFile(targetFile).catch(() => {
59
+ }) : copyFile(event.path, targetFile);
60
+ });
61
+ },
62
+ { ignore: ["*.generated.d.ts", "package.json", "tsconfig.json"] }
63
+ );
64
+ }
65
+ return targetDirectory;
66
+ }
67
+ async function applyTemplateDiff(targetDirectory, diffDirectory, templateDir = getStarterDir()) {
68
+ const createFilter = (re) => (filepath) => !re.test(relativePath(templateDir, filepath));
69
+ await copy(templateDir, targetDirectory, {
70
+ filter: createFilter(/[\/\\](dist|node_modules|\.cache)(\/|\\|$)/i)
71
+ });
72
+ await copy(diffDirectory, targetDirectory, {
73
+ filter: createFilter(
74
+ /[\/\\](dist|node_modules|\.cache|package\.json|tsconfig\.json)(\/|\\|$)/i
75
+ )
76
+ });
77
+ await mergePackageJson(diffDirectory, targetDirectory, {
78
+ ignoredKeys: ["scripts"]
79
+ });
80
+ }
81
+ async function copyDiffBuild(targetDirectory, diffDirectory) {
82
+ const targetDist = joinPath(diffDirectory, "dist");
83
+ await remove(targetDist);
84
+ await copy(joinPath(targetDirectory, "dist"), targetDist, {
85
+ overwrite: true
86
+ });
87
+ }
88
+
89
+ export { applyTemplateDiff, copyDiffBuild, prepareDiffDirectory };
@@ -42,7 +42,7 @@ async function downloadTarball(url, storageDir, signal) {
42
42
  strip: 1,
43
43
  filter: (name) => {
44
44
  name = name.replace(storageDir, "");
45
- return !name.startsWith(path.normalize("/templates/")) || name.startsWith(path.normalize("/templates/skeleton/"));
45
+ return !name.startsWith(path.normalize("/templates/")) && !name.startsWith(path.normalize("/examples/"));
46
46
  }
47
47
  })
48
48
  );
@@ -64,7 +64,8 @@ async function getLatestTemplates({
64
64
  }
65
65
  return {
66
66
  version,
67
- templatesDir: path.join(templateStorageVersionPath, "templates")
67
+ templatesDir: path.join(templateStorageVersionPath, "templates"),
68
+ examplesDir: path.join(templateStorageVersionPath, "examples")
68
69
  };
69
70
  } catch (e) {
70
71
  const error = e;
@@ -102,7 +102,7 @@ async function transpileProject(projectDir, keepTypes = true) {
102
102
  delete pkgJson.devDependencies[key];
103
103
  }
104
104
  }
105
- const codegenFlag = /\s*--codegen(-unstable)?/;
105
+ const codegenFlag = /\s*--codegen/;
106
106
  if (pkgJson.scripts?.dev) {
107
107
  pkgJson.scripts.dev = pkgJson.scripts.dev.replace(codegenFlag, "");
108
108
  }