@shopify/cli-hydrogen 7.1.2 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/hydrogen/build-vite.js +19 -10
- package/dist/commands/hydrogen/build.js +10 -2
- package/dist/commands/hydrogen/check.js +1 -0
- package/dist/commands/hydrogen/codegen.js +1 -0
- package/dist/commands/hydrogen/customer-account/push.js +170 -0
- package/dist/commands/hydrogen/debug/cpu.js +3 -0
- package/dist/commands/hydrogen/deploy.js +121 -36
- package/dist/commands/hydrogen/dev-vite.js +128 -59
- package/dist/commands/hydrogen/dev.js +108 -51
- package/dist/commands/hydrogen/env/list.js +7 -8
- package/dist/commands/hydrogen/env/pull.js +17 -1
- package/dist/commands/hydrogen/env/{push__unstable.js → push.js} +23 -50
- package/dist/commands/hydrogen/generate/route.js +1 -0
- package/dist/commands/hydrogen/init.js +45 -17
- package/dist/commands/hydrogen/link.js +20 -4
- package/dist/commands/hydrogen/list.js +1 -0
- package/dist/commands/hydrogen/login.js +1 -0
- package/dist/commands/hydrogen/logout.js +1 -0
- package/dist/commands/hydrogen/preview.js +31 -16
- package/dist/commands/hydrogen/setup/css.js +8 -1
- package/dist/commands/hydrogen/setup/markets.js +1 -0
- package/dist/commands/hydrogen/setup/vite.js +244 -138
- package/dist/commands/hydrogen/setup.js +21 -22
- package/dist/commands/hydrogen/shortcut.js +10 -0
- package/dist/commands/hydrogen/unlink.js +1 -0
- package/dist/commands/hydrogen/upgrade.js +2 -1
- package/dist/generator-templates/assets/vite/package.json +3 -4
- package/dist/generator-templates/assets/vite/vite.config.js +10 -2
- package/dist/generator-templates/starter/CHANGELOG.md +89 -0
- package/dist/generator-templates/starter/README.md +3 -44
- package/dist/generator-templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -0
- package/dist/generator-templates/starter/app/lib/fragments.ts +2 -0
- package/dist/generator-templates/starter/app/root.tsx +2 -5
- package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +1 -1
- package/dist/generator-templates/starter/app/routes/account.tsx +1 -1
- package/dist/generator-templates/starter/app/routes/collections.all.tsx +160 -0
- package/dist/generator-templates/starter/app/routes/products.$handle.tsx +1 -2
- package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +6 -3
- package/dist/generator-templates/starter/{remix.env.d.ts → env.d.ts} +8 -2
- package/dist/generator-templates/starter/package.json +14 -9
- package/dist/generator-templates/starter/server.ts +2 -1
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +59 -3
- package/dist/generator-templates/starter/vite.config.ts +21 -0
- package/dist/{commands/hydrogen/init.d.ts → init.d.ts} +11 -3
- package/dist/lib/check-lockfile.js +12 -18
- package/dist/lib/codegen.js +37 -13
- package/dist/lib/common.js +50 -0
- package/dist/lib/cpu-profiler.js +4 -1
- package/dist/lib/dev-shared.js +97 -0
- package/dist/lib/environment-variables.js +51 -30
- package/dist/lib/file.js +8 -1
- package/dist/lib/flags.js +37 -16
- package/dist/lib/graphql/admin/customer-application-update.js +29 -0
- package/dist/lib/graphql/admin/get-oxygen-data.js +1 -0
- package/dist/lib/graphql/admin/list-environments.js +1 -0
- package/dist/lib/graphql/admin/pull-variables.js +4 -4
- package/dist/lib/graphql/admin/test-helper.js +37 -0
- package/dist/lib/log.js +86 -13
- package/dist/lib/mini-oxygen/common.js +19 -33
- package/dist/lib/mini-oxygen/index.js +6 -2
- package/dist/lib/mini-oxygen/node.js +43 -31
- package/dist/lib/mini-oxygen/workerd.js +72 -165
- package/dist/lib/missing-routes.js +1 -1
- package/dist/lib/onboarding/common.js +82 -70
- package/dist/lib/onboarding/local.js +19 -9
- package/dist/lib/onboarding/remote.js +35 -30
- package/dist/lib/package-managers.js +24 -0
- package/dist/lib/remix-config.js +17 -1
- package/dist/lib/request-events.js +6 -1
- package/dist/lib/setups/i18n/replacers.js +9 -6
- package/dist/lib/setups/routes/generate.js +1 -0
- package/dist/lib/shell.js +2 -1
- package/dist/lib/shopify-config.js +19 -1
- package/dist/lib/template-diff.js +36 -15
- package/dist/lib/template-downloader.js +35 -5
- package/dist/lib/transpile/morph/typedefs.js +5 -2
- package/dist/lib/transpile/project.js +8 -4
- package/dist/lib/tunneling.js +44 -0
- package/dist/lib/virtual-routes.js +1 -1
- package/dist/lib/vite-config.js +39 -9
- package/oclif.manifest.json +711 -498
- package/package.json +32 -24
- package/dist/commands/hydrogen/deploy.test.js +0 -553
- package/dist/commands/hydrogen/env/list.test.js +0 -148
- package/dist/commands/hydrogen/env/pull.test.js +0 -207
- package/dist/commands/hydrogen/env/push__unstable.test.js +0 -383
- package/dist/commands/hydrogen/generate/route.test.js +0 -43
- package/dist/commands/hydrogen/init.test.js +0 -641
- package/dist/commands/hydrogen/link.test.js +0 -187
- package/dist/commands/hydrogen/list.test.js +0 -111
- package/dist/commands/hydrogen/setup.test.js +0 -61
- package/dist/commands/hydrogen/shortcut.test.js +0 -30
- package/dist/commands/hydrogen/unlink.test.js +0 -36
- package/dist/commands/hydrogen/upgrade.test.js +0 -786
- package/dist/generator-templates/starter/remix.config.js +0 -24
- package/dist/lib/auth.test.js +0 -157
- package/dist/lib/check-lockfile.test.js +0 -81
- package/dist/lib/check-version.test.js +0 -86
- package/dist/lib/environment-variables.test.js +0 -149
- package/dist/lib/file.test.js +0 -68
- package/dist/lib/flags.test.js +0 -43
- package/dist/lib/get-oxygen-deployment-data.test.js +0 -120
- package/dist/lib/gid.test.js +0 -15
- package/dist/lib/graphql/admin/client.test.js +0 -76
- package/dist/lib/graphql/admin/create-storefront.test.js +0 -64
- package/dist/lib/graphql/admin/link-storefront.test.js +0 -38
- package/dist/lib/graphql/admin/list-environments.test.js +0 -44
- package/dist/lib/graphql/admin/list-storefronts.test.js +0 -44
- package/dist/lib/graphql/admin/pull-variables.test.js +0 -43
- package/dist/lib/graphql/business-platform/user-account.test.js +0 -80
- package/dist/lib/log.test.js +0 -92
- package/dist/lib/mini-oxygen/assets.js +0 -134
- package/dist/lib/mini-oxygen/mini-oxygen.test.js +0 -214
- package/dist/lib/mini-oxygen/workerd-inspector-logs.js +0 -227
- package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +0 -200
- package/dist/lib/mini-oxygen/workerd-inspector.js +0 -219
- package/dist/lib/missing-routes.test.js +0 -45
- package/dist/lib/remix-version-check.test.js +0 -39
- package/dist/lib/remix-version-interop.test.js +0 -13
- package/dist/lib/setups/i18n/domains.test.js +0 -39
- package/dist/lib/setups/i18n/replacers.test.js +0 -261
- package/dist/lib/setups/i18n/subdomains.test.js +0 -39
- package/dist/lib/setups/i18n/subfolders.test.js +0 -39
- package/dist/lib/setups/routes/generate.test.js +0 -296
- package/dist/lib/shell.test.js +0 -111
- package/dist/lib/shopify-config.test.js +0 -199
- package/dist/lib/string.test.js +0 -16
- package/dist/lib/virtual-routes.test.js +0 -49
- package/dist/lib/vite/hydrogen-middleware.js +0 -82
- package/dist/lib/vite/mini-oxygen.js +0 -152
- package/dist/lib/vite/plugins.d.ts +0 -27
- package/dist/lib/vite/plugins.js +0 -139
- package/dist/lib/vite/shared.js +0 -10
- package/dist/lib/vite/utils.js +0 -55
- package/dist/lib/vite/worker-entry.js +0 -1518
- /package/dist/generator-templates/starter/{.eslintrc.js → .eslintrc.cjs} +0 -0
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
2
|
import { resolvePath, dirname } from '@shopify/cli-kit/node/path';
|
|
3
|
-
import { readFile } from '@shopify/cli-kit/node/fs';
|
|
3
|
+
import { readFile, createFileReadStream } from '@shopify/cli-kit/node/fs';
|
|
4
4
|
import { renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
5
|
-
import {
|
|
5
|
+
import { outputNewline } from '@shopify/cli-kit/node/output';
|
|
6
6
|
import colors from '@shopify/cli-kit/node/colors';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { OXYGEN_HEADERS_MAP, SUBREQUEST_PROFILER_ENDPOINT, logRequestLine } from './common.js';
|
|
7
|
+
import { handleMiniOxygenImportFail, logRequestLine, SUBREQUEST_PROFILER_ENDPOINT } from './common.js';
|
|
8
|
+
import { TUNNEL_DOMAIN, getUtilityBannerlines, getDebugBannerLine } from '../dev-shared.js';
|
|
10
9
|
import { setConstructors, handleDebugNetworkRequest, H2O_BINDING_NAME, createLogRequestEvent } from '../request-events.js';
|
|
11
|
-
import { STATIC_ASSET_EXTENSIONS, createAssetsServer, buildAssetsUrl } from './assets.js';
|
|
12
10
|
|
|
13
|
-
const PRIVATE_WORKERD_INSPECTOR_PORT = 9222;
|
|
14
|
-
const OXYGEN_WORKERD_COMPAT_PARAMS = {
|
|
15
|
-
compatibilityFlags: ["streams_enable_constructors"],
|
|
16
|
-
compatibilityDate: "2022-10-31"
|
|
17
|
-
};
|
|
18
11
|
async function startWorkerdServer({
|
|
19
12
|
root,
|
|
20
|
-
|
|
13
|
+
appPort,
|
|
21
14
|
inspectorPort: publicInspectorPort,
|
|
22
15
|
assetsPort,
|
|
23
16
|
debug = false,
|
|
@@ -26,58 +19,56 @@ async function startWorkerdServer({
|
|
|
26
19
|
buildPathClient,
|
|
27
20
|
env
|
|
28
21
|
}) {
|
|
29
|
-
const
|
|
30
|
-
const oxygenHeadersMap = Object.values(OXYGEN_HEADERS_MAP).reduce(
|
|
31
|
-
(acc, item) => {
|
|
32
|
-
acc[item.name] = item.defaultValue;
|
|
33
|
-
return acc;
|
|
34
|
-
},
|
|
35
|
-
{}
|
|
36
|
-
);
|
|
22
|
+
const { createMiniOxygen, Response } = await import('@shopify/mini-oxygen').catch(handleMiniOxygenImportFail);
|
|
37
23
|
setConstructors({ Response });
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"SUBREQUEST_PROFILER_ENDPOINT",
|
|
43
|
-
JSON.stringify(SUBREQUEST_PROFILER_ENDPOINT)
|
|
44
|
-
);
|
|
45
|
-
if (process.env.NODE_ENV === "test") {
|
|
46
|
-
stringifiedOxygenHandler = stringifiedOxygenHandler.replace(
|
|
47
|
-
/\w*vite_ssr_import[\w\d]*\./g,
|
|
48
|
-
""
|
|
24
|
+
async function handleCustomerAccountSchema() {
|
|
25
|
+
const require2 = createRequire(import.meta.url);
|
|
26
|
+
const filePath = require2.resolve(
|
|
27
|
+
"@shopify/hydrogen/customer-account.schema.json"
|
|
49
28
|
);
|
|
29
|
+
return new Response(createFileReadStream(filePath), {
|
|
30
|
+
headers: { "Content-Type": "application/json" }
|
|
31
|
+
});
|
|
50
32
|
}
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
33
|
+
const absoluteBundlePath = resolvePath(root, buildPathWorkerFile);
|
|
34
|
+
const mainWorkerName = "hydrogen";
|
|
35
|
+
const miniOxygen = createMiniOxygen({
|
|
36
|
+
debug,
|
|
54
37
|
port: appPort,
|
|
55
|
-
inspectorPort: privateInspectorPort,
|
|
56
|
-
log: new NoOpLog(),
|
|
57
|
-
liveReload: watch,
|
|
58
38
|
host: "localhost",
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
39
|
+
liveReload: watch,
|
|
40
|
+
requestHook: logRequestLine,
|
|
41
|
+
inspectorPort: publicInspectorPort,
|
|
42
|
+
inspectWorkerName: mainWorkerName,
|
|
43
|
+
assets: { port: assetsPort, directory: buildPathClient },
|
|
63
44
|
workers: [
|
|
64
45
|
{
|
|
65
|
-
name: "
|
|
46
|
+
name: "hydrogen:middleware",
|
|
66
47
|
modules: true,
|
|
67
|
-
script: `export default { fetch:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
48
|
+
script: `export default { fetch: (request, env) => {
|
|
49
|
+
const url = new URL(request.url);
|
|
50
|
+
if (url.hostname.endsWith('${TUNNEL_DOMAIN.ORIGINAL}')) {
|
|
51
|
+
url.hostname = url.hostname.replace(
|
|
52
|
+
'${TUNNEL_DOMAIN.ORIGINAL}',
|
|
53
|
+
'${TUNNEL_DOMAIN.REBRANDED}',
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return url.pathname === '${SUBREQUEST_PROFILER_ENDPOINT}'
|
|
58
|
+
? env.profiler.fetch(url, request)
|
|
59
|
+
: url.pathname === '/graphiql/customer-account.schema.json'
|
|
60
|
+
? env.assets.fetch(url, request)
|
|
61
|
+
: env.next.fetch(url, request)
|
|
62
|
+
}
|
|
63
|
+
}`,
|
|
72
64
|
serviceBindings: {
|
|
73
|
-
|
|
74
|
-
assets:
|
|
75
|
-
|
|
76
|
-
logRequest
|
|
65
|
+
profiler: handleDebugNetworkRequest,
|
|
66
|
+
assets: handleCustomerAccountSchema,
|
|
67
|
+
next: mainWorkerName
|
|
77
68
|
}
|
|
78
69
|
},
|
|
79
70
|
{
|
|
80
|
-
name:
|
|
71
|
+
name: mainWorkerName,
|
|
81
72
|
modulesRoot: dirname(absoluteBundlePath),
|
|
82
73
|
modules: [
|
|
83
74
|
{
|
|
@@ -86,7 +77,6 @@ async function startWorkerdServer({
|
|
|
86
77
|
contents: await readFile(absoluteBundlePath)
|
|
87
78
|
}
|
|
88
79
|
],
|
|
89
|
-
...OXYGEN_WORKERD_COMPAT_PARAMS,
|
|
90
80
|
bindings: { ...env },
|
|
91
81
|
serviceBindings: {
|
|
92
82
|
[H2O_BINDING_NAME]: createLogRequestEvent({
|
|
@@ -96,130 +86,47 @@ async function startWorkerdServer({
|
|
|
96
86
|
}
|
|
97
87
|
]
|
|
98
88
|
});
|
|
99
|
-
|
|
100
|
-
const miniOxygen = new Miniflare(miniOxygenOptions);
|
|
101
|
-
const listeningAt = (await miniOxygen.ready).origin;
|
|
102
|
-
const sourceMapPath = buildPathWorkerFile + ".map";
|
|
103
|
-
const reconnect = createInspectorConnector({
|
|
104
|
-
debug,
|
|
105
|
-
sourceMapPath,
|
|
106
|
-
absoluteBundlePath,
|
|
107
|
-
privateInspectorPort,
|
|
108
|
-
publicInspectorPort
|
|
109
|
-
});
|
|
110
|
-
await reconnect();
|
|
111
|
-
const assetsServer = createAssetsServer(buildPathClient);
|
|
112
|
-
assetsServer.listen(assetsPort);
|
|
89
|
+
const { workerUrl, inspectorUrl } = await miniOxygen.ready;
|
|
113
90
|
return {
|
|
114
|
-
port:
|
|
115
|
-
listeningAt,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
-
if (hydrogen) {
|
|
123
|
-
hydrogen.bindings = { ...nextOptions?.env ?? env };
|
|
91
|
+
port: Number(workerUrl.port),
|
|
92
|
+
listeningAt: workerUrl.origin,
|
|
93
|
+
reload(nextOptions) {
|
|
94
|
+
return miniOxygen.reload(async ({ workers }) => {
|
|
95
|
+
const mainWorker = workers.find(({ name }) => name === mainWorkerName);
|
|
96
|
+
if (Array.isArray(mainWorker.modules) && mainWorker.modules[0]) {
|
|
97
|
+
mainWorker.modules[0].contents = await readFile(absoluteBundlePath);
|
|
124
98
|
}
|
|
125
|
-
|
|
126
|
-
|
|
99
|
+
if (nextOptions) {
|
|
100
|
+
mainWorker.bindings = { ...nextOptions?.env ?? env };
|
|
101
|
+
}
|
|
102
|
+
return { workers };
|
|
103
|
+
});
|
|
127
104
|
},
|
|
128
105
|
showBanner(options) {
|
|
129
|
-
|
|
106
|
+
outputNewline();
|
|
107
|
+
const customSections = [];
|
|
108
|
+
if (options?.host) {
|
|
109
|
+
customSections.push({ body: getUtilityBannerlines(options.host) });
|
|
110
|
+
}
|
|
111
|
+
if (inspectorUrl) {
|
|
112
|
+
customSections.push({
|
|
113
|
+
body: { warn: getDebugBannerLine(Number(inspectorUrl.port)) }
|
|
114
|
+
});
|
|
115
|
+
}
|
|
130
116
|
renderSuccess({
|
|
131
117
|
headline: `${options?.headlinePrefix ?? ""}MiniOxygen (Worker Runtime) ${options?.mode ?? "development"} server running.`,
|
|
132
118
|
body: [
|
|
133
|
-
`View ${options?.appName
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
`View ${options?.appName ? colors.cyan(options?.appName) : "Hydrogen"} app:`,
|
|
120
|
+
{ link: { url: options?.host || workerUrl.origin } }
|
|
121
|
+
],
|
|
122
|
+
customSections
|
|
137
123
|
});
|
|
138
124
|
console.log("");
|
|
139
125
|
},
|
|
140
126
|
async close() {
|
|
141
|
-
assetsServer.closeAllConnections();
|
|
142
|
-
assetsServer.close();
|
|
143
127
|
await miniOxygen.dispose();
|
|
144
128
|
}
|
|
145
129
|
};
|
|
146
130
|
}
|
|
147
|
-
async function miniOxygenHandler(request, env, context) {
|
|
148
|
-
const { pathname } = new URL(request.url);
|
|
149
|
-
if (pathname === SUBREQUEST_PROFILER_ENDPOINT) {
|
|
150
|
-
return env.debugNetwork.fetch(request);
|
|
151
|
-
}
|
|
152
|
-
if (request.method === "GET") {
|
|
153
|
-
const staticAssetExtensions = new Set(env.staticAssetExtensions);
|
|
154
|
-
const wellKnown = pathname.startsWith("/.well-known");
|
|
155
|
-
const extension = pathname.split(".").at(-1) ?? "";
|
|
156
|
-
const isAsset = wellKnown || !!staticAssetExtensions.has(extension.toUpperCase());
|
|
157
|
-
if (isAsset) {
|
|
158
|
-
const response2 = await env.assets.fetch(
|
|
159
|
-
new Request(request.url, {
|
|
160
|
-
signal: request.signal,
|
|
161
|
-
headers: request.headers
|
|
162
|
-
})
|
|
163
|
-
);
|
|
164
|
-
if (response2.status !== 404)
|
|
165
|
-
return response2;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
const requestInit = {
|
|
169
|
-
headers: {
|
|
170
|
-
"request-id": crypto.randomUUID(),
|
|
171
|
-
...env.oxygenHeadersMap,
|
|
172
|
-
...Object.fromEntries(request.headers.entries())
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
const startTimeMs = Date.now();
|
|
176
|
-
const response = await env.hydrogen.fetch(request, requestInit);
|
|
177
|
-
const durationMs = Date.now() - startTimeMs;
|
|
178
|
-
context.waitUntil(
|
|
179
|
-
env.logRequest.fetch(
|
|
180
|
-
new Request(request.url, {
|
|
181
|
-
method: request.method,
|
|
182
|
-
signal: request.signal,
|
|
183
|
-
headers: {
|
|
184
|
-
...requestInit.headers,
|
|
185
|
-
"h2-duration-ms": String(durationMs),
|
|
186
|
-
"h2-response-status": String(response.status)
|
|
187
|
-
}
|
|
188
|
-
})
|
|
189
|
-
)
|
|
190
|
-
);
|
|
191
|
-
return response;
|
|
192
|
-
}
|
|
193
|
-
function createAssetHandler(assetsPort) {
|
|
194
|
-
const assetsServerOrigin = buildAssetsUrl(assetsPort);
|
|
195
|
-
return async (request) => {
|
|
196
|
-
return fetch(
|
|
197
|
-
new Request(
|
|
198
|
-
request.url.replace(
|
|
199
|
-
new URL(request.url).origin + "/",
|
|
200
|
-
assetsServerOrigin
|
|
201
|
-
),
|
|
202
|
-
request
|
|
203
|
-
)
|
|
204
|
-
);
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
async function logRequest(request) {
|
|
208
|
-
logRequestLine(request, {
|
|
209
|
-
responseStatus: Number(request.headers.get("h2-response-status") || 200),
|
|
210
|
-
durationMs: Number(request.headers.get("h2-duration-ms") || 0)
|
|
211
|
-
});
|
|
212
|
-
return new Response("ok");
|
|
213
|
-
}
|
|
214
|
-
function getDebugBannerLine(publicInspectorPort) {
|
|
215
|
-
const isVSCode = process.env.TERM_PROGRAM === "vscode";
|
|
216
|
-
const debuggingDocsLink = "https://h2o.fyi/debugging/server-code" + (isVSCode ? "#visual-studio-code" : "#step-2-attach-a-debugger");
|
|
217
|
-
return outputContent`\n\nDebugging enabled on port ${String(
|
|
218
|
-
publicInspectorPort
|
|
219
|
-
)}.\nAttach a ${outputToken.link(
|
|
220
|
-
colors.yellow(isVSCode ? "VSCode debugger" : "debugger"),
|
|
221
|
-
debuggingDocsLink
|
|
222
|
-
)} or open DevTools in http://localhost:${String(publicInspectorPort)}.`.value;
|
|
223
|
-
}
|
|
224
131
|
|
|
225
|
-
export {
|
|
132
|
+
export { startWorkerdServer };
|
|
@@ -58,7 +58,7 @@ function findMissingRoutes(config, requiredRoutes = REQUIRED_ROUTES) {
|
|
|
58
58
|
}
|
|
59
59
|
const optionalSegment = ":?[^\\/\\?]+\\?";
|
|
60
60
|
const reString = `^(${optionalSegment}\\/)?` + // Starts with an optional segment
|
|
61
|
-
requiredRoute.replaceAll(".", "\\.").replace(/\//g, `\\/(${optionalSegment}\\/)?`).replace(/:[^/)?]+/g, ":[^\\/]
|
|
61
|
+
requiredRoute.replaceAll(".", "\\.").replace(/\//g, `\\/(${optionalSegment}\\/)?`).replace(/:[^/)?]+/g, "(:[^\\/]+|\\*)") + // Replace params with regex
|
|
62
62
|
`(\\/${optionalSegment})?$`;
|
|
63
63
|
if (new RegExp(reString).test(currentRoute.path)) {
|
|
64
64
|
missingRoutes.delete(requiredRoute);
|
|
@@ -6,14 +6,14 @@ import { joinPath, resolvePath, basename } from '@shopify/cli-kit/node/path';
|
|
|
6
6
|
import { initializeGitRepository, addAllToGitFromDirectory, createGitCommit } from '@shopify/cli-kit/node/git';
|
|
7
7
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
8
8
|
import { rmdir, writeFile, fileExists, isDirectory } from '@shopify/cli-kit/node/fs';
|
|
9
|
-
import { outputDebug,
|
|
9
|
+
import { outputDebug, formatPackageManagerCommand } from '@shopify/cli-kit/node/output';
|
|
10
10
|
import colors from '@shopify/cli-kit/node/colors';
|
|
11
11
|
import { login, renderLoginSuccess } from '../auth.js';
|
|
12
12
|
import { renderI18nPrompt, setupI18nStrategy, I18N_STRATEGY_NAME_MAP } from '../setups/i18n/index.js';
|
|
13
13
|
import { titleize } from '../string.js';
|
|
14
14
|
import { ALIAS_NAME, createPlatformShortcut } from '../shell.js';
|
|
15
15
|
import { transpileProject } from '../transpile/index.js';
|
|
16
|
-
import {
|
|
16
|
+
import { 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
19
|
import { getStorefronts } from '../graphql/admin/link-storefront.js';
|
|
@@ -189,7 +189,7 @@ async function handleProjectLocation({
|
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
-
await rmdir(directory);
|
|
192
|
+
await rmdir(directory, { force: true });
|
|
193
193
|
}
|
|
194
194
|
return {
|
|
195
195
|
name: basename(location),
|
|
@@ -220,33 +220,10 @@ async function handleLanguage(projectDir, controller, flagLanguage) {
|
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
222
|
async function handleCssStrategy(projectDir, controller, flagStyling) {
|
|
223
|
-
|
|
224
|
-
abortSignal: controller.signal,
|
|
225
|
-
extraChoices: { none: "Skip and set up later" }
|
|
226
|
-
});
|
|
227
|
-
const cssStrategy = selection === "none" ? void 0 : selection;
|
|
228
|
-
return {
|
|
229
|
-
cssStrategy,
|
|
230
|
-
async setupCss() {
|
|
231
|
-
if (cssStrategy) {
|
|
232
|
-
const result = await setupCssStrategy(
|
|
233
|
-
cssStrategy,
|
|
234
|
-
{
|
|
235
|
-
rootDirectory: projectDir,
|
|
236
|
-
appDirectory: joinPath(projectDir, "app")
|
|
237
|
-
// Default value in new projects
|
|
238
|
-
},
|
|
239
|
-
true
|
|
240
|
-
);
|
|
241
|
-
if (result) {
|
|
242
|
-
await result.workPromise;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
};
|
|
223
|
+
return {};
|
|
247
224
|
}
|
|
248
|
-
async function handleDependencies(projectDir, controller, shouldInstallDeps) {
|
|
249
|
-
const detectedPackageManager = packageManagerFromUserAgent();
|
|
225
|
+
async function handleDependencies(projectDir, controller, packageManagerFromFlag, shouldInstallDeps) {
|
|
226
|
+
const detectedPackageManager = packageManagerFromFlag ?? packageManagerFromUserAgent();
|
|
250
227
|
let actualPackageManager = "npm";
|
|
251
228
|
if (shouldInstallDeps !== false) {
|
|
252
229
|
if (detectedPackageManager === "unknown") {
|
|
@@ -403,39 +380,6 @@ async function renderProjectReady(project, {
|
|
|
403
380
|
}
|
|
404
381
|
]
|
|
405
382
|
},
|
|
406
|
-
{
|
|
407
|
-
title: "Help\n",
|
|
408
|
-
body: {
|
|
409
|
-
list: {
|
|
410
|
-
items: [
|
|
411
|
-
{
|
|
412
|
-
link: {
|
|
413
|
-
label: "Guides",
|
|
414
|
-
url: "https://h2o.fyi/building"
|
|
415
|
-
}
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
link: {
|
|
419
|
-
label: "API reference",
|
|
420
|
-
url: "https://shopify.dev/docs/api/storefront"
|
|
421
|
-
}
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
link: {
|
|
425
|
-
label: "Demo Store code",
|
|
426
|
-
url: "https://github.com/Shopify/hydrogen/tree/HEAD/templates/demo-store"
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
[
|
|
430
|
-
"Run",
|
|
431
|
-
{
|
|
432
|
-
command: `${cliCommand} --help`
|
|
433
|
-
}
|
|
434
|
-
]
|
|
435
|
-
]
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
383
|
{
|
|
440
384
|
title: "Next steps\n",
|
|
441
385
|
body: [
|
|
@@ -445,13 +389,11 @@ async function renderProjectReady(project, {
|
|
|
445
389
|
[
|
|
446
390
|
"Run",
|
|
447
391
|
{
|
|
448
|
-
command:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
].filter(Boolean).join(" && ")
|
|
454
|
-
)}`.value
|
|
392
|
+
command: [
|
|
393
|
+
project.directory === process.cwd() ? void 0 : `cd ${project.location.replace(/^\.\//, "")}`,
|
|
394
|
+
depsInstalled ? void 0 : `${packageManager} install`,
|
|
395
|
+
formatPackageManagerCommand(packageManager, "dev")
|
|
396
|
+
].filter(Boolean).join(" && ")
|
|
455
397
|
}
|
|
456
398
|
]
|
|
457
399
|
].filter((step) => Boolean(step))
|
|
@@ -485,5 +427,75 @@ async function projectExists(projectDir) {
|
|
|
485
427
|
function normalizeRoutePath(routePath) {
|
|
486
428
|
return routePath.replace(/(^|\.)_index$/, "").replace(/((^|\.)[^\.]+)_\./g, "$1.").replace(/\.(?!\w+\])/g, "/").replace(/\$$/g, ":catchAll").replace(/\$/g, ":").replace(/[\[\]]/g, "").replace(/:\w*Handle/i, ":handle");
|
|
487
429
|
}
|
|
430
|
+
function generateRandomName() {
|
|
431
|
+
function getRandomElement(arr) {
|
|
432
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
433
|
+
}
|
|
434
|
+
const geographicalFeature = getRandomElement([
|
|
435
|
+
"Bay",
|
|
436
|
+
"Bend",
|
|
437
|
+
"Cape",
|
|
438
|
+
"Cliff",
|
|
439
|
+
"Cove",
|
|
440
|
+
"Creek",
|
|
441
|
+
"Dale",
|
|
442
|
+
"Dune",
|
|
443
|
+
"Fjord",
|
|
444
|
+
"Glade",
|
|
445
|
+
"Gulf",
|
|
446
|
+
"Hill",
|
|
447
|
+
"Isle",
|
|
448
|
+
"Knoll",
|
|
449
|
+
"Lake",
|
|
450
|
+
"Loch",
|
|
451
|
+
"Mesa",
|
|
452
|
+
"Peak",
|
|
453
|
+
"Pond",
|
|
454
|
+
"Quay",
|
|
455
|
+
"Reef",
|
|
456
|
+
"Ridge",
|
|
457
|
+
"Rise",
|
|
458
|
+
"River",
|
|
459
|
+
"Road",
|
|
460
|
+
"Shore",
|
|
461
|
+
"Strait",
|
|
462
|
+
"Stream",
|
|
463
|
+
"Vale",
|
|
464
|
+
"Valley",
|
|
465
|
+
"View",
|
|
466
|
+
"Vista"
|
|
467
|
+
]);
|
|
468
|
+
const colorNames = getRandomElement([
|
|
469
|
+
"Crimson",
|
|
470
|
+
"Azure",
|
|
471
|
+
"Coral",
|
|
472
|
+
"Fuchsia",
|
|
473
|
+
"Indigo",
|
|
474
|
+
"Ivory",
|
|
475
|
+
"Lavender",
|
|
476
|
+
"Lime",
|
|
477
|
+
"Magenta",
|
|
478
|
+
"Maroon",
|
|
479
|
+
"Orchid",
|
|
480
|
+
"Peach",
|
|
481
|
+
"Plum",
|
|
482
|
+
"Quartz",
|
|
483
|
+
"Salmon",
|
|
484
|
+
"Teal",
|
|
485
|
+
"Turquoise",
|
|
486
|
+
"Violet",
|
|
487
|
+
"Yellow",
|
|
488
|
+
"Ebony",
|
|
489
|
+
"Jade",
|
|
490
|
+
"Lilac",
|
|
491
|
+
"Mint",
|
|
492
|
+
"Onyx",
|
|
493
|
+
"Pearl",
|
|
494
|
+
"Ruby",
|
|
495
|
+
"Sapphire",
|
|
496
|
+
"Topaz"
|
|
497
|
+
]);
|
|
498
|
+
return `${colorNames} ${geographicalFeature}`;
|
|
499
|
+
}
|
|
488
500
|
|
|
489
|
-
export { LANGUAGES, commitAll, createAbortHandler, createInitialCommit, generateProjectEntries, handleCliShortcut, handleCssStrategy, handleDependencies, handleI18n, handleLanguage, handleProjectLocation, handleRouteGeneration, handleStorefrontLink, handleStorefrontSelection, renderProjectReady };
|
|
501
|
+
export { LANGUAGES, commitAll, createAbortHandler, createInitialCommit, generateProjectEntries, generateRandomName, handleCliShortcut, handleCssStrategy, handleDependencies, handleI18n, handleLanguage, handleProjectLocation, handleRouteGeneration, handleStorefrontLink, handleStorefrontSelection, renderProjectReady };
|
|
@@ -63,15 +63,20 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
63
63
|
})
|
|
64
64
|
)
|
|
65
65
|
).catch(abort);
|
|
66
|
+
const initMsg = {
|
|
67
|
+
create: "Creating storefront",
|
|
68
|
+
setup: `Setting up ${options.quickstart ? "Quickstart " : ""}project`,
|
|
69
|
+
install: "Installing dependencies. This could take a few minutes"
|
|
70
|
+
};
|
|
66
71
|
const tasks = [
|
|
67
72
|
{
|
|
68
|
-
title:
|
|
73
|
+
title: initMsg.create,
|
|
69
74
|
task: async () => {
|
|
70
75
|
await createStorefrontPromise;
|
|
71
76
|
}
|
|
72
77
|
},
|
|
73
78
|
{
|
|
74
|
-
title:
|
|
79
|
+
title: initMsg.setup,
|
|
75
80
|
task: async () => {
|
|
76
81
|
await backgroundWorkPromise;
|
|
77
82
|
}
|
|
@@ -152,6 +157,7 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
152
157
|
const { packageManager, shouldInstallDeps, installDeps } = await handleDependencies(
|
|
153
158
|
project.directory,
|
|
154
159
|
controller,
|
|
160
|
+
options.packageManager,
|
|
155
161
|
options.installDeps
|
|
156
162
|
);
|
|
157
163
|
const setupSummary = {
|
|
@@ -171,7 +177,7 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
171
177
|
}
|
|
172
178
|
});
|
|
173
179
|
tasks.push({
|
|
174
|
-
title:
|
|
180
|
+
title: initMsg.install,
|
|
175
181
|
task: async () => {
|
|
176
182
|
await installingDepsPromise;
|
|
177
183
|
}
|
|
@@ -190,12 +196,16 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
190
196
|
});
|
|
191
197
|
showShortcutBanner();
|
|
192
198
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
+
if (options.quickstart) {
|
|
200
|
+
console.log("\n");
|
|
201
|
+
} else {
|
|
202
|
+
renderSuccess({
|
|
203
|
+
headline: [
|
|
204
|
+
{ userInput: storefrontInfo?.title ?? project.name },
|
|
205
|
+
"is ready to build."
|
|
206
|
+
]
|
|
207
|
+
});
|
|
208
|
+
}
|
|
199
209
|
const continueWithSetup = (options.i18n ?? options.routes) !== void 0 || await renderConfirmationPrompt({
|
|
200
210
|
message: "Do you want to scaffold routes and core functionality?",
|
|
201
211
|
confirmationMessage: "Yes, set up now",
|
|
@@ -1,37 +1,19 @@
|
|
|
1
1
|
import { readdir } from 'node:fs/promises';
|
|
2
2
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
3
|
-
import {
|
|
3
|
+
import { copyFile, fileExists } from '@shopify/cli-kit/node/fs';
|
|
4
4
|
import { readAndParsePackageJson } from '@shopify/cli-kit/node/node-package-manager';
|
|
5
5
|
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
6
6
|
import { renderTasks, renderInfo } from '@shopify/cli-kit/node/ui';
|
|
7
|
-
import {
|
|
7
|
+
import { downloadExternalRepo, downloadMonorepoTemplates } from '../template-downloader.js';
|
|
8
8
|
import { applyTemplateDiff } from '../template-diff.js';
|
|
9
9
|
import { getCliCommand } from '../shell.js';
|
|
10
10
|
import { createAbortHandler, handleProjectLocation, handleLanguage, createInitialCommit, handleDependencies, commitAll, renderProjectReady } from './common.js';
|
|
11
11
|
|
|
12
|
+
const DEMO_STORE_REPO = "shopify/hydrogen-demo-store";
|
|
12
13
|
async function setupRemoteTemplate(options, controller) {
|
|
13
|
-
const appTemplate = options.template;
|
|
14
|
+
const appTemplate = options.template === "demo-store" ? DEMO_STORE_REPO : options.template;
|
|
14
15
|
let abort = createAbortHandler(controller);
|
|
15
|
-
const backgroundDownloadPromise =
|
|
16
|
-
signal: controller.signal
|
|
17
|
-
}).then(async ({ templatesDir, examplesDir }) => {
|
|
18
|
-
const templatePath = joinPath(templatesDir, appTemplate);
|
|
19
|
-
const examplePath = joinPath(examplesDir, appTemplate);
|
|
20
|
-
if (await fileExists(templatePath)) {
|
|
21
|
-
return { templatesDir, sourcePath: templatePath };
|
|
22
|
-
}
|
|
23
|
-
if (await fileExists(examplePath)) {
|
|
24
|
-
return { templatesDir, sourcePath: examplePath };
|
|
25
|
-
}
|
|
26
|
-
const availableTemplates = (await Promise.all([readdir(examplesDir), readdir(templatesDir)]).catch(
|
|
27
|
-
() => []
|
|
28
|
-
)).flat().filter((name) => name !== "skeleton" && !name.endsWith(".md")).sort();
|
|
29
|
-
throw new AbortError(
|
|
30
|
-
`Unknown value in \`--template\` flag "${appTemplate}".
|
|
31
|
-
Skip the flag or provide the name of a template or example in the Hydrogen repository.`,
|
|
32
|
-
availableTemplates.length === 0 ? "" : { list: { title: "Available templates:", items: availableTemplates } }
|
|
33
|
-
);
|
|
34
|
-
}).catch(abort);
|
|
16
|
+
const backgroundDownloadPromise = appTemplate.includes("/") ? getExternalTemplate(appTemplate, controller.signal).catch(abort) : getMonorepoTemplate(appTemplate, controller.signal).catch(abort);
|
|
35
17
|
const project = await handleProjectLocation({ ...options, controller });
|
|
36
18
|
if (!project)
|
|
37
19
|
return;
|
|
@@ -42,16 +24,12 @@ Skip the flag or provide the name of a template or example in the Hydrogen repos
|
|
|
42
24
|
let backgroundWorkPromise = Promise.resolve().then(async () => {
|
|
43
25
|
if (controller.signal.aborted)
|
|
44
26
|
return;
|
|
45
|
-
const { sourcePath,
|
|
27
|
+
const { sourcePath, skeletonPath } = downloaded;
|
|
46
28
|
const pkgJson = await readAndParsePackageJson(
|
|
47
29
|
joinPath(sourcePath, "package.json")
|
|
48
30
|
);
|
|
49
31
|
if (pkgJson.scripts?.dev?.includes("--diff")) {
|
|
50
|
-
return applyTemplateDiff(
|
|
51
|
-
project.directory,
|
|
52
|
-
sourcePath,
|
|
53
|
-
joinPath(templatesDir, "skeleton")
|
|
54
|
-
);
|
|
32
|
+
return applyTemplateDiff(project.directory, sourcePath, skeletonPath);
|
|
55
33
|
}
|
|
56
34
|
return copyFile(sourcePath, project.directory);
|
|
57
35
|
}).catch(abort);
|
|
@@ -65,6 +43,7 @@ Skip the flag or provide the name of a template or example in the Hydrogen repos
|
|
|
65
43
|
const { packageManager, shouldInstallDeps, installDeps } = await handleDependencies(
|
|
66
44
|
project.directory,
|
|
67
45
|
controller,
|
|
46
|
+
options.packageManager,
|
|
68
47
|
options.installDeps
|
|
69
48
|
);
|
|
70
49
|
const setupSummary = {
|
|
@@ -108,7 +87,7 @@ Skip the flag or provide the name of a template or example in the Hydrogen repos
|
|
|
108
87
|
}
|
|
109
88
|
await renderProjectReady(project, setupSummary);
|
|
110
89
|
renderInfo({
|
|
111
|
-
headline: `Your project will display inventory from ${options.template
|
|
90
|
+
headline: `Your project will display inventory from ${options.template.endsWith(DEMO_STORE_REPO) ? "the Hydrogen Demo Store" : "Mock.shop"}.`,
|
|
112
91
|
body: `To connect this project to your Shopify store\u2019s inventory, update \`${project.name}/.env\` with your store ID and Storefront API key.`
|
|
113
92
|
});
|
|
114
93
|
return {
|
|
@@ -116,5 +95,31 @@ Skip the flag or provide the name of a template or example in the Hydrogen repos
|
|
|
116
95
|
...setupSummary
|
|
117
96
|
};
|
|
118
97
|
}
|
|
98
|
+
async function getExternalTemplate(appTemplate, signal) {
|
|
99
|
+
const { templateDir } = await downloadExternalRepo(appTemplate, signal);
|
|
100
|
+
return { sourcePath: templateDir };
|
|
101
|
+
}
|
|
102
|
+
async function getMonorepoTemplate(appTemplate, signal) {
|
|
103
|
+
const { templatesDir, examplesDir } = await downloadMonorepoTemplates({
|
|
104
|
+
signal
|
|
105
|
+
});
|
|
106
|
+
const skeletonPath = joinPath(templatesDir, "skeleton");
|
|
107
|
+
const templatePath = joinPath(templatesDir, appTemplate);
|
|
108
|
+
const examplePath = joinPath(examplesDir, appTemplate);
|
|
109
|
+
if (await fileExists(templatePath)) {
|
|
110
|
+
return { skeletonPath, sourcePath: templatePath };
|
|
111
|
+
}
|
|
112
|
+
if (await fileExists(examplePath)) {
|
|
113
|
+
return { skeletonPath, sourcePath: examplePath };
|
|
114
|
+
}
|
|
115
|
+
const availableTemplates = (await Promise.all([readdir(examplesDir), readdir(templatesDir)]).catch(
|
|
116
|
+
() => []
|
|
117
|
+
)).flat().filter((name) => name !== "skeleton" && !name.endsWith(".md")).concat("demo-store").sort();
|
|
118
|
+
throw new AbortError(
|
|
119
|
+
`Unknown value in \`--template\` flag "${appTemplate}".
|
|
120
|
+
Skip the flag or provide the name of a template or example in the Hydrogen repository or a URL to a git repository.`,
|
|
121
|
+
availableTemplates.length === 0 ? "" : { list: { title: "Available templates:", items: availableTemplates } }
|
|
122
|
+
);
|
|
123
|
+
}
|
|
119
124
|
|
|
120
125
|
export { setupRemoteTemplate };
|