@shopify/cli-hydrogen 8.1.0 → 8.2.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/assets/hydrogen/starter/CHANGELOG.md +166 -0
- package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +37 -0
- package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +150 -0
- package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +68 -0
- package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +101 -0
- package/dist/assets/hydrogen/starter/app/components/Header.tsx +3 -3
- package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +2 -2
- package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +80 -0
- package/dist/assets/hydrogen/starter/app/components/ProductImage.tsx +23 -0
- package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +27 -0
- package/dist/assets/hydrogen/starter/app/lib/session.ts +5 -0
- package/dist/assets/hydrogen/starter/app/root.tsx +23 -36
- package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -5
- package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +12 -70
- package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +7 -14
- package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +1 -8
- package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +5 -22
- package/dist/assets/hydrogen/starter/app/routes/account.tsx +0 -1
- package/dist/assets/hydrogen/starter/app/routes/cart.tsx +1 -3
- package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +51 -232
- package/dist/assets/hydrogen/starter/package.json +10 -11
- package/dist/assets/hydrogen/starter/server.ts +4 -0
- package/dist/assets/hydrogen/tailwind/package.json +1 -6
- package/dist/assets/hydrogen/tailwind/tailwind.css +6 -3
- package/dist/assets/hydrogen/vanilla-extract/package.json +2 -3
- package/dist/assets/hydrogen/virtual-routes/components/{Layout.jsx → PageLayout.jsx} +2 -2
- package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +1 -2
- package/dist/assets/hydrogen/virtual-routes/components/RequestTable.jsx +1 -2
- package/dist/assets/hydrogen/virtual-routes/routes/index.jsx +1 -2
- package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +8 -30
- package/dist/commands/hydrogen/build.js +33 -10
- package/dist/commands/hydrogen/customer-account/push.js +3 -6
- package/dist/commands/hydrogen/debug/cpu.js +3 -3
- package/dist/commands/hydrogen/deploy.js +14 -3
- package/dist/commands/hydrogen/dev.js +3 -6
- package/dist/commands/hydrogen/env/list.js +1 -2
- package/dist/commands/hydrogen/env/pull.js +2 -4
- package/dist/commands/hydrogen/env/push.js +6 -12
- package/dist/commands/hydrogen/init.d.ts +18 -15
- package/dist/commands/hydrogen/init.js +12 -24
- package/dist/commands/hydrogen/link.js +1 -2
- package/dist/commands/hydrogen/preview.js +4 -6
- package/dist/commands/hydrogen/setup/css.js +29 -12
- package/dist/commands/hydrogen/setup/vite.js +3 -6
- package/dist/commands/hydrogen/setup.js +8 -7
- package/dist/commands/hydrogen/upgrade.js +16 -32
- package/dist/hooks/init.js +50 -6
- package/dist/index.d.ts +46 -46
- package/dist/lib/auth.js +1 -2
- package/dist/lib/build.js +1 -2
- package/dist/lib/bundle/analyzer.js +39 -24
- package/dist/lib/bundle/vite-plugin.js +161 -0
- package/dist/lib/check-cli-version.js +61 -0
- package/dist/lib/check-lockfile.js +2 -2
- package/dist/lib/classic-compiler/build.js +3 -3
- package/dist/lib/classic-compiler/dev.js +5 -10
- package/dist/lib/codegen.js +8 -16
- package/dist/lib/defer.js +2 -4
- package/dist/lib/environment-variables.js +2 -4
- package/dist/lib/file.js +15 -7
- package/dist/lib/flags.js +10 -0
- package/dist/lib/get-oxygen-deployment-data.js +1 -2
- package/dist/lib/graphiql-url.js +1 -2
- package/dist/lib/import-utils.js +3 -2
- package/dist/lib/log.js +11 -22
- package/dist/lib/mini-oxygen/common.js +1 -2
- package/dist/lib/mini-oxygen/node.js +1 -2
- package/dist/lib/missing-routes.js +1 -2
- package/dist/lib/onboarding/common.js +60 -15
- package/dist/lib/onboarding/local.js +14 -13
- package/dist/lib/onboarding/remote.js +16 -9
- package/dist/lib/onboarding/setup-template.mocks.js +6 -3
- package/dist/lib/remix-config.js +2 -4
- package/dist/lib/remix-version-check.js +1 -2
- package/dist/lib/request-events.js +3 -6
- package/dist/lib/setups/css/assets.js +1 -1
- package/dist/lib/setups/css/index.js +17 -10
- package/dist/lib/setups/css/replacers.js +74 -76
- package/dist/lib/setups/css/tailwind.js +16 -20
- package/dist/lib/setups/css/vanilla-extract.js +8 -5
- package/dist/lib/setups/i18n/replacers.js +1 -2
- package/dist/lib/setups/routes/generate.js +18 -19
- package/dist/lib/shell.js +5 -10
- package/dist/lib/template-diff.js +83 -104
- package/dist/lib/template-downloader.js +2 -2
- package/dist/lib/transpile/morph/functions.js +3 -6
- package/dist/lib/transpile/morph/index.js +2 -4
- package/dist/lib/transpile/morph/typedefs.js +3 -6
- package/dist/lib/transpile/morph/utils.js +2 -4
- package/dist/lib/transpile/project.js +4 -3
- package/oclif.manifest.json +51 -4
- package/package.json +8 -12
- package/dist/assets/hydrogen/css-modules/package.json +0 -6
- package/dist/assets/hydrogen/postcss/package.json +0 -10
- package/dist/assets/hydrogen/postcss/postcss.config.js +0 -8
- package/dist/assets/hydrogen/starter/app/components/Cart.tsx +0 -364
- package/dist/assets/hydrogen/tailwind/postcss.config.js +0 -10
- package/dist/assets/hydrogen/tailwind/tailwind.config.js +0 -8
- package/dist/lib/check-version.js +0 -75
- package/dist/lib/setups/css/css-modules.js +0 -23
- package/dist/lib/setups/css/postcss.js +0 -31
package/dist/lib/file.js
CHANGED
|
@@ -6,8 +6,7 @@ import { formatCode } from './format-code.js';
|
|
|
6
6
|
|
|
7
7
|
async function replaceFileContent(filepath, formatConfig, replacer) {
|
|
8
8
|
let content = await replacer(await readFile(filepath));
|
|
9
|
-
if (typeof content !== "string")
|
|
10
|
-
return;
|
|
9
|
+
if (typeof content !== "string") return;
|
|
11
10
|
if (formatConfig) {
|
|
12
11
|
content = await formatCode(content, formatConfig, filepath);
|
|
13
12
|
}
|
|
@@ -62,8 +61,7 @@ async function mergePackageJson(sourceDir, targetDir, options) {
|
|
|
62
61
|
(key) => !MANAGED_PACKAGE_JSON_KEYS.includes(key)
|
|
63
62
|
);
|
|
64
63
|
for (const key of unmanagedKeys) {
|
|
65
|
-
if (ignoredKeys.has(key))
|
|
66
|
-
continue;
|
|
64
|
+
if (ignoredKeys.has(key)) continue;
|
|
67
65
|
const sourceValue = sourcePkgJson[key];
|
|
68
66
|
const targetValue = targetPkgJson[key];
|
|
69
67
|
const newValue = Array.isArray(sourceValue) && Array.isArray(targetValue) ? [...targetValue, ...sourceValue] : typeof sourceValue === "object" && typeof targetValue === "object" ? { ...targetValue, ...sourceValue } : sourceValue;
|
|
@@ -73,8 +71,7 @@ async function mergePackageJson(sourceDir, targetDir, options) {
|
|
|
73
71
|
([dep]) => dep.startsWith("@remix-run/")
|
|
74
72
|
)?.[1];
|
|
75
73
|
for (const key of MANAGED_PACKAGE_JSON_KEYS) {
|
|
76
|
-
if (ignoredKeys.has(key))
|
|
77
|
-
continue;
|
|
74
|
+
if (ignoredKeys.has(key)) continue;
|
|
78
75
|
if (sourcePkgJson[key]) {
|
|
79
76
|
targetPkgJson[key] = [
|
|
80
77
|
.../* @__PURE__ */ new Set([
|
|
@@ -96,5 +93,16 @@ async function mergePackageJson(sourceDir, targetDir, options) {
|
|
|
96
93
|
options?.onResult?.(targetPkgJson) ?? targetPkgJson
|
|
97
94
|
);
|
|
98
95
|
}
|
|
96
|
+
async function mergeTsConfig(sourceDir, targetDir) {
|
|
97
|
+
const sourceTsConfig = await readFile(joinPath(sourceDir, "tsconfig.json"));
|
|
98
|
+
const sourceTsTypes = sourceTsConfig.match(/"types": \[(.*?)\]/)?.[1];
|
|
99
|
+
if (sourceTsTypes) {
|
|
100
|
+
replaceFileContent(
|
|
101
|
+
joinPath(targetDir, "tsconfig.json"),
|
|
102
|
+
false,
|
|
103
|
+
(content) => content.replace(/"types":\s*\[[^\]]*\]/, `"types": [${sourceTsTypes}]`)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
99
107
|
|
|
100
|
-
export { findFileWithExtension, mergePackageJson, replaceFileContent };
|
|
108
|
+
export { findFileWithExtension, mergePackageJson, mergeTsConfig, replaceFileContent };
|
package/dist/lib/flags.js
CHANGED
|
@@ -3,6 +3,7 @@ import { camelize } from '@shopify/cli-kit/common/string';
|
|
|
3
3
|
import { renderInfo } from '@shopify/cli-kit/node/ui';
|
|
4
4
|
import { normalizeStoreFqdn } from '@shopify/cli-kit/node/context/fqdn';
|
|
5
5
|
import colors from '@shopify/cli-kit/node/colors';
|
|
6
|
+
import { STYLING_CHOICES } from './setups/css/index.js';
|
|
6
7
|
import { I18N_CHOICES } from './setups/i18n/index.js';
|
|
7
8
|
|
|
8
9
|
const DEFAULT_APP_PORT = 3e3;
|
|
@@ -86,6 +87,15 @@ const commonFlags = {
|
|
|
86
87
|
dependsOn: ["codegen"]
|
|
87
88
|
})
|
|
88
89
|
},
|
|
90
|
+
styling: {
|
|
91
|
+
styling: Flags.string({
|
|
92
|
+
description: `Sets the styling strategy to use. One of ${STYLING_CHOICES.map(
|
|
93
|
+
(item) => `\`${item}\``
|
|
94
|
+
).join(", ")}.`,
|
|
95
|
+
choices: STYLING_CHOICES,
|
|
96
|
+
env: "SHOPIFY_HYDROGEN_FLAG_STYLING"
|
|
97
|
+
})
|
|
98
|
+
},
|
|
89
99
|
markets: {
|
|
90
100
|
markets: Flags.string({
|
|
91
101
|
description: `Sets the URL structure to support multiple markets. Must be one of: ${I18N_CHOICES.map(
|
|
@@ -18,8 +18,7 @@ async function getOxygenDeploymentData({
|
|
|
18
18
|
config,
|
|
19
19
|
cliCommand
|
|
20
20
|
});
|
|
21
|
-
if (!linkedStorefront)
|
|
22
|
-
return;
|
|
21
|
+
if (!linkedStorefront) return;
|
|
23
22
|
config.storefront = linkedStorefront;
|
|
24
23
|
const { storefront } = await getOxygenData(session, config.storefront.id);
|
|
25
24
|
if (!storefront) {
|
package/dist/lib/graphiql-url.js
CHANGED
|
@@ -5,8 +5,7 @@ function getGraphiQLUrl({
|
|
|
5
5
|
let url = `${host.endsWith("/") ? host.slice(0, -1) : host}/graphiql`;
|
|
6
6
|
if (graphql) {
|
|
7
7
|
let { query, variables } = graphql;
|
|
8
|
-
if (typeof variables !== "string")
|
|
9
|
-
variables = JSON.stringify(variables);
|
|
8
|
+
if (typeof variables !== "string") variables = JSON.stringify(variables);
|
|
10
9
|
url += `?query=${encodeURIComponent(query)}${variables ? `&variables=${encodeURIComponent(variables)}` : ""}`;
|
|
11
10
|
if (graphql.schema) {
|
|
12
11
|
url += `&schema=${graphql.schema}`;
|
package/dist/lib/import-utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
2
3
|
import { findUpAndReadPackageJson } from '@shopify/cli-kit/node/node-package-manager';
|
|
3
4
|
import { joinPath, dirname } from '@shopify/cli-kit/node/path';
|
|
4
5
|
|
|
@@ -11,11 +12,11 @@ async function importVite(root) {
|
|
|
11
12
|
dirname(vitePackageJson.path),
|
|
12
13
|
viteNodeIndexFile
|
|
13
14
|
);
|
|
14
|
-
return import(viteNodePath);
|
|
15
|
+
return import(pathToFileURL(viteNodePath).href);
|
|
15
16
|
}
|
|
16
17
|
function importLocal(packageName, path) {
|
|
17
18
|
const realPath = require2.resolve(packageName, { paths: [path] });
|
|
18
|
-
return import(realPath);
|
|
19
|
+
return import(pathToFileURL(realPath).href);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export { importLocal, importVite };
|
package/dist/lib/log.js
CHANGED
|
@@ -25,8 +25,7 @@ function debounceMessage(args, debounceFor) {
|
|
|
25
25
|
const message = item?.message ?? item;
|
|
26
26
|
return typeof message === "string" ? message : "";
|
|
27
27
|
}).filter(Boolean).join("");
|
|
28
|
-
if (printedMessages.has(key))
|
|
29
|
-
return true;
|
|
28
|
+
if (printedMessages.has(key)) return true;
|
|
30
29
|
printedMessages.add(key);
|
|
31
30
|
if (debounceFor !== true) {
|
|
32
31
|
setTimeout(() => printedMessages.delete(key), debounceFor ?? 1e3);
|
|
@@ -47,18 +46,15 @@ function injectLogReplacer(method, debouncer) {
|
|
|
47
46
|
return;
|
|
48
47
|
}
|
|
49
48
|
const replacers = messageReplacers.reduce((acc, [matcher, replacer]) => {
|
|
50
|
-
if (matcher(args, acc.length))
|
|
51
|
-
acc.push(replacer);
|
|
49
|
+
if (matcher(args, acc.length)) acc.push(replacer);
|
|
52
50
|
return acc;
|
|
53
51
|
}, []);
|
|
54
|
-
if (replacers.length === 0)
|
|
55
|
-
return originalConsole[method](...args);
|
|
52
|
+
if (replacers.length === 0) return originalConsole[method](...args);
|
|
56
53
|
const result = replacers.reduce(
|
|
57
54
|
(resultArgs, replacer) => resultArgs && replacer(resultArgs),
|
|
58
55
|
args
|
|
59
56
|
);
|
|
60
|
-
if (result)
|
|
61
|
-
return originalConsole[method](...result);
|
|
57
|
+
if (result) return originalConsole[method](...result);
|
|
62
58
|
};
|
|
63
59
|
}
|
|
64
60
|
}
|
|
@@ -200,21 +196,17 @@ function muteAuthLogs({
|
|
|
200
196
|
if (process.stdout.write === originalWrite) {
|
|
201
197
|
const write = originalWrite.bind(process.stdout);
|
|
202
198
|
process.stdout.write = (item, cb) => {
|
|
203
|
-
if (typeof item !== "string")
|
|
204
|
-
return write(item, cb);
|
|
199
|
+
if (typeof item !== "string") return write(item, cb);
|
|
205
200
|
const replacers = messageReplacers.reduce((acc, [matcher, replacer]) => {
|
|
206
|
-
if (matcher([item], acc.length))
|
|
207
|
-
acc.push(replacer);
|
|
201
|
+
if (matcher([item], acc.length)) acc.push(replacer);
|
|
208
202
|
return acc;
|
|
209
203
|
}, []);
|
|
210
|
-
if (replacers.length === 0)
|
|
211
|
-
return write(item, cb);
|
|
204
|
+
if (replacers.length === 0) return write(item, cb);
|
|
212
205
|
const result = replacers.reduce(
|
|
213
206
|
(resultArgs, replacer) => resultArgs && replacer(resultArgs),
|
|
214
207
|
[item]
|
|
215
208
|
);
|
|
216
|
-
if (result)
|
|
217
|
-
return write(result[0], cb);
|
|
209
|
+
if (result) return write(result[0], cb);
|
|
218
210
|
};
|
|
219
211
|
}
|
|
220
212
|
addMessageReplacers(
|
|
@@ -225,8 +217,7 @@ function muteAuthLogs({
|
|
|
225
217
|
const content = first.replace(" to Shopify Partners", "");
|
|
226
218
|
const link = content.match(/(https?:\/\/.*)Log in/)?.[1];
|
|
227
219
|
onKeyTimeout(link);
|
|
228
|
-
if (link)
|
|
229
|
-
return;
|
|
220
|
+
if (link) return;
|
|
230
221
|
return [content];
|
|
231
222
|
}
|
|
232
223
|
],
|
|
@@ -266,8 +257,7 @@ function enhanceH2Logs(options) {
|
|
|
266
257
|
stringArg += "\nRun `h2 link` to link your store.";
|
|
267
258
|
}
|
|
268
259
|
const [, type, scope, message] = stringArg.match(/\[h2:([^:]+):([^\]]+)\]\s+(.*)$/ims) || [];
|
|
269
|
-
if (!type || !scope || !message)
|
|
270
|
-
return args;
|
|
260
|
+
if (!type || !scope || !message) return args;
|
|
271
261
|
const headline = `In Hydrogen's \`${scope.trim()}\`:
|
|
272
262
|
|
|
273
263
|
`;
|
|
@@ -281,8 +271,7 @@ function enhanceH2Logs(options) {
|
|
|
281
271
|
colors.magentaBright(`\`${options.cliCommand ?? "$1"} $2\``)
|
|
282
272
|
);
|
|
283
273
|
}
|
|
284
|
-
if (hasLinks || hasCommands)
|
|
285
|
-
lines.pop();
|
|
274
|
+
if (hasLinks || hasCommands) lines.pop();
|
|
286
275
|
if (type === "error" || errorObject) {
|
|
287
276
|
let tryMessage = hasLinks || hasCommands ? lastLine : void 0;
|
|
288
277
|
let stack = errorObject?.stack;
|
|
@@ -18,8 +18,7 @@ function logRequestLine({
|
|
|
18
18
|
}) {
|
|
19
19
|
try {
|
|
20
20
|
const url = new URL(request.url);
|
|
21
|
-
if (DEV_ROUTES.has(url.pathname) || url.pathname === "/favicon.ico")
|
|
22
|
-
return;
|
|
21
|
+
if (DEV_ROUTES.has(url.pathname) || url.pathname === "/favicon.ico") return;
|
|
23
22
|
const isDataRequest = url.searchParams.has("_data");
|
|
24
23
|
let route = request.url.replace(url.origin, "");
|
|
25
24
|
let info = "";
|
|
@@ -42,8 +42,7 @@ async function startNodeServer({
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
if (debug) {
|
|
45
|
-
if (!inspectorPort)
|
|
46
|
-
inspectorPort = await findPort(DEFAULT_INSPECTOR_PORT);
|
|
45
|
+
if (!inspectorPort) inspectorPort = await findPort(DEFAULT_INSPECTOR_PORT);
|
|
47
46
|
(await import('node:inspector')).open(inspectorPort);
|
|
48
47
|
}
|
|
49
48
|
const readWorkerFile = () => readFile(buildPathWorkerFile).catch((error) => {
|
|
@@ -51,8 +51,7 @@ function findMissingRoutes(config, requiredRoutes = REQUIRED_ROUTES) {
|
|
|
51
51
|
const parentRoute = userRoutes.find(
|
|
52
52
|
(r) => r.id === currentRoute.parentId
|
|
53
53
|
);
|
|
54
|
-
if (!parentRoute)
|
|
55
|
-
break;
|
|
54
|
+
if (!parentRoute) break;
|
|
56
55
|
currentRoute.path = `${parentRoute.path}/${currentRoute.path}`;
|
|
57
56
|
currentRoute.parentId = parentRoute.parentId;
|
|
58
57
|
}
|
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
import { readdir } from 'node:fs/promises';
|
|
1
|
+
import { symlink, readdir } from 'node:fs/promises';
|
|
2
2
|
import { packageManagerFromUserAgent, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
|
|
3
3
|
import { renderConfirmationPrompt, renderInfo, renderTextPrompt, renderSelectPrompt, renderFatalError, renderWarning, renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
4
4
|
import { hyphenate, capitalize } from '@shopify/cli-kit/common/string';
|
|
5
5
|
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
|
-
import { rmdir, writeFile, fileExists, isDirectory } from '@shopify/cli-kit/node/fs';
|
|
8
|
+
import { rmdir, copyFile, writeFile, fileExists, isDirectory } from '@shopify/cli-kit/node/fs';
|
|
9
9
|
import { outputDebug, formatPackageManagerCommand } from '@shopify/cli-kit/node/output';
|
|
10
|
+
import { currentProcessIsGlobal } from '@shopify/cli-kit/node/is-global';
|
|
10
11
|
import colors from '@shopify/cli-kit/node/colors';
|
|
11
12
|
import { login, renderLoginSuccess } from '../auth.js';
|
|
12
13
|
import { renderI18nPrompt, setupI18nStrategy, I18N_STRATEGY_NAME_MAP } from '../setups/i18n/index.js';
|
|
13
14
|
import { titleize } from '../string.js';
|
|
14
15
|
import { ALIAS_NAME, createPlatformShortcut } from '../shell.js';
|
|
15
16
|
import { transpileProject } from '../transpile/index.js';
|
|
16
|
-
import { CSS_STRATEGY_NAME_MAP } from '../setups/css/index.js';
|
|
17
|
+
import { renderCssPrompt, setupCssStrategy, CSS_STRATEGY_NAME_MAP } from '../setups/css/index.js';
|
|
17
18
|
import { renderRoutePrompt, generateRoutes, generateProjectFile } from '../setups/routes/generate.js';
|
|
18
19
|
import { execAsync } from '../process.js';
|
|
19
20
|
import { getStorefronts } from '../graphql/admin/link-storefront.js';
|
|
21
|
+
import { isHydrogenMonorepo, getSkeletonSourceDir, getRepoNodeModules } from '../build.js';
|
|
20
22
|
|
|
21
23
|
const LANGUAGES = {
|
|
22
24
|
js: "JavaScript",
|
|
@@ -46,7 +48,7 @@ async function handleRouteGeneration(controller, flagRoutes) {
|
|
|
46
48
|
const needsRouteGeneration = routesToScaffold === "all" || routesToScaffold.length > 0;
|
|
47
49
|
return {
|
|
48
50
|
needsRouteGeneration,
|
|
49
|
-
setupRoutes: async (directory, language,
|
|
51
|
+
setupRoutes: async (directory, language, options) => {
|
|
50
52
|
if (needsRouteGeneration) {
|
|
51
53
|
const result = await generateRoutes(
|
|
52
54
|
{
|
|
@@ -54,8 +56,9 @@ async function handleRouteGeneration(controller, flagRoutes) {
|
|
|
54
56
|
directory,
|
|
55
57
|
force: true,
|
|
56
58
|
typescript: language === "ts",
|
|
57
|
-
localePrefix: i18nStrategy === "subfolders" ? "locale" : false,
|
|
58
|
-
signal: controller.signal
|
|
59
|
+
localePrefix: options?.i18nStrategy === "subfolders" ? "locale" : false,
|
|
60
|
+
signal: controller.signal,
|
|
61
|
+
...options
|
|
59
62
|
},
|
|
60
63
|
{
|
|
61
64
|
rootDirectory: directory,
|
|
@@ -75,8 +78,7 @@ function generateProjectEntries(options) {
|
|
|
75
78
|
);
|
|
76
79
|
}
|
|
77
80
|
async function handleCliShortcut(controller, cliCommand, flagShortcut) {
|
|
78
|
-
if (cliCommand === ALIAS_NAME)
|
|
79
|
-
return {};
|
|
81
|
+
if (cliCommand === ALIAS_NAME) return {};
|
|
80
82
|
const shouldCreateShortcut = flagShortcut ?? await renderConfirmationPrompt({
|
|
81
83
|
confirmationMessage: "Yes",
|
|
82
84
|
cancellationMessage: "No",
|
|
@@ -89,8 +91,7 @@ async function handleCliShortcut(controller, cliCommand, flagShortcut) {
|
|
|
89
91
|
],
|
|
90
92
|
abortSignal: controller.signal
|
|
91
93
|
});
|
|
92
|
-
if (!shouldCreateShortcut)
|
|
93
|
-
return {};
|
|
94
|
+
if (!shouldCreateShortcut) return {};
|
|
94
95
|
return {
|
|
95
96
|
createShortcut: async () => {
|
|
96
97
|
try {
|
|
@@ -223,7 +224,33 @@ async function handleLanguage(projectDir, controller, flagLanguage) {
|
|
|
223
224
|
};
|
|
224
225
|
}
|
|
225
226
|
async function handleCssStrategy(projectDir, controller, flagStyling) {
|
|
226
|
-
|
|
227
|
+
const selection = flagStyling ?? await renderCssPrompt({
|
|
228
|
+
abortSignal: controller.signal,
|
|
229
|
+
extraChoices: { none: "Skip and set up later" }
|
|
230
|
+
});
|
|
231
|
+
const cssStrategy = selection === "none" ? void 0 : selection;
|
|
232
|
+
return {
|
|
233
|
+
cssStrategy,
|
|
234
|
+
async setupCss() {
|
|
235
|
+
if (cssStrategy) {
|
|
236
|
+
if (cssStrategy === "postcss" || cssStrategy === "css-modules") {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const result = await setupCssStrategy(
|
|
240
|
+
cssStrategy,
|
|
241
|
+
{
|
|
242
|
+
rootDirectory: projectDir,
|
|
243
|
+
appDirectory: joinPath(projectDir, "app")
|
|
244
|
+
// Default value in new projects
|
|
245
|
+
},
|
|
246
|
+
true
|
|
247
|
+
);
|
|
248
|
+
if (result) {
|
|
249
|
+
await result.workPromise;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
227
254
|
}
|
|
228
255
|
async function handleDependencies(projectDir, controller, packageManagerFromFlag, shouldInstallDeps) {
|
|
229
256
|
const detectedPackageManager = packageManagerFromFlag ?? packageManagerFromUserAgent();
|
|
@@ -257,6 +284,20 @@ async function handleDependencies(projectDir, controller, packageManagerFromFlag
|
|
|
257
284
|
});
|
|
258
285
|
}
|
|
259
286
|
}
|
|
287
|
+
if (isHydrogenMonorepo) {
|
|
288
|
+
await copyFile(
|
|
289
|
+
joinPath(getSkeletonSourceDir(), ".npmrc"),
|
|
290
|
+
joinPath(projectDir, ".npmrc")
|
|
291
|
+
).catch(() => {
|
|
292
|
+
});
|
|
293
|
+
if (!shouldInstallDeps) {
|
|
294
|
+
await symlink(
|
|
295
|
+
await getRepoNodeModules(),
|
|
296
|
+
joinPath(projectDir, "node_modules")
|
|
297
|
+
).catch(() => {
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
260
301
|
return {
|
|
261
302
|
packageManager: actualPackageManager,
|
|
262
303
|
shouldInstallDeps,
|
|
@@ -292,7 +333,7 @@ async function createInitialCommit(directory) {
|
|
|
292
333
|
return commitAll(directory, "Scaffold Storefront");
|
|
293
334
|
} catch (error) {
|
|
294
335
|
outputDebug(
|
|
295
|
-
"Failed to initialize Git.\n" + error?.stack
|
|
336
|
+
"Failed to initialize Git.\n" + (error?.stack ?? error?.message ?? error)
|
|
296
337
|
);
|
|
297
338
|
}
|
|
298
339
|
}
|
|
@@ -302,7 +343,7 @@ async function commitAll(directory, message) {
|
|
|
302
343
|
await createGitCommit(message, { directory });
|
|
303
344
|
} catch (error) {
|
|
304
345
|
outputDebug(
|
|
305
|
-
"Failed to commit code.\n" + error?.stack
|
|
346
|
+
"Failed to commit code.\n" + (error?.stack ?? error?.message ?? error)
|
|
306
347
|
);
|
|
307
348
|
}
|
|
308
349
|
}
|
|
@@ -395,7 +436,7 @@ async function renderProjectReady(project, {
|
|
|
395
436
|
command: [
|
|
396
437
|
project.directory === process.cwd() ? void 0 : `cd ${project.location.replace(/^\.\//, "")}`,
|
|
397
438
|
depsInstalled ? void 0 : `${packageManager} install`,
|
|
398
|
-
formatPackageManagerCommand(packageManager, "dev")
|
|
439
|
+
currentProcessIsGlobal() ? "npm run dev" : formatPackageManagerCommand(packageManager, "dev")
|
|
399
440
|
].filter(Boolean).join(" && ")
|
|
400
441
|
}
|
|
401
442
|
]
|
|
@@ -417,10 +458,14 @@ function createAbortHandler(controller, project) {
|
|
|
417
458
|
}
|
|
418
459
|
renderFatalError(
|
|
419
460
|
new AbortError(
|
|
420
|
-
"Failed to initialize project: " + error?.message,
|
|
461
|
+
"Failed to initialize project: " + (error?.message ?? ""),
|
|
421
462
|
error?.tryMessage ?? error?.stack
|
|
422
463
|
)
|
|
423
464
|
);
|
|
465
|
+
if (process.env.SHOPIFY_UNIT_TEST && process.exit.name !== "spy") {
|
|
466
|
+
console.error("Error during test before process.exit:", error);
|
|
467
|
+
throw error;
|
|
468
|
+
}
|
|
424
469
|
process.exit(1);
|
|
425
470
|
};
|
|
426
471
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cp } from 'node:fs/promises';
|
|
2
2
|
import { writeFile } from '@shopify/cli-kit/node/fs';
|
|
3
3
|
import { relativePath, joinPath } from '@shopify/cli-kit/node/path';
|
|
4
4
|
import { hyphenate } from '@shopify/cli-kit/common/string';
|
|
@@ -32,22 +32,21 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
32
32
|
storefrontInfo,
|
|
33
33
|
controller
|
|
34
34
|
});
|
|
35
|
-
if (!project)
|
|
36
|
-
|
|
37
|
-
if (templateAction === "mock")
|
|
38
|
-
project.storefrontTitle = "Mock.shop";
|
|
35
|
+
if (!project) return;
|
|
36
|
+
if (templateAction === "mock") project.storefrontTitle = "Mock.shop";
|
|
39
37
|
const abort = createAbortHandler(controller, project);
|
|
40
38
|
const createStorefrontPromise = storefrontInfo && !storefrontInfo.id && createStorefront(storefrontInfo.session, storefrontInfo.title).then(async ({ storefront, jobId }) => {
|
|
41
|
-
if (jobId)
|
|
42
|
-
await waitForJob(storefrontInfo.session, jobId);
|
|
39
|
+
if (jobId) await waitForJob(storefrontInfo.session, jobId);
|
|
43
40
|
return storefront;
|
|
44
41
|
}).catch(abort);
|
|
45
42
|
const templateDir = await getStarterDir();
|
|
46
|
-
let backgroundWorkPromise =
|
|
43
|
+
let backgroundWorkPromise = cp(
|
|
47
44
|
templateDir,
|
|
48
45
|
project.directory,
|
|
49
46
|
// Filter out the `app` directory and server.ts, which will be generated later
|
|
50
47
|
{
|
|
48
|
+
force: true,
|
|
49
|
+
recursive: true,
|
|
51
50
|
filter: (filepath) => !/^(app\/|dist\/|node_modules\/|server\.ts)/i.test(
|
|
52
51
|
relativePath(templateDir, filepath)
|
|
53
52
|
)
|
|
@@ -120,10 +119,7 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
120
119
|
// Set required env vars
|
|
121
120
|
writeFile(
|
|
122
121
|
joinPath(project.directory, ".env"),
|
|
123
|
-
envLeadingComment + "\n" + [
|
|
124
|
-
["SESSION_SECRET", "foobar"],
|
|
125
|
-
["PUBLIC_STORE_DOMAIN", "mock.shop"]
|
|
126
|
-
].map(([key, value]) => `${key}="${value}"`).join("\n") + "\n"
|
|
122
|
+
envLeadingComment + "\n" + [["SESSION_SECRET", "foobar"]].map(([key, value]) => `${key}="${value}"`).join("\n") + "\n"
|
|
127
123
|
)
|
|
128
124
|
);
|
|
129
125
|
}
|
|
@@ -236,7 +232,12 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
236
232
|
).catch((error) => {
|
|
237
233
|
setupSummary.i18nError = error;
|
|
238
234
|
});
|
|
239
|
-
await setupRoutes(project.directory, language,
|
|
235
|
+
await setupRoutes(project.directory, language, {
|
|
236
|
+
i18nStrategy,
|
|
237
|
+
// The init process might have added and modified files. Do not overwrite them.
|
|
238
|
+
// E.g. CSS imports might have been added to the root.
|
|
239
|
+
overwriteFileDeps: false
|
|
240
|
+
}).then((routes) => {
|
|
240
241
|
setupSummary.routes = routes;
|
|
241
242
|
if (options.git && routes) {
|
|
242
243
|
return commitAll(
|
|
@@ -7,6 +7,7 @@ import { renderTasks, renderInfo } from '@shopify/cli-kit/node/ui';
|
|
|
7
7
|
import { downloadExternalRepo, downloadMonorepoTemplates } from '../template-downloader.js';
|
|
8
8
|
import { applyTemplateDiff } from '../template-diff.js';
|
|
9
9
|
import { getCliCommand } from '../shell.js';
|
|
10
|
+
import { replaceFileContent } from '../file.js';
|
|
10
11
|
import { createAbortHandler, handleProjectLocation, handleLanguage, createInitialCommit, handleDependencies, commitAll, renderProjectReady } from './common.js';
|
|
11
12
|
|
|
12
13
|
const DEMO_STORE_REPO = "shopify/hydrogen-demo-store";
|
|
@@ -15,15 +16,12 @@ async function setupRemoteTemplate(options, controller) {
|
|
|
15
16
|
let abort = createAbortHandler(controller);
|
|
16
17
|
const backgroundDownloadPromise = appTemplate.includes("/") ? getExternalTemplate(appTemplate, controller.signal).catch(abort) : getMonorepoTemplate(appTemplate, controller.signal).catch(abort);
|
|
17
18
|
const project = await handleProjectLocation({ ...options, controller });
|
|
18
|
-
if (!project)
|
|
19
|
-
return;
|
|
19
|
+
if (!project) return;
|
|
20
20
|
abort = createAbortHandler(controller, project);
|
|
21
21
|
const downloaded = await backgroundDownloadPromise;
|
|
22
|
-
if (controller.signal.aborted)
|
|
23
|
-
return;
|
|
22
|
+
if (controller.signal.aborted) return;
|
|
24
23
|
let backgroundWorkPromise = Promise.resolve().then(async () => {
|
|
25
|
-
if (controller.signal.aborted)
|
|
26
|
-
return;
|
|
24
|
+
if (controller.signal.aborted) return;
|
|
27
25
|
const { sourcePath, skeletonPath } = downloaded;
|
|
28
26
|
const pkgJson = await readAndParsePackageJson(
|
|
29
27
|
joinPath(sourcePath, "package.json")
|
|
@@ -31,7 +29,17 @@ async function setupRemoteTemplate(options, controller) {
|
|
|
31
29
|
if (pkgJson.scripts?.dev?.includes("--diff")) {
|
|
32
30
|
return applyTemplateDiff(project.directory, sourcePath, skeletonPath);
|
|
33
31
|
}
|
|
34
|
-
|
|
32
|
+
await copyFile(sourcePath, project.directory);
|
|
33
|
+
await replaceFileContent(
|
|
34
|
+
joinPath(project.directory, "package.json"),
|
|
35
|
+
false,
|
|
36
|
+
(content) => (
|
|
37
|
+
// Remove the cli plugin dependency from the package.json because it's
|
|
38
|
+
// only used for monorepo development. This line is present in non-diff
|
|
39
|
+
// examples like `express` when scaffolding a new project:
|
|
40
|
+
content.replace(/^\s*"@shopify\/cli-hydrogen": "[^"]+",?\n/m, "")
|
|
41
|
+
)
|
|
42
|
+
);
|
|
35
43
|
}).catch(abort);
|
|
36
44
|
const supportsTranspilation = await fileExists(
|
|
37
45
|
joinPath(downloaded.sourcePath, "tsconfig.json")
|
|
@@ -79,8 +87,7 @@ async function setupRemoteTemplate(options, controller) {
|
|
|
79
87
|
}
|
|
80
88
|
});
|
|
81
89
|
}
|
|
82
|
-
if (controller.signal.aborted)
|
|
83
|
-
return;
|
|
90
|
+
if (controller.signal.aborted) return;
|
|
84
91
|
await renderTasks(tasks);
|
|
85
92
|
if (options.git) {
|
|
86
93
|
await commitAll(project.directory, "Lockfile");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { rm, symlink } from 'node:fs/promises';
|
|
1
2
|
import { vi } from 'vitest';
|
|
2
|
-
import { remove, createSymlink } from 'fs-extra/esm';
|
|
3
3
|
import { writeFile } from '@shopify/cli-kit/node/fs';
|
|
4
4
|
import { dirname, joinPath } from '@shopify/cli-kit/node/path';
|
|
5
5
|
import { getSkeletonSourceDir, getRepoNodeModules } from '../build.js';
|
|
@@ -43,9 +43,12 @@ vi.mock(
|
|
|
43
43
|
renderTasksHook.mockImplementationOnce(async () => {
|
|
44
44
|
await writeFile(`${directory}/package-lock.json`, "{}");
|
|
45
45
|
});
|
|
46
|
-
await
|
|
46
|
+
await rm(joinPath(directory, "node_modules"), {
|
|
47
|
+
force: true,
|
|
48
|
+
recursive: true
|
|
49
|
+
}).catch(() => {
|
|
47
50
|
});
|
|
48
|
-
await
|
|
51
|
+
await symlink(
|
|
49
52
|
await getRepoNodeModules(),
|
|
50
53
|
joinPath(directory, "node_modules")
|
|
51
54
|
);
|
package/dist/lib/remix-config.js
CHANGED
|
@@ -42,8 +42,7 @@ function handleRemixImportFail() {
|
|
|
42
42
|
}
|
|
43
43
|
function getRawRemixConfig(root) {
|
|
44
44
|
return findFileWithExtension(root, "remix.config").then(({ filepath }) => {
|
|
45
|
-
if (!filepath)
|
|
46
|
-
throw new AbortError("No remix.config.js file found.");
|
|
45
|
+
if (!filepath) throw new AbortError("No remix.config.js file found.");
|
|
47
46
|
return createRequire(import.meta.url)(filepath);
|
|
48
47
|
});
|
|
49
48
|
}
|
|
@@ -152,8 +151,7 @@ async function assertEntryFileExists(root, fileRelative) {
|
|
|
152
151
|
const { name, ext } = path.parse(file);
|
|
153
152
|
return name === path.basename(fileAbsolute) && /^\.[jt]s$/.test(ext);
|
|
154
153
|
});
|
|
155
|
-
if (exists2)
|
|
156
|
-
return;
|
|
154
|
+
if (exists2) return;
|
|
157
155
|
}
|
|
158
156
|
throw new AbortError(
|
|
159
157
|
`Entry file "${fileRelative}" not found.`,
|
|
@@ -17,8 +17,7 @@ function checkRemixVersions(projectPath, requiredVersionInHydrogen = REQUIRED_RE
|
|
|
17
17
|
const outOfSyncPkgs = pkgs.filter(
|
|
18
18
|
(pkg) => pkg.version && !satisfiesSemver(pkg.version, requiredVersionInHydrogen)
|
|
19
19
|
);
|
|
20
|
-
if (outOfSyncPkgs.length === 0)
|
|
21
|
-
return;
|
|
20
|
+
if (outOfSyncPkgs.length === 0) return;
|
|
22
21
|
const items = outOfSyncPkgs.reduce((acc, item) => {
|
|
23
22
|
if (item.version) {
|
|
24
23
|
acc.push(`${item.name}@${item.version}`);
|
|
@@ -95,8 +95,7 @@ function createLogRequestEvent(options) {
|
|
|
95
95
|
})
|
|
96
96
|
};
|
|
97
97
|
eventHistory.push(event);
|
|
98
|
-
if (eventHistory.length > 100)
|
|
99
|
-
eventHistory.shift();
|
|
98
|
+
if (eventHistory.length > 100) eventHistory.shift();
|
|
100
99
|
eventEmitter.emit("request", event);
|
|
101
100
|
return createResponse();
|
|
102
101
|
};
|
|
@@ -116,16 +115,14 @@ function streamRequestEvents(request) {
|
|
|
116
115
|
eventEmitter.addListener("request", enqueueEvent);
|
|
117
116
|
let closed = false;
|
|
118
117
|
function close() {
|
|
119
|
-
if (closed)
|
|
120
|
-
return;
|
|
118
|
+
if (closed) return;
|
|
121
119
|
closed = true;
|
|
122
120
|
request.signal.removeEventListener("abort", close);
|
|
123
121
|
eventEmitter.removeListener("request", enqueueEvent);
|
|
124
122
|
controller.close();
|
|
125
123
|
}
|
|
126
124
|
request.signal.addEventListener("abort", close);
|
|
127
|
-
if (request.signal.aborted)
|
|
128
|
-
return close();
|
|
125
|
+
if (request.signal.aborted) return close();
|
|
129
126
|
}
|
|
130
127
|
});
|
|
131
128
|
return createResponse(stream, {
|
|
@@ -5,8 +5,8 @@ import { getAssetsDir } from '../../build.js';
|
|
|
5
5
|
|
|
6
6
|
const SETUP_CSS_STRATEGIES = [
|
|
7
7
|
"tailwind",
|
|
8
|
-
"css-modules",
|
|
9
8
|
"vanilla-extract",
|
|
9
|
+
"css-modules",
|
|
10
10
|
"postcss"
|
|
11
11
|
];
|
|
12
12
|
async function copyAssets(feature, assets, rootDirectory, replacer = (content, filename) => content) {
|
|
@@ -2,27 +2,34 @@ import { renderSelectPrompt } from '@shopify/cli-kit/node/ui';
|
|
|
2
2
|
import { SETUP_CSS_STRATEGIES } from './assets.js';
|
|
3
3
|
export { SETUP_CSS_STRATEGIES } from './assets.js';
|
|
4
4
|
import { setupTailwind } from './tailwind.js';
|
|
5
|
-
import { setupPostCss } from './postcss.js';
|
|
6
|
-
import { setupCssModules } from './css-modules.js';
|
|
7
5
|
import { setupVanillaExtract } from './vanilla-extract.js';
|
|
8
6
|
|
|
9
7
|
const STYLING_CHOICES = [...SETUP_CSS_STRATEGIES, "none"];
|
|
10
8
|
const CSS_STRATEGY_NAME_MAP = {
|
|
11
|
-
tailwind: "Tailwind",
|
|
12
|
-
"css-modules": "CSS Modules",
|
|
9
|
+
tailwind: "Tailwind (v4 alpha)",
|
|
13
10
|
"vanilla-extract": "Vanilla Extract",
|
|
14
|
-
|
|
11
|
+
"css-modules": "CSS Modules",
|
|
12
|
+
postcss: "PostCSS"
|
|
13
|
+
};
|
|
14
|
+
const CSS_STRATEGY_HELP_URL_MAP = {
|
|
15
|
+
postcss: "https://vitejs.dev/guide/features.html#postcss",
|
|
16
|
+
"css-modules": "https://vitejs.dev/guide/features.html#css-modules",
|
|
17
|
+
"vanilla-extract": "https://vanilla-extract.style/documentation/styling/",
|
|
18
|
+
tailwind: "https://tailwindcss.com/docs/configuration"
|
|
15
19
|
};
|
|
16
20
|
function setupCssStrategy(strategy, options, force) {
|
|
17
21
|
switch (strategy) {
|
|
18
22
|
case "tailwind":
|
|
19
23
|
return setupTailwind(options, force);
|
|
20
|
-
case "postcss":
|
|
21
|
-
return setupPostCss(options, force);
|
|
22
|
-
case "css-modules":
|
|
23
|
-
return setupCssModules(options);
|
|
24
24
|
case "vanilla-extract":
|
|
25
25
|
return setupVanillaExtract(options);
|
|
26
|
+
case "postcss":
|
|
27
|
+
case "css-modules":
|
|
28
|
+
return {
|
|
29
|
+
workPromise: Promise.resolve(),
|
|
30
|
+
generatedAssets: [],
|
|
31
|
+
needsInstallDeps: false
|
|
32
|
+
};
|
|
26
33
|
default:
|
|
27
34
|
throw new Error("Unknown strategy");
|
|
28
35
|
}
|
|
@@ -43,4 +50,4 @@ async function renderCssPrompt(options) {
|
|
|
43
50
|
});
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
export { CSS_STRATEGY_NAME_MAP, STYLING_CHOICES, renderCssPrompt, setupCssStrategy };
|
|
53
|
+
export { CSS_STRATEGY_HELP_URL_MAP, CSS_STRATEGY_NAME_MAP, STYLING_CHOICES, renderCssPrompt, setupCssStrategy };
|