@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.
- package/dist/commands/hydrogen/build.js +40 -78
- package/dist/commands/hydrogen/codegen.js +8 -3
- package/dist/commands/hydrogen/deploy.js +107 -35
- package/dist/commands/hydrogen/deploy.test.js +83 -13
- package/dist/commands/hydrogen/dev.js +30 -15
- package/dist/commands/hydrogen/init.js +1 -1
- package/dist/commands/hydrogen/init.test.js +155 -53
- package/dist/commands/hydrogen/link.js +5 -21
- package/dist/commands/hydrogen/link.test.js +10 -10
- package/dist/commands/hydrogen/preview.js +7 -6
- package/dist/commands/hydrogen/setup.js +0 -4
- package/dist/commands/hydrogen/setup.test.js +0 -1
- package/dist/commands/hydrogen/upgrade.js +15 -0
- package/dist/generator-templates/starter/.graphqlrc.yml +12 -1
- package/dist/generator-templates/starter/CHANGELOG.md +56 -0
- package/dist/generator-templates/starter/README.md +23 -0
- package/dist/generator-templates/starter/app/components/Cart.tsx +1 -1
- package/dist/generator-templates/starter/app/components/Header.tsx +5 -1
- package/dist/generator-templates/starter/app/components/Layout.tsx +1 -1
- package/dist/generator-templates/starter/app/components/Search.tsx +1 -1
- package/dist/generator-templates/starter/app/graphql/customer-account/CustomerAddressMutations.ts +61 -0
- package/dist/generator-templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +39 -0
- package/dist/generator-templates/starter/app/graphql/customer-account/CustomerOrderQuery.ts +87 -0
- package/dist/generator-templates/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +58 -0
- package/dist/generator-templates/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +24 -0
- package/dist/generator-templates/starter/app/lib/fragments.ts +102 -0
- package/dist/generator-templates/starter/app/lib/session.ts +67 -0
- package/dist/generator-templates/starter/app/root.tsx +11 -45
- package/dist/generator-templates/starter/app/routes/account.$.tsx +8 -4
- package/dist/generator-templates/starter/app/routes/account._index.tsx +5 -0
- package/dist/generator-templates/starter/app/routes/account.addresses.tsx +215 -206
- package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +56 -163
- package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +32 -109
- package/dist/generator-templates/starter/app/routes/account.profile.tsx +40 -180
- package/dist/generator-templates/starter/app/routes/account.tsx +20 -135
- package/dist/generator-templates/starter/app/routes/account_.authorize.tsx +5 -0
- package/dist/generator-templates/starter/app/routes/account_.login.tsx +3 -140
- package/dist/generator-templates/starter/app/routes/account_.logout.tsx +5 -24
- package/dist/generator-templates/starter/app/routes/cart.tsx +7 -5
- package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +1 -1
- package/dist/generator-templates/starter/app/routes/products.$handle.tsx +2 -2
- package/dist/generator-templates/starter/app/routes/search.tsx +1 -1
- package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +506 -0
- package/dist/generator-templates/starter/package.json +11 -11
- package/dist/generator-templates/starter/remix.config.js +4 -0
- package/dist/generator-templates/starter/remix.env.d.ts +4 -11
- package/dist/generator-templates/starter/server.ts +24 -167
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +104 -881
- package/dist/hooks/init.js +4 -4
- package/dist/lib/auth.js +5 -10
- package/dist/lib/build.js +6 -1
- package/dist/lib/bundle/analyzer.js +36 -26
- package/dist/lib/codegen.js +58 -18
- package/dist/lib/defer.js +12 -0
- package/dist/lib/file.js +52 -3
- package/dist/lib/flags.js +15 -8
- package/dist/lib/get-oxygen-deployment-data.test.js +4 -2
- package/dist/lib/graphql/admin/client.test.js +2 -2
- package/dist/lib/graphql/admin/get-oxygen-data.js +1 -0
- package/dist/lib/log.js +31 -14
- package/dist/lib/mini-oxygen/index.js +4 -5
- package/dist/lib/mini-oxygen/mini-oxygen.test.js +214 -0
- package/dist/lib/mini-oxygen/node.js +4 -2
- package/dist/lib/mini-oxygen/workerd-inspector-logs.js +2 -2
- package/dist/lib/mini-oxygen/workerd.js +27 -10
- package/dist/lib/missing-routes.js +6 -3
- package/dist/lib/onboarding/common.js +40 -9
- package/dist/lib/onboarding/local.js +19 -11
- package/dist/lib/onboarding/remote.js +48 -28
- package/dist/lib/request-events.js +65 -31
- package/dist/lib/setups/css/assets.js +1 -46
- package/dist/lib/setups/css/css-modules.js +3 -2
- package/dist/lib/setups/css/postcss.js +4 -2
- package/dist/lib/setups/css/tailwind.js +4 -2
- package/dist/lib/setups/css/vanilla-extract.js +3 -2
- package/dist/lib/setups/i18n/replacers.test.js +54 -38
- package/dist/lib/template-diff.js +89 -0
- package/dist/lib/template-downloader.js +3 -2
- package/dist/lib/transpile/project.js +1 -1
- package/dist/virtual-routes/assets/debug-network.css +592 -0
- package/dist/virtual-routes/assets/favicon-dark.svg +20 -0
- package/dist/virtual-routes/components/FlameChartWrapper.jsx +8 -10
- package/dist/virtual-routes/components/IconClose.jsx +38 -0
- package/dist/virtual-routes/components/IconDiscard.jsx +44 -0
- package/dist/virtual-routes/components/RequestDetails.jsx +179 -0
- package/dist/virtual-routes/components/RequestTable.jsx +92 -0
- package/dist/virtual-routes/components/RequestWaterfall.jsx +151 -0
- package/dist/virtual-routes/lib/useDebugNetworkServer.jsx +176 -0
- package/dist/virtual-routes/routes/subrequest-profiler.jsx +243 -0
- package/oclif.manifest.json +54 -61
- package/package.json +14 -11
- package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +0 -161
- package/dist/generator-templates/starter/app/routes/account_.recover.tsx +0 -129
- package/dist/generator-templates/starter/app/routes/account_.register.tsx +0 -207
- package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +0 -136
- package/dist/virtual-routes/routes/debug-network.jsx +0 -289
- /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 {
|
|
5
|
+
import { mapSourcePosition } from 'source-map-support';
|
|
5
6
|
|
|
6
7
|
const H2O_BINDING_NAME = "H2O_LOG_EVENT";
|
|
7
|
-
let ResponseConstructor
|
|
8
|
+
let ResponseConstructor;
|
|
8
9
|
function setConstructors(constructors) {
|
|
9
10
|
ResponseConstructor = constructors.Response;
|
|
10
11
|
}
|
|
11
|
-
const DEV_ROUTES = /* @__PURE__ */ new Set(["/graphiql", "/
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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,
|
|
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
|
|
44
|
+
export { SETUP_CSS_STRATEGIES, canWriteFiles, copyAssets };
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { mergePackageJson } from '
|
|
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 {
|
|
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 {
|
|
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 '
|
|
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
|
|
45
|
-
/// <reference types
|
|
46
|
-
/// <reference 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
|
|
49
|
+
import "@total-typescript/ts-reset";
|
|
50
50
|
|
|
51
|
-
import type {
|
|
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
|
|
56
|
-
import type {
|
|
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:
|
|
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
|
|
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
|
-
|
|
93
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
131
|
+
createCustomerAccountClient,
|
|
132
|
+
} from "@shopify/hydrogen";
|
|
135
133
|
import {
|
|
136
134
|
createRequestHandler,
|
|
137
135
|
getStorefrontHeaders,
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
} from
|
|
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(
|
|
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(
|
|
163
|
-
|
|
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: () => ({
|
|
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(
|
|
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:
|
|
238
|
+
const defaultLocale: I18nLocale = { language: "EN", country: "US" };
|
|
223
239
|
const supportedLocales = {
|
|
224
|
-
ES:
|
|
225
|
-
FR:
|
|
226
|
-
DE:
|
|
227
|
-
JP:
|
|
228
|
-
} as Record<I18nLocale[
|
|
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/"))
|
|
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
|
|
105
|
+
const codegenFlag = /\s*--codegen/;
|
|
106
106
|
if (pkgJson.scripts?.dev) {
|
|
107
107
|
pkgJson.scripts.dev = pkgJson.scripts.dev.replace(codegenFlag, "");
|
|
108
108
|
}
|