@shopify/cli-hydrogen 6.0.2 → 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 +173 -37
- package/dist/commands/hydrogen/deploy.test.js +192 -20
- package/dist/commands/hydrogen/dev.js +56 -31
- 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 +22 -11
- package/dist/commands/hydrogen/setup.js +0 -4
- package/dist/commands/hydrogen/setup.test.js +0 -1
- package/dist/commands/hydrogen/shortcut.js +1 -0
- package/dist/commands/hydrogen/upgrade.js +720 -0
- package/dist/commands/hydrogen/upgrade.test.js +786 -0
- package/dist/generator-templates/starter/.graphqlrc.yml +12 -1
- package/dist/generator-templates/starter/CHANGELOG.md +126 -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/Footer.tsx +3 -1
- package/dist/generator-templates/starter/app/components/Header.tsx +5 -1
- package/dist/generator-templates/starter/app/components/Layout.tsx +14 -11
- 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/[robots.txt].tsx +0 -27
- 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 -10
- package/dist/generator-templates/starter/remix.config.js +4 -0
- package/dist/generator-templates/starter/remix.env.d.ts +6 -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/check-lockfile.js +1 -0
- package/dist/lib/codegen.js +59 -18
- package/dist/lib/defer.js +12 -0
- package/dist/lib/file.js +52 -3
- package/dist/lib/flags.js +27 -9
- 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 +32 -14
- package/dist/lib/mini-oxygen/assets.js +118 -0
- package/dist/lib/mini-oxygen/common.js +2 -1
- package/dist/lib/mini-oxygen/index.js +7 -5
- package/dist/lib/mini-oxygen/mini-oxygen.test.js +214 -0
- package/dist/lib/mini-oxygen/node.js +19 -5
- package/dist/lib/mini-oxygen/workerd-inspector-logs.js +227 -0
- package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +200 -0
- package/dist/lib/mini-oxygen/workerd-inspector.js +62 -235
- package/dist/lib/mini-oxygen/workerd.js +74 -50
- 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/render-errors.js +2 -0
- 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 +56 -38
- package/dist/lib/shell.js +1 -1
- 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 +134 -59
- package/package.json +18 -26
- 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,25 +1,28 @@
|
|
|
1
|
-
import { Response, Miniflare,
|
|
2
|
-
import { resolvePath } from '@shopify/cli-kit/node/path';
|
|
3
|
-
import {
|
|
1
|
+
import { Response, Miniflare, Request, fetch, NoOpLog } from 'miniflare';
|
|
2
|
+
import { resolvePath, dirname } from '@shopify/cli-kit/node/path';
|
|
3
|
+
import { readFile } from '@shopify/cli-kit/node/fs';
|
|
4
4
|
import { renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
5
|
+
import { outputContent, outputToken } from '@shopify/cli-kit/node/output';
|
|
6
|
+
import colors from '@shopify/cli-kit/node/colors';
|
|
7
|
+
import { createInspectorConnector } from './workerd-inspector.js';
|
|
8
8
|
import { findPort } from '../find-port.js';
|
|
9
9
|
import { OXYGEN_HEADERS_MAP, logRequestLine } from './common.js';
|
|
10
|
-
import { setConstructors, handleDebugNetworkRequest, H2O_BINDING_NAME,
|
|
10
|
+
import { setConstructors, handleDebugNetworkRequest, H2O_BINDING_NAME, createLogRequestEvent } from '../request-events.js';
|
|
11
|
+
import { STATIC_ASSET_EXTENSIONS, createAssetsServer, buildAssetsUrl } from './assets.js';
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
+
const PRIVATE_WORKERD_INSPECTOR_PORT = 9222;
|
|
13
14
|
async function startWorkerdServer({
|
|
14
15
|
root,
|
|
15
|
-
port
|
|
16
|
+
port: appPort,
|
|
17
|
+
inspectorPort: publicInspectorPort,
|
|
18
|
+
assetsPort,
|
|
19
|
+
debug = false,
|
|
16
20
|
watch = false,
|
|
17
21
|
buildPathWorkerFile,
|
|
18
22
|
buildPathClient,
|
|
19
23
|
env
|
|
20
24
|
}) {
|
|
21
|
-
const
|
|
22
|
-
const inspectorPort = await findPort(DEFAULT_INSPECTOR_PORT);
|
|
25
|
+
const privateInspectorPort = await findPort(PRIVATE_WORKERD_INSPECTOR_PORT);
|
|
23
26
|
const oxygenHeadersMap = Object.values(OXYGEN_HEADERS_MAP).reduce(
|
|
24
27
|
(acc, item) => {
|
|
25
28
|
acc[item.name] = item.defaultValue;
|
|
@@ -28,44 +31,57 @@ async function startWorkerdServer({
|
|
|
28
31
|
{}
|
|
29
32
|
);
|
|
30
33
|
setConstructors({ Response });
|
|
34
|
+
const absoluteBundlePath = resolvePath(root, buildPathWorkerFile);
|
|
35
|
+
const handleAssets = createAssetHandler(assetsPort);
|
|
36
|
+
const staticAssetExtensions = STATIC_ASSET_EXTENSIONS.slice();
|
|
37
|
+
let stringifiedOxygenHandler = miniOxygenHandler.toString();
|
|
38
|
+
if (process.env.NODE_ENV === "test") {
|
|
39
|
+
stringifiedOxygenHandler = stringifiedOxygenHandler.replace(
|
|
40
|
+
/\w*vite_ssr_import[\w\d]*\./g,
|
|
41
|
+
""
|
|
42
|
+
);
|
|
43
|
+
}
|
|
31
44
|
const buildMiniOxygenOptions = async () => ({
|
|
32
45
|
cf: false,
|
|
33
46
|
verbose: false,
|
|
34
47
|
port: appPort,
|
|
35
|
-
inspectorPort,
|
|
48
|
+
inspectorPort: privateInspectorPort,
|
|
36
49
|
log: new NoOpLog(),
|
|
37
50
|
liveReload: watch,
|
|
38
51
|
host: "localhost",
|
|
52
|
+
handleRuntimeStdio() {
|
|
53
|
+
},
|
|
39
54
|
workers: [
|
|
40
55
|
{
|
|
41
56
|
name: "mini-oxygen",
|
|
42
57
|
modules: true,
|
|
43
|
-
script: `export default { fetch: ${
|
|
58
|
+
script: `export default { fetch: ${stringifiedOxygenHandler} }`,
|
|
44
59
|
bindings: {
|
|
45
|
-
|
|
60
|
+
staticAssetExtensions,
|
|
46
61
|
oxygenHeadersMap
|
|
47
62
|
},
|
|
48
63
|
serviceBindings: {
|
|
49
64
|
hydrogen: "hydrogen",
|
|
50
|
-
assets:
|
|
65
|
+
assets: handleAssets,
|
|
51
66
|
debugNetwork: handleDebugNetworkRequest,
|
|
52
67
|
logRequest
|
|
53
68
|
}
|
|
54
69
|
},
|
|
55
70
|
{
|
|
56
71
|
name: "hydrogen",
|
|
72
|
+
modulesRoot: dirname(absoluteBundlePath),
|
|
57
73
|
modules: [
|
|
58
74
|
{
|
|
59
75
|
type: "ESModule",
|
|
60
|
-
path:
|
|
61
|
-
contents: await readFile(
|
|
76
|
+
path: absoluteBundlePath,
|
|
77
|
+
contents: await readFile(absoluteBundlePath)
|
|
62
78
|
}
|
|
63
79
|
],
|
|
64
80
|
compatibilityFlags: ["streams_enable_constructors"],
|
|
65
81
|
compatibilityDate: "2022-10-31",
|
|
66
82
|
bindings: { ...env },
|
|
67
83
|
serviceBindings: {
|
|
68
|
-
[H2O_BINDING_NAME]:
|
|
84
|
+
[H2O_BINDING_NAME]: createLogRequestEvent({ absoluteBundlePath })
|
|
69
85
|
}
|
|
70
86
|
}
|
|
71
87
|
]
|
|
@@ -74,8 +90,16 @@ async function startWorkerdServer({
|
|
|
74
90
|
const miniOxygen = new Miniflare(miniOxygenOptions);
|
|
75
91
|
const listeningAt = (await miniOxygen.ready).origin;
|
|
76
92
|
const sourceMapPath = buildPathWorkerFile + ".map";
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
const reconnect = createInspectorConnector({
|
|
94
|
+
debug,
|
|
95
|
+
sourceMapPath,
|
|
96
|
+
absoluteBundlePath,
|
|
97
|
+
privateInspectorPort,
|
|
98
|
+
publicInspectorPort
|
|
99
|
+
});
|
|
100
|
+
await reconnect();
|
|
101
|
+
const assetsServer = createAssetsServer(buildPathClient);
|
|
102
|
+
assetsServer.listen(assetsPort);
|
|
79
103
|
return {
|
|
80
104
|
port: appPort,
|
|
81
105
|
listeningAt,
|
|
@@ -89,25 +113,31 @@ async function startWorkerdServer({
|
|
|
89
113
|
hydrogen.bindings = { ...nextOptions?.env ?? env };
|
|
90
114
|
}
|
|
91
115
|
}
|
|
92
|
-
|
|
93
|
-
await miniOxygen.setOptions(miniOxygenOptions);
|
|
94
|
-
inspectorUrl ??= await findInspectorUrl(inspectorPort);
|
|
95
|
-
if (inspectorUrl) {
|
|
96
|
-
cleanupInspector = connectToInspector({ inspectorUrl, sourceMapPath });
|
|
97
|
-
}
|
|
116
|
+
await reconnect(() => miniOxygen.setOptions(miniOxygenOptions));
|
|
98
117
|
},
|
|
99
118
|
showBanner(options) {
|
|
100
119
|
console.log("");
|
|
120
|
+
const isVSCode = process.env.TERM_PROGRAM === "vscode";
|
|
121
|
+
const debuggingDocsLink = "https://h2o.fyi/debugging/server-code" + (isVSCode ? "#visual-studio-code" : "#step-2-attach-a-debugger");
|
|
122
|
+
const debuggerMessage = outputContent`\n\nDebugging enabled on port ${String(
|
|
123
|
+
publicInspectorPort
|
|
124
|
+
)}.\nAttach a ${outputToken.link(
|
|
125
|
+
colors.yellow(isVSCode ? "VSCode debugger" : "debugger"),
|
|
126
|
+
debuggingDocsLink
|
|
127
|
+
)} or open DevTools in http://localhost:${String(publicInspectorPort)}.`.value;
|
|
101
128
|
renderSuccess({
|
|
102
|
-
headline: `${options?.headlinePrefix ?? ""}MiniOxygen (
|
|
129
|
+
headline: `${options?.headlinePrefix ?? ""}MiniOxygen (Worker Runtime) ${options?.mode ?? "development"} server running.`,
|
|
103
130
|
body: [
|
|
104
131
|
`View ${options?.appName ?? "Hydrogen"} app: ${listeningAt}`,
|
|
105
|
-
...options?.extraLines ?? []
|
|
132
|
+
...options?.extraLines ?? [],
|
|
133
|
+
...debug ? [{ warn: debuggerMessage }] : []
|
|
106
134
|
]
|
|
107
135
|
});
|
|
108
136
|
console.log("");
|
|
109
137
|
},
|
|
110
138
|
async close() {
|
|
139
|
+
assetsServer.closeAllConnections();
|
|
140
|
+
assetsServer.close();
|
|
111
141
|
await miniOxygen.dispose();
|
|
112
142
|
}
|
|
113
143
|
};
|
|
@@ -118,7 +148,11 @@ async function miniOxygenHandler(request, env, context) {
|
|
|
118
148
|
return env.debugNetwork.fetch(request);
|
|
119
149
|
}
|
|
120
150
|
if (request.method === "GET") {
|
|
121
|
-
|
|
151
|
+
const staticAssetExtensions = new Set(env.staticAssetExtensions);
|
|
152
|
+
const wellKnown = pathname.startsWith("/.well-known");
|
|
153
|
+
const extension = pathname.split(".").at(-1) ?? "";
|
|
154
|
+
const isAsset = wellKnown || !!staticAssetExtensions.has(extension.toUpperCase());
|
|
155
|
+
if (isAsset) {
|
|
122
156
|
const response2 = await env.assets.fetch(
|
|
123
157
|
new Request(request.url, {
|
|
124
158
|
signal: request.signal,
|
|
@@ -154,28 +188,18 @@ async function miniOxygenHandler(request, env, context) {
|
|
|
154
188
|
);
|
|
155
189
|
return response;
|
|
156
190
|
}
|
|
157
|
-
function createAssetHandler(
|
|
191
|
+
function createAssetHandler(assetsPort) {
|
|
192
|
+
const assetsServerOrigin = buildAssetsUrl(assetsPort);
|
|
158
193
|
return async (request) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
"Content-Type": lookupMimeType(relativeAssetPath) || "text/plain",
|
|
169
|
-
"Content-Length": String(await fileSize(absoluteAssetPath))
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
} catch (error) {
|
|
173
|
-
if (error.code !== "ENOENT") {
|
|
174
|
-
throw error;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return new Response("Not Found", { status: 404 });
|
|
194
|
+
return fetch(
|
|
195
|
+
new Request(
|
|
196
|
+
request.url.replace(
|
|
197
|
+
new URL(request.url).origin + "/",
|
|
198
|
+
assetsServerOrigin
|
|
199
|
+
),
|
|
200
|
+
request
|
|
201
|
+
)
|
|
202
|
+
);
|
|
179
203
|
};
|
|
180
204
|
}
|
|
181
205
|
async function logRequest(request) {
|
|
@@ -23,12 +23,15 @@ const REQUIRED_ROUTES = [
|
|
|
23
23
|
// 'discount/:discountCode', => Handled in storefrontRedirect
|
|
24
24
|
"account",
|
|
25
25
|
"account/login",
|
|
26
|
-
"account/register",
|
|
27
26
|
// 'account/addresses',
|
|
28
27
|
// 'account/orders',
|
|
29
28
|
"account/orders/:orderId",
|
|
30
|
-
|
|
31
|
-
"account/
|
|
29
|
+
// -- Added for CAAPI:
|
|
30
|
+
"account/authorize"
|
|
31
|
+
// -- These were removed when migrating to CAAPI:
|
|
32
|
+
// 'account/register',
|
|
33
|
+
// 'account/reset/:id/:token',
|
|
34
|
+
// 'account/activate/:id/:token',
|
|
32
35
|
// 'password',
|
|
33
36
|
// 'opening_soon',
|
|
34
37
|
];
|
|
@@ -16,6 +16,7 @@ import { transpileProject } from '../transpile/index.js';
|
|
|
16
16
|
import { renderCssPrompt, setupCssStrategy, CSS_STRATEGY_NAME_MAP } from '../setups/css/index.js';
|
|
17
17
|
import { renderRoutePrompt, generateRoutes, generateProjectFile } from '../setups/routes/generate.js';
|
|
18
18
|
import { execAsync } from '../process.js';
|
|
19
|
+
import { getStorefronts } from '../graphql/admin/link-storefront.js';
|
|
19
20
|
|
|
20
21
|
const LANGUAGES = {
|
|
21
22
|
js: "JavaScript",
|
|
@@ -68,7 +69,7 @@ async function handleRouteGeneration(controller, flagRoutes) {
|
|
|
68
69
|
}
|
|
69
70
|
function generateProjectEntries(options) {
|
|
70
71
|
return Promise.all(
|
|
71
|
-
["root", "entry.server", "entry.client"].map(
|
|
72
|
+
["root", "entry.server", "entry.client", "../server.ts"].map(
|
|
72
73
|
(filename) => generateProjectFile(filename, options)
|
|
73
74
|
)
|
|
74
75
|
);
|
|
@@ -108,12 +109,41 @@ async function handleCliShortcut(controller, cliCommand, flagShortcut) {
|
|
|
108
109
|
async function handleStorefrontLink(controller) {
|
|
109
110
|
const { session, config } = await login();
|
|
110
111
|
renderLoginSuccess(config);
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
const storefronts = await getStorefronts(session);
|
|
113
|
+
let selectedStorefront = await handleStorefrontSelection(storefronts);
|
|
114
|
+
let title;
|
|
115
|
+
if (selectedStorefront) {
|
|
116
|
+
title = selectedStorefront.title;
|
|
117
|
+
} else {
|
|
118
|
+
title = await renderTextPrompt({
|
|
119
|
+
message: "New storefront name",
|
|
120
|
+
defaultValue: titleize(config.shopName),
|
|
121
|
+
abortSignal: controller.signal
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
...config,
|
|
126
|
+
id: selectedStorefront?.id,
|
|
127
|
+
title,
|
|
128
|
+
session
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async function handleStorefrontSelection(storefronts) {
|
|
132
|
+
const choices = [
|
|
133
|
+
{
|
|
134
|
+
label: "Create a new storefront",
|
|
135
|
+
value: null
|
|
136
|
+
},
|
|
137
|
+
...storefronts.map(({ id, title, productionUrl }) => ({
|
|
138
|
+
label: `${title} (${productionUrl})`,
|
|
139
|
+
value: id
|
|
140
|
+
}))
|
|
141
|
+
];
|
|
142
|
+
const storefrontId = await renderSelectPrompt({
|
|
143
|
+
message: "Select a Hydrogen storefront to link",
|
|
144
|
+
choices
|
|
115
145
|
});
|
|
116
|
-
return {
|
|
146
|
+
return storefrontId ? storefronts.find(({ id }) => id === storefrontId) : void 0;
|
|
117
147
|
}
|
|
118
148
|
async function handleProjectLocation({
|
|
119
149
|
storefrontInfo,
|
|
@@ -380,7 +410,7 @@ async function renderProjectReady(project, {
|
|
|
380
410
|
{
|
|
381
411
|
link: {
|
|
382
412
|
label: "Guides",
|
|
383
|
-
url: "https://
|
|
413
|
+
url: "https://h2o.fyi/building"
|
|
384
414
|
}
|
|
385
415
|
},
|
|
386
416
|
{
|
|
@@ -434,7 +464,8 @@ async function renderProjectReady(project, {
|
|
|
434
464
|
function createAbortHandler(controller, project) {
|
|
435
465
|
return async function abort(error) {
|
|
436
466
|
controller.abort();
|
|
437
|
-
|
|
467
|
+
await Promise.resolve();
|
|
468
|
+
if (project?.directory) {
|
|
438
469
|
await rmdir(project.directory, { force: true }).catch(() => {
|
|
439
470
|
});
|
|
440
471
|
}
|
|
@@ -457,4 +488,4 @@ function normalizeRoutePath(routePath) {
|
|
|
457
488
|
return routePath.replace(/(^|\.)_index$/, "").replace(/((^|\.)[^\.]+)_\./g, "$1.").replace(/\.(?!\w+\])/g, "/").replace(/\$$/g, ":catchAll").replace(/\$/g, ":").replace(/[\[\]]/g, "").replace(/:\w*Handle/i, ":handle");
|
|
458
489
|
}
|
|
459
490
|
|
|
460
|
-
export { LANGUAGES, commitAll, createAbortHandler, createInitialCommit, generateProjectEntries, handleCliShortcut, handleCssStrategy, handleDependencies, handleI18n, handleLanguage, handleProjectLocation, handleRouteGeneration, handleStorefrontLink, renderProjectReady };
|
|
491
|
+
export { LANGUAGES, commitAll, createAbortHandler, createInitialCommit, generateProjectEntries, handleCliShortcut, handleCssStrategy, handleDependencies, handleI18n, handleLanguage, handleProjectLocation, handleRouteGeneration, handleStorefrontLink, handleStorefrontSelection, renderProjectReady };
|
|
@@ -37,7 +37,7 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
37
37
|
if (templateAction === "mock")
|
|
38
38
|
project.storefrontTitle = "Mock.shop";
|
|
39
39
|
const abort = createAbortHandler(controller, project);
|
|
40
|
-
const createStorefrontPromise = storefrontInfo && createStorefront(storefrontInfo.session, storefrontInfo.title).then(async ({ storefront, jobId }) => {
|
|
40
|
+
const createStorefrontPromise = storefrontInfo && !storefrontInfo.id && createStorefront(storefrontInfo.session, storefrontInfo.title).then(async ({ storefront, jobId }) => {
|
|
41
41
|
if (jobId)
|
|
42
42
|
await waitForJob(storefrontInfo.session, jobId);
|
|
43
43
|
return storefront;
|
|
@@ -46,9 +46,9 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
46
46
|
let backgroundWorkPromise = copy(
|
|
47
47
|
templateDir,
|
|
48
48
|
project.directory,
|
|
49
|
-
// Filter out the `app` directory, which will be generated later
|
|
49
|
+
// Filter out the `app` directory and server.ts, which will be generated later
|
|
50
50
|
{
|
|
51
|
-
filter: (filepath) => !/^(app
|
|
51
|
+
filter: (filepath) => !/^(app\/|dist\/|node_modules\/|server\.ts)/i.test(
|
|
52
52
|
relativePath(templateDir, filepath)
|
|
53
53
|
)
|
|
54
54
|
}
|
|
@@ -90,19 +90,23 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
90
90
|
)
|
|
91
91
|
];
|
|
92
92
|
const envLeadingComment = "# The variables added in this file are only available locally in MiniOxygen.\n# Run `h2 link` to also inject environment variables from your storefront,\n# or `h2 env pull` to populate this file.";
|
|
93
|
-
|
|
93
|
+
let storefrontToLink;
|
|
94
|
+
if (storefrontInfo) {
|
|
94
95
|
promises.push(
|
|
95
96
|
// Save linked storefront in project
|
|
96
97
|
setUserAccount(project.directory, storefrontInfo),
|
|
97
|
-
createStorefrontPromise.then(
|
|
98
|
-
(storefront) => (
|
|
99
|
-
// Save linked storefront in project
|
|
100
|
-
setStorefront(project.directory, storefront)
|
|
101
|
-
)
|
|
102
|
-
),
|
|
103
98
|
// Write empty dotenv file to fallback to remote Oxygen variables
|
|
104
99
|
writeFile(joinPath(project.directory, ".env"), envLeadingComment)
|
|
105
100
|
);
|
|
101
|
+
if (storefrontInfo.id) {
|
|
102
|
+
storefrontToLink = { id: storefrontInfo.id, title: storefrontInfo.title };
|
|
103
|
+
} else if (createStorefrontPromise) {
|
|
104
|
+
promises.push(
|
|
105
|
+
createStorefrontPromise.then((createdStorefront) => {
|
|
106
|
+
storefrontToLink = createdStorefront;
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
}
|
|
106
110
|
} else if (templateAction === "mock") {
|
|
107
111
|
promises.push(
|
|
108
112
|
// Set required env vars
|
|
@@ -115,7 +119,11 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
115
119
|
)
|
|
116
120
|
);
|
|
117
121
|
}
|
|
118
|
-
return Promise.all(promises).
|
|
122
|
+
return Promise.all(promises).then(() => {
|
|
123
|
+
if (storefrontToLink) {
|
|
124
|
+
return setStorefront(project.directory, storefrontToLink);
|
|
125
|
+
}
|
|
126
|
+
}).catch(abort);
|
|
119
127
|
});
|
|
120
128
|
const { language, transpileProject } = await handleLanguage(
|
|
121
129
|
project.directory,
|
|
@@ -1,38 +1,58 @@
|
|
|
1
1
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
2
|
-
import { copyFile } from '@shopify/cli-kit/node/fs';
|
|
2
|
+
import { fileExists, copyFile } from '@shopify/cli-kit/node/fs';
|
|
3
|
+
import { readAndParsePackageJson } from '@shopify/cli-kit/node/node-package-manager';
|
|
3
4
|
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
4
5
|
import { renderTasks, renderInfo } from '@shopify/cli-kit/node/ui';
|
|
5
6
|
import { getLatestTemplates } from '../template-downloader.js';
|
|
6
|
-
import {
|
|
7
|
+
import { applyTemplateDiff } from '../template-diff.js';
|
|
8
|
+
import { createAbortHandler, handleProjectLocation, handleLanguage, createInitialCommit, handleDependencies, commitAll, renderProjectReady } from './common.js';
|
|
7
9
|
|
|
8
10
|
async function setupRemoteTemplate(options, controller) {
|
|
9
|
-
const isOfficialTemplate = options.template === "demo-store" || options.template === "hello-world";
|
|
10
|
-
if (!isOfficialTemplate) {
|
|
11
|
-
throw new AbortError(
|
|
12
|
-
"Only `demo-store` and `hello-world` are supported in --template flag for now.",
|
|
13
|
-
"Skip the --template flag to run the setup flow."
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
11
|
const appTemplate = options.template;
|
|
12
|
+
let abort = createAbortHandler(controller);
|
|
17
13
|
const backgroundDownloadPromise = getLatestTemplates({
|
|
18
14
|
signal: controller.signal
|
|
19
|
-
}).
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
}).then(async ({ templatesDir, examplesDir }) => {
|
|
16
|
+
const templatePath = joinPath(templatesDir, appTemplate);
|
|
17
|
+
const examplePath = joinPath(examplesDir, appTemplate);
|
|
18
|
+
if (await fileExists(templatePath)) {
|
|
19
|
+
return { templatesDir, sourcePath: templatePath };
|
|
20
|
+
}
|
|
21
|
+
if (await fileExists(examplePath)) {
|
|
22
|
+
return { templatesDir, sourcePath: examplePath };
|
|
23
|
+
}
|
|
24
|
+
throw new AbortError(
|
|
25
|
+
"Unknown value in --template flag.",
|
|
26
|
+
"Skip the --template flag or provide the name of a template or example in the Hydrogen repository."
|
|
27
|
+
);
|
|
28
|
+
}).catch(abort);
|
|
22
29
|
const project = await handleProjectLocation({ ...options, controller });
|
|
23
30
|
if (!project)
|
|
24
31
|
return;
|
|
25
|
-
|
|
26
|
-
let backgroundWorkPromise = backgroundDownloadPromise.then(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
abort = createAbortHandler(controller, project);
|
|
33
|
+
let backgroundWorkPromise = backgroundDownloadPromise.then(async (result) => {
|
|
34
|
+
if (controller.signal.aborted)
|
|
35
|
+
return;
|
|
36
|
+
const { sourcePath: sourcePath2, templatesDir } = result;
|
|
37
|
+
const pkgJson = await readAndParsePackageJson(
|
|
38
|
+
joinPath(sourcePath2, "package.json")
|
|
39
|
+
);
|
|
40
|
+
if (pkgJson.scripts?.dev?.includes("--diff")) {
|
|
41
|
+
return applyTemplateDiff(
|
|
42
|
+
project.directory,
|
|
43
|
+
sourcePath2,
|
|
44
|
+
joinPath(templatesDir, "skeleton")
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return copyFile(sourcePath2, project.directory);
|
|
48
|
+
}).catch(abort);
|
|
49
|
+
if (controller.signal.aborted)
|
|
50
|
+
return;
|
|
51
|
+
const { sourcePath } = await backgroundDownloadPromise;
|
|
52
|
+
const supportsTranspilation = await fileExists(
|
|
53
|
+
joinPath(sourcePath, "tsconfig.json")
|
|
35
54
|
);
|
|
55
|
+
const { language, transpileProject } = supportsTranspilation ? await handleLanguage(project.directory, controller, options.language) : { language: "js", transpileProject: () => Promise.resolve() };
|
|
36
56
|
backgroundWorkPromise = backgroundWorkPromise.then(() => transpileProject().catch(abort)).then(
|
|
37
57
|
() => options.git ? createInitialCommit(project.directory) : void 0
|
|
38
58
|
);
|
|
@@ -74,17 +94,17 @@ async function setupRemoteTemplate(options, controller) {
|
|
|
74
94
|
}
|
|
75
95
|
});
|
|
76
96
|
}
|
|
97
|
+
if (controller.signal.aborted)
|
|
98
|
+
return;
|
|
77
99
|
await renderTasks(tasks);
|
|
78
100
|
if (options.git) {
|
|
79
101
|
await commitAll(project.directory, "Lockfile");
|
|
80
102
|
}
|
|
81
103
|
await renderProjectReady(project, setupSummary);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
87
|
-
}
|
|
104
|
+
renderInfo({
|
|
105
|
+
headline: `Your project will display inventory from ${options.template === "demo-store" ? "the Hydrogen Demo Store" : "Mock.shop"}.`,
|
|
106
|
+
body: `To connect this project to your Shopify store\u2019s inventory, update \`${project.name}/.env\` with your store ID and Storefront API key.`
|
|
107
|
+
});
|
|
88
108
|
return {
|
|
89
109
|
...project,
|
|
90
110
|
...setupSummary
|
|
@@ -14,6 +14,7 @@ function renderMissingStorefront({
|
|
|
14
14
|
message: outputContent`${outputToken.errorText(
|
|
15
15
|
"Couldn\u2019t find Hydrogen storefront."
|
|
16
16
|
)}`.value,
|
|
17
|
+
skipOclifErrorHandling: true,
|
|
17
18
|
tryMessage: outputContent`Couldn’t find ${storefront.title} (ID: ${parseGid(
|
|
18
19
|
storefront.id
|
|
19
20
|
)}) on ${session.storeFqdn}. Check that the storefront exists and run ${outputToken.genericShellCommand(
|
|
@@ -29,6 +30,7 @@ function renderMissingLink({ session, cliCommand }) {
|
|
|
29
30
|
name: "NoLinkedStorefrontError",
|
|
30
31
|
type: 0,
|
|
31
32
|
message: `No linked Hydrogen storefront on ${session.storeFqdn}`,
|
|
33
|
+
skipOclifErrorHandling: true,
|
|
32
34
|
tryMessage: [
|
|
33
35
|
"To pull environment variables or to deploy to Oxygen, link this project to a Hydrogen storefront. To select a storefront to link, run",
|
|
34
36
|
{ command: `${cliCommand} link` }
|
|
@@ -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 };
|