@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
package/dist/hooks/init.js
CHANGED
|
@@ -2,11 +2,11 @@ import { spawnSync } from 'child_process';
|
|
|
2
2
|
import { outputDebug } from '@shopify/cli-kit/node/output';
|
|
3
3
|
|
|
4
4
|
const EXPERIMENTAL_VM_MODULES_FLAG = "--experimental-vm-modules";
|
|
5
|
+
function commandNeedsVM(id = "", argv = []) {
|
|
6
|
+
return "hydrogen:debug:cpu";
|
|
7
|
+
}
|
|
5
8
|
const hook = async function(options) {
|
|
6
|
-
if (options.id &&
|
|
7
|
-
["hydrogen:dev", "hydrogen:preview", "hydrogen:debug:cpu"].includes(
|
|
8
|
-
options.id
|
|
9
|
-
) && !process.execArgv.includes(EXPERIMENTAL_VM_MODULES_FLAG) && !(process.env.NODE_OPTIONS ?? "").includes(EXPERIMENTAL_VM_MODULES_FLAG)) {
|
|
9
|
+
if (commandNeedsVM(options.id, options.argv) && !process.execArgv.includes(EXPERIMENTAL_VM_MODULES_FLAG) && !(process.env.NODE_OPTIONS ?? "").includes(EXPERIMENTAL_VM_MODULES_FLAG)) {
|
|
10
10
|
outputDebug(
|
|
11
11
|
`Restarting CLI process with ${EXPERIMENTAL_VM_MODULES_FLAG} flag.`
|
|
12
12
|
);
|
package/dist/lib/auth.js
CHANGED
|
@@ -8,6 +8,7 @@ import ansiEscapes from 'ansi-escapes';
|
|
|
8
8
|
import { resetConfig, getConfig, setUserAccount } from './shopify-config.js';
|
|
9
9
|
import { getUserAccount } from './graphql/business-platform/user-account.js';
|
|
10
10
|
import { muteAuthLogs } from './log.js';
|
|
11
|
+
import { deferPromise } from './defer.js';
|
|
11
12
|
|
|
12
13
|
async function logout(root) {
|
|
13
14
|
await logout$1();
|
|
@@ -24,14 +25,11 @@ async function login(root, shop) {
|
|
|
24
25
|
shop = await normalizeStoreFqdn(shop);
|
|
25
26
|
const hideLoginInfo = showLoginInfo();
|
|
26
27
|
if (!shop || !shopName || !email || forcePrompt || shop !== existingConfig.shop) {
|
|
27
|
-
const dummyTimeout = setTimeout(() => {
|
|
28
|
-
}, 6e5);
|
|
29
28
|
const token = await ensureAuthenticatedBusinessPlatform().catch(() => {
|
|
30
29
|
throw new AbortError(
|
|
31
30
|
"Unable to authenticate with Shopify. Please report this issue."
|
|
32
31
|
);
|
|
33
32
|
});
|
|
34
|
-
clearTimeout(dummyTimeout);
|
|
35
33
|
const userAccount = await getUserAccount(token);
|
|
36
34
|
await hideLoginInfo();
|
|
37
35
|
const preselected = !forcePrompt && shop && userAccount.activeShops.find(({ fqdn }) => shop === fqdn);
|
|
@@ -56,10 +54,7 @@ async function login(root, shop) {
|
|
|
56
54
|
return { session, config };
|
|
57
55
|
}
|
|
58
56
|
function showLoginInfo() {
|
|
59
|
-
|
|
60
|
-
const promise = new Promise((resolve) => {
|
|
61
|
-
deferredResolve = resolve;
|
|
62
|
-
});
|
|
57
|
+
const deferred = deferPromise();
|
|
63
58
|
console.log("");
|
|
64
59
|
let hasLoggedTimeout = false;
|
|
65
60
|
let hasLoggedPressKey = false;
|
|
@@ -94,21 +89,21 @@ function showLoginInfo() {
|
|
|
94
89
|
{
|
|
95
90
|
title: "Waiting for Shopify authentication",
|
|
96
91
|
task: async () => {
|
|
97
|
-
await promise;
|
|
92
|
+
await deferred.promise;
|
|
98
93
|
}
|
|
99
94
|
}
|
|
100
95
|
]);
|
|
101
96
|
});
|
|
102
97
|
}
|
|
103
98
|
});
|
|
104
|
-
promise.then(() => {
|
|
99
|
+
deferred.promise.then(() => {
|
|
105
100
|
restoreLogs();
|
|
106
101
|
if (hasLoggedPressKey) {
|
|
107
102
|
process.stdout.write(ansiEscapes.eraseLines(hasLoggedTimeout ? 11 : 10));
|
|
108
103
|
}
|
|
109
104
|
});
|
|
110
105
|
return async () => {
|
|
111
|
-
|
|
106
|
+
deferred.resolve();
|
|
112
107
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
113
108
|
};
|
|
114
109
|
}
|
package/dist/lib/build.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { execAsync } from './process.js';
|
|
2
3
|
|
|
3
4
|
const GENERATOR_TEMPLATES_DIR = "generator-templates";
|
|
4
5
|
const GENERATOR_STARTER_DIR = "starter";
|
|
@@ -47,5 +48,9 @@ function getSkeletonSourceDir() {
|
|
|
47
48
|
new URL(`../../../../templates/skeleton`, import.meta.url)
|
|
48
49
|
);
|
|
49
50
|
}
|
|
51
|
+
async function getRepoNodeModules() {
|
|
52
|
+
const { stdout } = await execAsync("npm root");
|
|
53
|
+
return stdout.trim() || fileURLToPath(new URL(`../../../../node_modules`, import.meta.url));
|
|
54
|
+
}
|
|
50
55
|
|
|
51
|
-
export { GENERATOR_APP_DIR, GENERATOR_ROUTE_DIR, GENERATOR_SETUP_ASSETS_DIR, GENERATOR_SETUP_ASSETS_SUB_DIRS, GENERATOR_STARTER_DIR, GENERATOR_TEMPLATES_DIR, getAssetDir, getSkeletonSourceDir, getStarterDir, getTemplateAppFile };
|
|
56
|
+
export { GENERATOR_APP_DIR, GENERATOR_ROUTE_DIR, GENERATOR_SETUP_ASSETS_DIR, GENERATOR_SETUP_ASSETS_SUB_DIRS, GENERATOR_STARTER_DIR, GENERATOR_TEMPLATES_DIR, getAssetDir, getRepoNodeModules, getSkeletonSourceDir, getStarterDir, getTemplateAppFile };
|
|
@@ -2,30 +2,43 @@ import { joinPath, dirname } from '@shopify/cli-kit/node/path';
|
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { fileExists, readFile, writeFile } from '@shopify/cli-kit/node/fs';
|
|
4
4
|
import colors from '@shopify/cli-kit/node/colors';
|
|
5
|
+
import { renderWarning } from '@shopify/cli-kit/node/ui';
|
|
5
6
|
|
|
6
|
-
async function hasMetafile(buildPath) {
|
|
7
|
-
return (await Promise.all([
|
|
8
|
-
fileExists(joinPath(buildPath, "worker", "metafile.server.json")),
|
|
9
|
-
fileExists(joinPath(buildPath, "worker", "metafile.js.json"))
|
|
10
|
-
])).every(Boolean);
|
|
11
|
-
}
|
|
12
7
|
async function buildBundleAnalysis(buildPath) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
8
|
+
const workerBuildPath = joinPath(buildPath, "worker");
|
|
9
|
+
const serverMetafile = "metafile.server.json";
|
|
10
|
+
const clientMetafile = "metafile.js.json";
|
|
11
|
+
const hasMetafile = (await Promise.all([
|
|
12
|
+
fileExists(joinPath(workerBuildPath, serverMetafile)),
|
|
13
|
+
fileExists(joinPath(workerBuildPath, clientMetafile))
|
|
14
|
+
])).every(Boolean);
|
|
15
|
+
if (!hasMetafile)
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
await Promise.all([
|
|
19
|
+
writeBundleAnalyzerFile(
|
|
20
|
+
workerBuildPath,
|
|
21
|
+
serverMetafile,
|
|
22
|
+
"worker-bundle-analyzer.html"
|
|
23
|
+
),
|
|
24
|
+
writeBundleAnalyzerFile(
|
|
25
|
+
workerBuildPath,
|
|
26
|
+
clientMetafile,
|
|
27
|
+
"client-bundle-analyzer.html"
|
|
28
|
+
)
|
|
29
|
+
]);
|
|
30
|
+
return "file://" + joinPath(workerBuildPath, "worker-bundle-analyzer.html");
|
|
31
|
+
} catch (thrown) {
|
|
32
|
+
const error = thrown;
|
|
33
|
+
renderWarning({
|
|
34
|
+
headline: "Could not generate bundle analysis",
|
|
35
|
+
body: error?.stack ?? error?.message ?? error
|
|
36
|
+
});
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
26
39
|
}
|
|
27
|
-
async function writeBundleAnalyzerFile(
|
|
28
|
-
const metafile = await readFile(joinPath(
|
|
40
|
+
async function writeBundleAnalyzerFile(workerBuildPath, metafileName, outputFile) {
|
|
41
|
+
const metafile = await readFile(joinPath(workerBuildPath, metafileName), {
|
|
29
42
|
encoding: "utf8"
|
|
30
43
|
});
|
|
31
44
|
const metafile64 = Buffer.from(metafile, "utf-8").toString("base64");
|
|
@@ -38,10 +51,7 @@ async function writeBundleAnalyzerFile(buildPath, metafileName, outputFile) {
|
|
|
38
51
|
`globalThis.METAFILE = '';`,
|
|
39
52
|
`globalThis.METAFILE = '${metafile64}';`
|
|
40
53
|
);
|
|
41
|
-
await writeFile(
|
|
42
|
-
joinPath(buildPath, "worker", outputFile),
|
|
43
|
-
templateWithMetafile
|
|
44
|
-
);
|
|
54
|
+
await writeFile(joinPath(workerBuildPath, outputFile), templateWithMetafile);
|
|
45
55
|
}
|
|
46
56
|
async function getBundleAnalysisSummary(bundlePath) {
|
|
47
57
|
const esbuild = await import('esbuild').catch(() => {
|
|
@@ -59,4 +69,4 @@ async function getBundleAnalysisSummary(bundlePath) {
|
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
71
|
|
|
62
|
-
export { buildBundleAnalysis, getBundleAnalysisSummary
|
|
72
|
+
export { buildBundleAnalysis, getBundleAnalysisSummary };
|
package/dist/lib/codegen.js
CHANGED
|
@@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { getCodeFormatOptions, formatCode } from './format-code.js';
|
|
4
4
|
import { renderWarning, renderFatalError } from '@shopify/cli-kit/node/ui';
|
|
5
|
-
import { relativePath, joinPath } from '@shopify/cli-kit/node/path';
|
|
5
|
+
import { relativePath, joinPath, basename } from '@shopify/cli-kit/node/path';
|
|
6
6
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
7
7
|
|
|
8
8
|
const nodePath = process.argv[1];
|
|
@@ -55,6 +55,7 @@ function spawnCodegenProcess({
|
|
|
55
55
|
type: 0,
|
|
56
56
|
name: "CodegenError",
|
|
57
57
|
message: `Codegen process exited with code ${code}`,
|
|
58
|
+
skipOclifErrorHandling: true,
|
|
58
59
|
tryMessage: "Try restarting the dev server."
|
|
59
60
|
});
|
|
60
61
|
process.exit(code);
|
|
@@ -109,12 +110,35 @@ async function generateTypes({
|
|
|
109
110
|
});
|
|
110
111
|
codegenContext.cwd = dirs.rootDirectory;
|
|
111
112
|
await generate(codegenContext, true);
|
|
112
|
-
return Object.
|
|
113
|
+
return Object.entries(codegenConfig.generates).reduce((acc, [key, value]) => {
|
|
114
|
+
if ("documents" in value) {
|
|
115
|
+
acc[key] = (Array.isArray(value.documents) ? value.documents : [value.documents]).filter((document) => typeof document === "string");
|
|
116
|
+
}
|
|
117
|
+
return acc;
|
|
118
|
+
}, {});
|
|
113
119
|
}
|
|
114
120
|
async function generateDefaultConfig({ rootDirectory, appDirectory }, forceSfapiVersion) {
|
|
115
|
-
const {
|
|
121
|
+
const { getSchema, preset, pluckConfig } = await import('@shopify/hydrogen-codegen');
|
|
122
|
+
const { loadConfig } = await import('graphql-config');
|
|
123
|
+
const gqlConfig = await loadConfig({
|
|
124
|
+
rootDir: rootDirectory,
|
|
125
|
+
throwOnEmpty: false,
|
|
126
|
+
throwOnMissing: false,
|
|
127
|
+
legacy: false
|
|
128
|
+
}).catch(() => void 0);
|
|
129
|
+
const sfapiSchema = getSchema("storefront");
|
|
130
|
+
const sfapiProject = findGqlProject(sfapiSchema, gqlConfig);
|
|
116
131
|
const defaultGlob = "*!(*.d).{ts,tsx,js,jsx}";
|
|
117
132
|
const appDirRelative = relativePath(rootDirectory, appDirectory);
|
|
133
|
+
const caapiSchema = getSchema("customer-account");
|
|
134
|
+
const caapiProject = findGqlProject(caapiSchema, gqlConfig);
|
|
135
|
+
const customerAccountAPIConfig = caapiProject?.documents ? {
|
|
136
|
+
["customer-accountapi.generated.d.ts"]: {
|
|
137
|
+
preset,
|
|
138
|
+
schema: caapiSchema,
|
|
139
|
+
documents: caapiProject?.documents
|
|
140
|
+
}
|
|
141
|
+
} : void 0;
|
|
118
142
|
return {
|
|
119
143
|
filepath: "virtual:codegen",
|
|
120
144
|
config: {
|
|
@@ -123,8 +147,8 @@ async function generateDefaultConfig({ rootDirectory, appDirectory }, forceSfapi
|
|
|
123
147
|
generates: {
|
|
124
148
|
["storefrontapi.generated.d.ts"]: {
|
|
125
149
|
preset,
|
|
126
|
-
schema,
|
|
127
|
-
documents: [
|
|
150
|
+
schema: sfapiSchema,
|
|
151
|
+
documents: sfapiProject?.documents ?? [
|
|
128
152
|
defaultGlob,
|
|
129
153
|
// E.g. ./server.(t|j)s
|
|
130
154
|
joinPath(appDirRelative, "**", defaultGlob)
|
|
@@ -145,24 +169,41 @@ async function generateDefaultConfig({ rootDirectory, appDirectory }, forceSfapi
|
|
|
145
169
|
scalars: { JSON: "unknown" }
|
|
146
170
|
}
|
|
147
171
|
}
|
|
148
|
-
}
|
|
172
|
+
},
|
|
173
|
+
...customerAccountAPIConfig
|
|
149
174
|
}
|
|
150
175
|
}
|
|
151
176
|
};
|
|
152
177
|
}
|
|
178
|
+
function findGqlProject(schemaFilepath, gqlConfig) {
|
|
179
|
+
if (!gqlConfig)
|
|
180
|
+
return;
|
|
181
|
+
const schemaFilename = basename(schemaFilepath);
|
|
182
|
+
return Object.values(gqlConfig.projects || {}).find(
|
|
183
|
+
(project) => typeof project.schema === "string" && project.schema.endsWith(schemaFilename)
|
|
184
|
+
);
|
|
185
|
+
}
|
|
153
186
|
async function addHooksToHydrogenOptions(codegenConfig, { rootDirectory }) {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
187
|
+
const hydrogenProjectsOptions = Object.values(codegenConfig.generates).filter(
|
|
188
|
+
(value) => {
|
|
189
|
+
const foundPreset = (Array.isArray(value) ? value[0] : value)?.preset;
|
|
190
|
+
if (typeof foundPreset === "object") {
|
|
191
|
+
const name = Symbol.for("name");
|
|
192
|
+
if (name in foundPreset) {
|
|
193
|
+
return foundPreset[name] === "hydrogen";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
for (const options of hydrogenProjectsOptions) {
|
|
199
|
+
const hydrogenOptions = Array.isArray(options) ? options[0] : options;
|
|
200
|
+
if (hydrogenOptions) {
|
|
201
|
+
const formatConfig = await getCodeFormatOptions(rootDirectory);
|
|
202
|
+
hydrogenOptions.hooks = {
|
|
203
|
+
beforeOneFileWrite: (file, content) => formatCode(content, formatConfig, file),
|
|
204
|
+
...hydrogenOptions.hooks
|
|
205
|
+
};
|
|
206
|
+
}
|
|
166
207
|
}
|
|
167
208
|
}
|
|
168
209
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function deferPromise() {
|
|
2
|
+
let resolve = (value) => {
|
|
3
|
+
};
|
|
4
|
+
let reject = resolve;
|
|
5
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
6
|
+
resolve = _resolve;
|
|
7
|
+
reject = _reject;
|
|
8
|
+
});
|
|
9
|
+
return { promise, resolve, reject };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { deferPromise };
|
package/dist/lib/file.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import { resolvePath, joinPath } from '@shopify/cli-kit/node/path';
|
|
2
3
|
import { readFile, writeFile, isDirectory, fileExists } from '@shopify/cli-kit/node/fs';
|
|
3
|
-
import {
|
|
4
|
+
import { readAndParsePackageJson, writePackageJSON } from '@shopify/cli-kit/node/node-package-manager';
|
|
4
5
|
import { formatCode } from './format-code.js';
|
|
5
6
|
|
|
6
7
|
async function replaceFileContent(filepath, formatConfig, replacer) {
|
|
@@ -37,5 +38,53 @@ async function findFileWithExtension(directory, fileBase, extensions = DEFAULT_E
|
|
|
37
38
|
}
|
|
38
39
|
return {};
|
|
39
40
|
}
|
|
41
|
+
const MANAGED_PACKAGE_JSON_KEYS = Object.freeze([
|
|
42
|
+
"dependencies",
|
|
43
|
+
"devDependencies",
|
|
44
|
+
"peerDependencies"
|
|
45
|
+
]);
|
|
46
|
+
async function mergePackageJson(sourceDir, targetDir, options) {
|
|
47
|
+
const targetPkgJson = await readAndParsePackageJson(
|
|
48
|
+
joinPath(targetDir, "package.json")
|
|
49
|
+
);
|
|
50
|
+
const sourcePkgJson = await readAndParsePackageJson(
|
|
51
|
+
joinPath(sourceDir, "package.json")
|
|
52
|
+
);
|
|
53
|
+
const ignoredKeys = /* @__PURE__ */ new Set(["comment", ...options?.ignoredKeys ?? []]);
|
|
54
|
+
const unmanagedKeys = Object.keys(sourcePkgJson).filter(
|
|
55
|
+
(key) => !MANAGED_PACKAGE_JSON_KEYS.includes(key)
|
|
56
|
+
);
|
|
57
|
+
for (const key of unmanagedKeys) {
|
|
58
|
+
if (ignoredKeys.has(key))
|
|
59
|
+
continue;
|
|
60
|
+
const sourceValue = sourcePkgJson[key];
|
|
61
|
+
const targetValue = targetPkgJson[key];
|
|
62
|
+
const newValue = Array.isArray(sourceValue) && Array.isArray(targetValue) ? [...targetValue, ...sourceValue] : typeof sourceValue === "object" && typeof targetValue === "object" ? { ...targetValue, ...sourceValue } : sourceValue;
|
|
63
|
+
targetPkgJson[key] = newValue;
|
|
64
|
+
}
|
|
65
|
+
const remixVersion = Object.entries(targetPkgJson.dependencies || {}).find(
|
|
66
|
+
([dep]) => dep.startsWith("@remix-run/")
|
|
67
|
+
)?.[1];
|
|
68
|
+
for (const key of MANAGED_PACKAGE_JSON_KEYS) {
|
|
69
|
+
if (ignoredKeys.has(key))
|
|
70
|
+
continue;
|
|
71
|
+
if (sourcePkgJson[key]) {
|
|
72
|
+
targetPkgJson[key] = [
|
|
73
|
+
.../* @__PURE__ */ new Set([
|
|
74
|
+
...Object.keys(targetPkgJson[key] ?? {}),
|
|
75
|
+
...Object.keys(sourcePkgJson[key] ?? {})
|
|
76
|
+
])
|
|
77
|
+
].sort().reduce((acc, dep) => {
|
|
78
|
+
let version = sourcePkgJson[key]?.[dep] ?? targetPkgJson[key]?.[dep];
|
|
79
|
+
if (dep.startsWith("@remix-run/") && remixVersion) {
|
|
80
|
+
version = remixVersion;
|
|
81
|
+
}
|
|
82
|
+
acc[dep] = version;
|
|
83
|
+
return acc;
|
|
84
|
+
}, {});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await writePackageJSON(targetDir, targetPkgJson);
|
|
88
|
+
}
|
|
40
89
|
|
|
41
|
-
export { findFileWithExtension, replaceFileContent };
|
|
90
|
+
export { findFileWithExtension, mergePackageJson, replaceFileContent };
|
package/dist/lib/flags.js
CHANGED
|
@@ -5,6 +5,7 @@ import { normalizeStoreFqdn } from '@shopify/cli-kit/node/context/fqdn';
|
|
|
5
5
|
import colors from '@shopify/cli-kit/node/colors';
|
|
6
6
|
import { STYLING_CHOICES } from './setups/css/index.js';
|
|
7
7
|
import { I18N_CHOICES } from './setups/i18n/index.js';
|
|
8
|
+
import { DEFAULT_INSPECTOR_PORT } from './mini-oxygen/common.js';
|
|
8
9
|
|
|
9
10
|
const DEFAULT_PORT = 3e3;
|
|
10
11
|
const commonFlags = {
|
|
@@ -17,9 +18,9 @@ const commonFlags = {
|
|
|
17
18
|
env: "SHOPIFY_HYDROGEN_FLAG_PORT",
|
|
18
19
|
default: DEFAULT_PORT
|
|
19
20
|
}),
|
|
20
|
-
|
|
21
|
-
description: "Run the app in a
|
|
22
|
-
env: "
|
|
21
|
+
legacyRuntime: Flags.boolean({
|
|
22
|
+
description: "Run the app in a Node.js sandbox instead of an Oxygen worker.",
|
|
23
|
+
env: "SHOPIFY_HYDROGEN_FLAG_WORKER"
|
|
23
24
|
}),
|
|
24
25
|
force: Flags.boolean({
|
|
25
26
|
description: "Overwrite the destination directory and files if they already exist.",
|
|
@@ -51,9 +52,7 @@ const commonFlags = {
|
|
|
51
52
|
codegen: Flags.boolean({
|
|
52
53
|
description: "Generate types for the Storefront API queries found in your project.",
|
|
53
54
|
required: false,
|
|
54
|
-
default: false
|
|
55
|
-
deprecateAliases: true,
|
|
56
|
-
aliases: ["codegen-unstable"]
|
|
55
|
+
default: false
|
|
57
56
|
}),
|
|
58
57
|
codegenConfigPath: Flags.string({
|
|
59
58
|
description: "Specify a path to a codegen configuration file. Defaults to `<root>/codegen.ts` if it exists.",
|
|
@@ -78,6 +77,21 @@ const commonFlags = {
|
|
|
78
77
|
description: "Create a shortcut to the Shopify Hydrogen CLI.",
|
|
79
78
|
env: "SHOPIFY_HYDROGEN_FLAG_SHORTCUT",
|
|
80
79
|
allowNo: true
|
|
80
|
+
}),
|
|
81
|
+
debug: Flags.boolean({
|
|
82
|
+
description: "Enables inspector connections with a debugger.",
|
|
83
|
+
env: "SHOPIFY_HYDROGEN_FLAG_DEBUG",
|
|
84
|
+
default: false
|
|
85
|
+
}),
|
|
86
|
+
inspectorPort: Flags.integer({
|
|
87
|
+
description: "Port where the inspector will be available.",
|
|
88
|
+
env: "SHOPIFY_HYDROGEN_FLAG_INSPECTOR_PORT",
|
|
89
|
+
default: DEFAULT_INSPECTOR_PORT
|
|
90
|
+
}),
|
|
91
|
+
diff: Flags.boolean({
|
|
92
|
+
description: "Applies the current files on top of Hydrogen's starter template in a temporary directory.",
|
|
93
|
+
default: false,
|
|
94
|
+
required: false
|
|
81
95
|
})
|
|
82
96
|
};
|
|
83
97
|
function flagsToCamelObject(obj) {
|
|
@@ -104,18 +118,22 @@ function parseProcessFlags(processArgv, flagMap = {}) {
|
|
|
104
118
|
}
|
|
105
119
|
return flagsToCamelObject(options);
|
|
106
120
|
}
|
|
107
|
-
function deprecated(name) {
|
|
108
|
-
|
|
121
|
+
function deprecated(name, { isBoolean = false } = {}) {
|
|
122
|
+
const customFlag = Flags.custom({
|
|
109
123
|
parse: () => {
|
|
110
124
|
renderInfo({
|
|
111
125
|
headline: `The ${colors.bold(
|
|
112
126
|
name
|
|
113
|
-
)} flag is deprecated and will be removed in a future version of Shopify CLI.`
|
|
127
|
+
)} flag is deprecated and will be removed in a future version of Shopify Hydrogen CLI.`
|
|
114
128
|
});
|
|
115
129
|
return Promise.resolve(" ");
|
|
116
130
|
},
|
|
117
131
|
hidden: true
|
|
118
132
|
});
|
|
133
|
+
return {
|
|
134
|
+
...customFlag(),
|
|
135
|
+
type: isBoolean ? "boolean" : "option"
|
|
136
|
+
};
|
|
119
137
|
}
|
|
120
138
|
function overrideFlag(flag, extra) {
|
|
121
139
|
return {
|
|
@@ -25,11 +25,13 @@ describe("getOxygenDeploymentData", () => {
|
|
|
25
25
|
const environments = [
|
|
26
26
|
{
|
|
27
27
|
name: "production",
|
|
28
|
-
branch: "main"
|
|
28
|
+
branch: "main",
|
|
29
|
+
type: "PRODUCTION"
|
|
29
30
|
},
|
|
30
31
|
{
|
|
31
32
|
name: "preview",
|
|
32
|
-
branch:
|
|
33
|
+
branch: null,
|
|
34
|
+
type: "PREVIEW"
|
|
33
35
|
}
|
|
34
36
|
];
|
|
35
37
|
beforeEach(() => {
|
|
@@ -14,7 +14,7 @@ describe("adminRequest", () => {
|
|
|
14
14
|
token: "",
|
|
15
15
|
storeFqdn: ""
|
|
16
16
|
});
|
|
17
|
-
expect(response).
|
|
17
|
+
expect(response).toMatchObject(fakeResponse);
|
|
18
18
|
});
|
|
19
19
|
describe("when there is an unknown error response", () => {
|
|
20
20
|
it("passes along the error message", async () => {
|
|
@@ -30,7 +30,7 @@ describe("adminRequest", () => {
|
|
|
30
30
|
token: "",
|
|
31
31
|
storeFqdn: ""
|
|
32
32
|
});
|
|
33
|
-
await expect(response).rejects.
|
|
33
|
+
await expect(response).rejects.toMatchObject(fakeGraphqlError);
|
|
34
34
|
});
|
|
35
35
|
});
|
|
36
36
|
describe("when the app isn't installed", () => {
|
package/dist/lib/log.js
CHANGED
|
@@ -32,6 +32,10 @@ function debounceMessage(args, debounceFor) {
|
|
|
32
32
|
}
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
|
+
function warningDebouncer([first]) {
|
|
36
|
+
return typeof first === "string" && // Show these warnings only once.
|
|
37
|
+
(first.includes("[h2:warn:createStorefrontClient]") || first.includes("[h2:warn:createCustomerAccountClient]")) ? true : void 0;
|
|
38
|
+
}
|
|
35
39
|
function injectLogReplacer(method, debouncer) {
|
|
36
40
|
if (!methodsReplaced.has(method)) {
|
|
37
41
|
methodsReplaced.add(method);
|
|
@@ -57,7 +61,7 @@ function injectLogReplacer(method, debouncer) {
|
|
|
57
61
|
function muteDevLogs({ workerReload } = {}) {
|
|
58
62
|
injectLogReplacer("log");
|
|
59
63
|
injectLogReplacer("error");
|
|
60
|
-
injectLogReplacer("warn");
|
|
64
|
+
injectLogReplacer("warn", warningDebouncer);
|
|
61
65
|
let isFirstWorkerReload = true;
|
|
62
66
|
addMessageReplacers("dev-node", [
|
|
63
67
|
([first]) => typeof first === "string" && first.includes("[mf:"),
|
|
@@ -77,18 +81,30 @@ function muteDevLogs({ workerReload } = {}) {
|
|
|
77
81
|
]);
|
|
78
82
|
addMessageReplacers(
|
|
79
83
|
"dev-workerd",
|
|
84
|
+
// Workerd logs
|
|
85
|
+
[
|
|
86
|
+
([first]) => typeof first === "string" && /^\x1B\[31m(workerd\/|stack:( (0|[a-f\d]{4,})){3,})/.test(first),
|
|
87
|
+
() => {
|
|
88
|
+
}
|
|
89
|
+
],
|
|
80
90
|
[
|
|
81
|
-
|
|
82
|
-
([first]) => typeof first === "string" && /^\x1B\[31m(workerd\/|stack:)/.test(first),
|
|
91
|
+
([first]) => typeof first === "string" && /\$LLVM_SYMBOLIZER|recursive isolate lock/.test(first),
|
|
83
92
|
() => {
|
|
84
93
|
}
|
|
85
94
|
],
|
|
86
|
-
// Non-actionable warnings
|
|
95
|
+
// Non-actionable warnings:
|
|
87
96
|
[
|
|
88
97
|
([first]) => typeof first === "string" && /^A promise rejection/i.test(first),
|
|
89
98
|
() => {
|
|
90
99
|
}
|
|
91
100
|
],
|
|
101
|
+
[
|
|
102
|
+
([first]) => typeof first === "string" && /resolved to multiple addresses/i.test(first),
|
|
103
|
+
// For win32
|
|
104
|
+
() => {
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
// Non-actionable errors:
|
|
92
108
|
[
|
|
93
109
|
([first]) => {
|
|
94
110
|
const message = first?.message ?? first;
|
|
@@ -157,13 +173,7 @@ function muteAuthLogs({
|
|
|
157
173
|
}
|
|
158
174
|
function enhanceH2Logs(options) {
|
|
159
175
|
injectLogReplacer("error");
|
|
160
|
-
injectLogReplacer(
|
|
161
|
-
"warn",
|
|
162
|
-
([first]) => (
|
|
163
|
-
// Show createStorefrontClient warnings only once.
|
|
164
|
-
first?.includes?.("[h2:warn:createStorefrontClient]") ? true : void 0
|
|
165
|
-
)
|
|
166
|
-
);
|
|
176
|
+
injectLogReplacer("warn", warningDebouncer);
|
|
167
177
|
addMessageReplacers("h2-warn", [
|
|
168
178
|
([first]) => {
|
|
169
179
|
const message = first?.message ?? first;
|
|
@@ -182,10 +192,11 @@ function enhanceH2Logs(options) {
|
|
|
182
192
|
const lines = message.split("\n");
|
|
183
193
|
const lastLine = lines.at(-1) ?? "";
|
|
184
194
|
const hasLinks = /https?:\/\//.test(lastLine);
|
|
185
|
-
|
|
195
|
+
const hasCommands = /`h2 [^`]+`/.test(lastLine);
|
|
196
|
+
if (hasLinks || hasCommands)
|
|
186
197
|
lines.pop();
|
|
187
198
|
if (type === "error" || errorObject) {
|
|
188
|
-
let tryMessage = hasLinks ? lastLine : void 0;
|
|
199
|
+
let tryMessage = hasLinks || hasCommands ? lastLine : void 0;
|
|
189
200
|
let stack = errorObject?.stack;
|
|
190
201
|
let cause = errorObject?.cause;
|
|
191
202
|
if (typeof cause === "string") {
|
|
@@ -235,7 +246,13 @@ function enhanceH2Logs(options) {
|
|
|
235
246
|
const render = type === "warn" ? renderWarning : renderInfo;
|
|
236
247
|
render({
|
|
237
248
|
body: headline + colors.bold(lines.join("\n")),
|
|
238
|
-
reference
|
|
249
|
+
reference,
|
|
250
|
+
nextSteps: hasCommands ? [
|
|
251
|
+
lastLine.replace(
|
|
252
|
+
/`h2 [^`]+`/g,
|
|
253
|
+
(cmd) => colors.bold(colors.yellow(cmd))
|
|
254
|
+
)
|
|
255
|
+
] : void 0
|
|
239
256
|
});
|
|
240
257
|
return;
|
|
241
258
|
}
|
|
@@ -266,6 +283,7 @@ function createRemixLogger() {
|
|
|
266
283
|
name: "error",
|
|
267
284
|
type: 0,
|
|
268
285
|
message: buildMessageBody(message, options?.details),
|
|
286
|
+
skipOclifErrorHandling: true,
|
|
269
287
|
tryMessage: ""
|
|
270
288
|
});
|
|
271
289
|
}
|