@shopify/cli-hydrogen 12.0.0 → 12.0.2
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/README.md +1 -1
- package/dist/assets/hydrogen/routes/locale-check.ts +3 -3
- package/dist/assets/hydrogen/starter/.graphqlrc.ts +4 -2
- package/dist/assets/hydrogen/starter/CHANGELOG.md +278 -0
- package/dist/assets/hydrogen/starter/app/components/Aside.tsx +4 -2
- package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +67 -38
- package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +49 -8
- package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +133 -57
- package/dist/assets/hydrogen/starter/app/components/Header.tsx +3 -3
- package/dist/assets/hydrogen/starter/app/components/MockShopNotice.tsx +20 -0
- package/dist/assets/hydrogen/starter/app/components/PaginatedResourceSection.tsx +24 -4
- package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/entry.client.tsx +2 -3
- package/dist/assets/hydrogen/starter/app/lib/context.ts +5 -0
- package/dist/assets/hydrogen/starter/app/lib/fragments.ts +8 -0
- package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +14 -53
- package/dist/assets/hydrogen/starter/app/routes/_index.tsx +15 -9
- package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +14 -14
- package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/routes/account.tsx +4 -7
- package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +10 -0
- package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +2 -9
- package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +1 -4
- package/dist/assets/hydrogen/starter/app/routes/cart.tsx +3 -11
- package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +1 -3
- package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +2 -8
- package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +1 -4
- package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +2 -9
- package/dist/assets/hydrogen/starter/app/routes/search.tsx +18 -12
- package/dist/assets/hydrogen/starter/app/styles/app.css +72 -1
- package/dist/assets/hydrogen/starter/eslint.config.js +1 -0
- package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.md +3 -3
- package/dist/assets/hydrogen/starter/guides/search/search.md +1 -1
- package/dist/assets/hydrogen/starter/package.json +16 -16
- package/dist/assets/hydrogen/starter/server.ts +5 -7
- package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +89 -11
- package/dist/assets/hydrogen/starter/vite.config.ts +10 -4
- package/dist/assets/hydrogen/vite/package.json +1 -2
- package/dist/assets/hydrogen/vite/vite.config.js +4 -3
- package/dist/commands/hydrogen/deploy.js +19 -13
- package/dist/commands/hydrogen/dev.js +6 -6
- package/dist/commands/hydrogen/generate/route.js +1 -1
- package/dist/commands/hydrogen/init.d.ts +2 -4
- package/dist/commands/hydrogen/init.js +2 -8
- package/dist/commands/hydrogen/preview.js +1 -1
- package/dist/commands/hydrogen/setup/css.js +1 -1
- package/dist/commands/hydrogen/setup/markets.js +1 -1
- package/dist/commands/hydrogen/setup/vite.js +1 -1
- package/dist/commands/hydrogen/setup.js +31 -33
- package/dist/commands/hydrogen/upgrade.js +173 -39
- package/dist/lib/build.js +4 -5
- package/dist/lib/bundle/vite-plugin.js +7 -21
- package/dist/lib/check-lockfile.js +16 -7
- package/dist/lib/codegen.js +2 -2
- package/dist/lib/format-code.js +3 -3
- package/dist/lib/graphql/admin/client.js +48 -23
- package/dist/lib/import-utils.js +20 -4
- package/dist/lib/mini-oxygen/workerd.js +3 -5
- package/dist/lib/onboarding/common.js +21 -26
- package/dist/lib/onboarding/local.js +46 -59
- package/dist/lib/package-managers.js +2 -0
- package/dist/lib/patch-cli.js +198 -0
- package/dist/lib/setups/routes/generate.js +5 -3
- package/dist/lib/template-pack.js +93 -0
- package/dist/lib/transpile/morph/functions.js +1 -1
- package/dist/lib/transpile/morph/typedefs.js +2 -3
- package/dist/lib/upgrade-e2e-utils.js +50 -0
- package/oclif.manifest.json +7 -15
- package/package.json +22 -19
- package/dist/assets/hydrogen/starter/app/routes/api.$version.[graphql.json].tsx +0 -14
- package/dist/lib/react-router-version-check.js +0 -82
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { resolvePath, dirname } from '@shopify/cli-kit/node/path';
|
|
3
|
-
import {
|
|
3
|
+
import { createFileReadStream, readFile } from '@shopify/cli-kit/node/fs';
|
|
4
4
|
import { renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
5
5
|
import { outputNewline } from '@shopify/cli-kit/node/output';
|
|
6
6
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
7
7
|
import colors from '@shopify/cli-kit/node/colors';
|
|
8
8
|
import { handleMiniOxygenImportFail, SUBREQUEST_PROFILER_ENDPOINT, logRequestLine } from './common.js';
|
|
9
9
|
import { TUNNEL_DOMAIN, getUtilityBannerlines, getDebugBannerLine } from '../dev-shared.js';
|
|
10
|
-
import { setConstructors, handleDebugNetworkRequest,
|
|
10
|
+
import { setConstructors, handleDebugNetworkRequest, H2O_BINDING_NAME, createLogRequestEvent } from '../request-events.js';
|
|
11
11
|
import { importLocal } from '../import-utils.js';
|
|
12
12
|
|
|
13
13
|
async function startWorkerdServer({
|
|
@@ -28,9 +28,7 @@ async function startWorkerdServer({
|
|
|
28
28
|
setConstructors({ Response });
|
|
29
29
|
async function handleCustomerAccountSchema() {
|
|
30
30
|
const require2 = createRequire(import.meta.url);
|
|
31
|
-
const filePath = require2.resolve(
|
|
32
|
-
"@shopify/hydrogen/customer-account.schema.json"
|
|
33
|
-
);
|
|
31
|
+
const filePath = require2.resolve("@shopify/hydrogen/customer-account.schema.json");
|
|
34
32
|
return new Response(createFileReadStream(filePath), {
|
|
35
33
|
headers: { "Content-Type": "application/json" }
|
|
36
34
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { symlink, readdir } from 'node:fs/promises';
|
|
2
2
|
import { packageManagerFromUserAgent, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
|
|
3
|
-
import { renderConfirmationPrompt, renderInfo, renderTextPrompt, renderSelectPrompt, renderWarning, renderSuccess
|
|
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';
|
|
@@ -15,7 +15,7 @@ import { titleize } from '../string.js';
|
|
|
15
15
|
import { ALIAS_NAME, createPlatformShortcut } from '../shell.js';
|
|
16
16
|
import { transpileProject } from '../transpile/index.js';
|
|
17
17
|
import { renderCssPrompt, setupCssStrategy, CSS_STRATEGY_NAME_MAP } from '../setups/css/index.js';
|
|
18
|
-
import {
|
|
18
|
+
import { generateRoutes, generateProjectFile } from '../setups/routes/generate.js';
|
|
19
19
|
import { execAsync } from '../process.js';
|
|
20
20
|
import { getStorefronts } from '../graphql/admin/link-storefront.js';
|
|
21
21
|
import { isHydrogenMonorepo, getSkeletonSourceDir, getRepoNodeModules } from '../build.js';
|
|
@@ -42,32 +42,25 @@ async function handleI18n(controller, cliCommand, flagI18n) {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
const routesToScaffold = flagRoutes === true ? "all" : flagRoutes === false ? [] : await renderRoutePrompt({
|
|
47
|
-
abortSignal: controller.signal
|
|
48
|
-
});
|
|
49
|
-
const needsRouteGeneration = routesToScaffold === "all" || routesToScaffold.length > 0;
|
|
45
|
+
function handleRouteGeneration(controller) {
|
|
50
46
|
return {
|
|
51
|
-
needsRouteGeneration,
|
|
52
47
|
setupRoutes: async (directory, language, options) => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return result.routeGroups;
|
|
70
|
-
}
|
|
48
|
+
const result = await generateRoutes(
|
|
49
|
+
{
|
|
50
|
+
routeName: "all",
|
|
51
|
+
directory,
|
|
52
|
+
force: true,
|
|
53
|
+
typescript: language === "ts",
|
|
54
|
+
localePrefix: options?.i18nStrategy === "subfolders" ? "locale" : false,
|
|
55
|
+
signal: controller.signal,
|
|
56
|
+
...options
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
rootDirectory: directory,
|
|
60
|
+
appDirectory: joinPath(directory, "app")
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
return result.routeGroups;
|
|
71
64
|
}
|
|
72
65
|
};
|
|
73
66
|
}
|
|
@@ -284,6 +277,8 @@ async function handleDependencies(projectDir, controller, packageManagerFromFlag
|
|
|
284
277
|
cancellationMessage: "No",
|
|
285
278
|
abortSignal: controller.signal
|
|
286
279
|
});
|
|
280
|
+
} else {
|
|
281
|
+
actualPackageManager = detectedPackageManager;
|
|
287
282
|
}
|
|
288
283
|
}
|
|
289
284
|
if (isHydrogenMonorepo) {
|
|
@@ -2,8 +2,7 @@ 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';
|
|
5
|
-
import
|
|
6
|
-
import { renderSelectPrompt, renderSuccess, renderConfirmationPrompt, renderTasks } from '@shopify/cli-kit/node/ui';
|
|
5
|
+
import { renderSelectPrompt, renderSuccess, renderTasks } from '@shopify/cli-kit/node/ui';
|
|
7
6
|
import { handleStorefrontLink, handleProjectLocation, createAbortHandler, generateProjectEntries, handleLanguage, createInitialCommit, handleCssStrategy, commitAll, handleDependencies, handleCliShortcut, handleI18n, handleRouteGeneration, renderProjectReady } from './common.js';
|
|
8
7
|
import { createStorefront } from '../graphql/admin/create-storefront.js';
|
|
9
8
|
import { waitForJob } from '../graphql/admin/fetch-job.js';
|
|
@@ -49,7 +48,7 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
49
48
|
{
|
|
50
49
|
force: true,
|
|
51
50
|
recursive: true,
|
|
52
|
-
filter: (filepath) => !/^(app
|
|
51
|
+
filter: (filepath) => !/^(app(\/|$)|dist(\/|$)|node_modules(\/|$)|server\.ts$|\.shopify(\/|$))/i.test(
|
|
53
52
|
relativePath(templateDir, filepath)
|
|
54
53
|
)
|
|
55
54
|
}
|
|
@@ -217,65 +216,53 @@ async function setupLocalStarterTemplate(options, controller) {
|
|
|
217
216
|
]
|
|
218
217
|
});
|
|
219
218
|
}
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
overwriteFileDeps: false
|
|
255
|
-
}).then(async (routes) => {
|
|
256
|
-
setupSummary.routes = routes;
|
|
257
|
-
if (language === "ts" && setupSummary.depsInstalled) {
|
|
258
|
-
try {
|
|
259
|
-
await execAsync("npx react-router typegen", {
|
|
260
|
-
cwd: project.directory
|
|
261
|
-
});
|
|
262
|
-
} catch (error) {
|
|
263
|
-
outputWarn(
|
|
264
|
-
"Failed to generate React Router types after route generation. You may need to run `npx react-router typegen` manually."
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (options.git && routes) {
|
|
269
|
-
return commitAll(
|
|
270
|
-
project.directory,
|
|
271
|
-
`Generate routes for core functionality`
|
|
219
|
+
const { i18nStrategy, setupI18n } = await handleI18n(
|
|
220
|
+
controller,
|
|
221
|
+
setupSummary.cliCommand,
|
|
222
|
+
options.i18n
|
|
223
|
+
);
|
|
224
|
+
const { setupRoutes } = handleRouteGeneration(controller);
|
|
225
|
+
setupSummary.i18n = i18nStrategy;
|
|
226
|
+
backgroundWorkPromise = backgroundWorkPromise.then(async () => {
|
|
227
|
+
await setupI18n({
|
|
228
|
+
rootDirectory: project.directory,
|
|
229
|
+
contextCreate: language === "ts" ? "app/lib/context.ts" : "app/lib/context.js"
|
|
230
|
+
}).then(
|
|
231
|
+
() => options.git ? commitAll(
|
|
232
|
+
project.directory,
|
|
233
|
+
`Setup markets support using ${i18nStrategy}`
|
|
234
|
+
) : void 0
|
|
235
|
+
).catch((error) => {
|
|
236
|
+
setupSummary.i18nError = error;
|
|
237
|
+
});
|
|
238
|
+
await setupRoutes(project.directory, language, {
|
|
239
|
+
i18nStrategy,
|
|
240
|
+
// The init process might have added and modified files. Do not overwrite them.
|
|
241
|
+
// E.g. CSS imports might have been added to the root.
|
|
242
|
+
overwriteFileDeps: false
|
|
243
|
+
}).then(async (routes) => {
|
|
244
|
+
setupSummary.routes = routes;
|
|
245
|
+
if (language === "ts" && setupSummary.depsInstalled) {
|
|
246
|
+
try {
|
|
247
|
+
await execAsync("npx react-router typegen", {
|
|
248
|
+
cwd: project.directory
|
|
249
|
+
});
|
|
250
|
+
} catch (error) {
|
|
251
|
+
outputWarn(
|
|
252
|
+
"Failed to generate React Router types after route generation. You may need to run `npx react-router typegen` manually."
|
|
272
253
|
);
|
|
273
254
|
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
255
|
+
}
|
|
256
|
+
if (options.git && routes) {
|
|
257
|
+
return commitAll(
|
|
258
|
+
project.directory,
|
|
259
|
+
`Generate routes for core functionality`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}).catch((error) => {
|
|
263
|
+
setupSummary.routesError = error;
|
|
277
264
|
});
|
|
278
|
-
}
|
|
265
|
+
});
|
|
279
266
|
await renderTasks(tasks);
|
|
280
267
|
if (options.git) {
|
|
281
268
|
await commitAll(project.directory, "Lockfile");
|
|
@@ -2,6 +2,7 @@ const packageManagers = [
|
|
|
2
2
|
{
|
|
3
3
|
name: "npm",
|
|
4
4
|
lockfile: "package-lock.json",
|
|
5
|
+
alternativeLockfiles: ["npm-shrinkwrap.json"],
|
|
5
6
|
installCommand: "npm ci"
|
|
6
7
|
},
|
|
7
8
|
{
|
|
@@ -17,6 +18,7 @@ const packageManagers = [
|
|
|
17
18
|
{
|
|
18
19
|
name: "bun",
|
|
19
20
|
lockfile: "bun.lockb",
|
|
21
|
+
alternativeLockfiles: ["bun.lock"],
|
|
20
22
|
installCommand: "bun install --frozen-lockfile"
|
|
21
23
|
}
|
|
22
24
|
];
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { readFileSync, existsSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
function isErrnoException(err) {
|
|
5
|
+
return err instanceof Error && "code" in err;
|
|
6
|
+
}
|
|
7
|
+
const MARKER = "// [hydrogen-monorepo-patch]";
|
|
8
|
+
function getRunJsPath(root) {
|
|
9
|
+
return resolve(root, "node_modules", "@shopify", "cli", "bin", "run.js");
|
|
10
|
+
}
|
|
11
|
+
function isPatchApplied(content) {
|
|
12
|
+
return content.includes(MARKER);
|
|
13
|
+
}
|
|
14
|
+
function generatePatchedContent() {
|
|
15
|
+
return `#!/usr/bin/env node
|
|
16
|
+
${MARKER}
|
|
17
|
+
|
|
18
|
+
// Mirrors upstream @shopify/cli/bin/run.js behavior.
|
|
19
|
+
// This is not introduced by the patch \u2014 @shopify/cli ships with this line.
|
|
20
|
+
// Changing it here would create a behavioral divergence between dev and production.
|
|
21
|
+
process.removeAllListeners('warning')
|
|
22
|
+
|
|
23
|
+
// --- Monorepo detection ---
|
|
24
|
+
// Walk up from cwd to find the hydrogen monorepo root.
|
|
25
|
+
// We look for packages/cli/package.json with the correct package name \u2014
|
|
26
|
+
// if it matches, we know we're inside the monorepo and should load the
|
|
27
|
+
// local @shopify/cli-hydrogen
|
|
28
|
+
const {existsSync, readFileSync} = await import('node:fs');
|
|
29
|
+
const {resolve, dirname} = await import('node:path');
|
|
30
|
+
|
|
31
|
+
let monorepoRoot = null;
|
|
32
|
+
let dir = process.cwd();
|
|
33
|
+
while (true) {
|
|
34
|
+
const candidate = resolve(dir, 'packages', 'cli', 'package.json');
|
|
35
|
+
if (existsSync(candidate)) {
|
|
36
|
+
try {
|
|
37
|
+
const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
38
|
+
if (pkg.name === '@shopify/cli-hydrogen') {
|
|
39
|
+
monorepoRoot = dir;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// Ignore malformed package.json and keep walking
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const parent = dirname(dir);
|
|
47
|
+
if (parent === dir) break; // reached filesystem root
|
|
48
|
+
dir = parent;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (monorepoRoot) {
|
|
52
|
+
// We're in the hydrogen monorepo. Start @shopify/cli normally but
|
|
53
|
+
// inject pluginAdditions so oclif loads @shopify/cli-hydrogen from
|
|
54
|
+
// the monorepo's workspace (packages/cli) instead of the version
|
|
55
|
+
// bundled inside @shopify/cli/dist.
|
|
56
|
+
//
|
|
57
|
+
// This uses the same pluginAdditions mechanism that ShopifyConfig
|
|
58
|
+
// uses internally
|
|
59
|
+
const {fileURLToPath} = await import('node:url');
|
|
60
|
+
const {Config, run, flush} = await import('@oclif/core');
|
|
61
|
+
|
|
62
|
+
// root must point to @shopify/cli's installed location so oclif
|
|
63
|
+
// can find its package.json, oclif config, and bundled commands.
|
|
64
|
+
const cliRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
65
|
+
|
|
66
|
+
const c = '\\x1b[38;5;209m';
|
|
67
|
+
const d = '\\x1b[2m';
|
|
68
|
+
const r = '\\x1b[0m';
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log(c + ' \u250C\u2500\u2500 hydrogen-monorepo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510' + r);
|
|
71
|
+
console.log(c + ' \u2502' + r + ' ' + c + '\u2502' + r);
|
|
72
|
+
console.log(c + ' \u2502' + r + ' Using local cli-hydrogen plugin from packages/cli ' + c + '\u2502' + r);
|
|
73
|
+
console.log(c + ' \u2502' + r + d + ' Bundled commands replaced with local source ' + r + c + '\u2502' + r);
|
|
74
|
+
console.log(c + ' \u2502' + r + ' ' + c + '\u2502' + r);
|
|
75
|
+
console.log(c + ' \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' + r);
|
|
76
|
+
console.log('');
|
|
77
|
+
|
|
78
|
+
// Tell ShopifyConfig to skip its own monorepo detection since we
|
|
79
|
+
// are handling pluginAdditions ourselves.
|
|
80
|
+
process.env.IGNORE_HYDROGEN_MONOREPO = '1';
|
|
81
|
+
|
|
82
|
+
const config = new Config({
|
|
83
|
+
root: cliRoot,
|
|
84
|
+
// pluginAdditions tells oclif's plugin loader to read the
|
|
85
|
+
// monorepo root's package.json, find @shopify/cli-hydrogen in
|
|
86
|
+
// its dependencies (workspace:*), and load that as a core plugin.
|
|
87
|
+
// Because workspace:* symlinks to packages/cli, oclif loads the
|
|
88
|
+
// local source code \u2014 exactly what we want for development.
|
|
89
|
+
pluginAdditions: {
|
|
90
|
+
core: ['@shopify/cli-hydrogen'],
|
|
91
|
+
path: monorepoRoot,
|
|
92
|
+
},
|
|
93
|
+
// Skip the oclif manifest cache so commands are loaded fresh from
|
|
94
|
+
// disk rather than from a potentially stale oclif.manifest.json.
|
|
95
|
+
ignoreManifest: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await config.load();
|
|
99
|
+
|
|
100
|
+
// --- Post-load command replacement ---
|
|
101
|
+
// After loading, both the bundled hydrogen commands (from @shopify/cli's
|
|
102
|
+
// root plugin) and the local ones (from pluginAdditions) are registered.
|
|
103
|
+
// Since @shopify/cli@3.83.0 (which upgraded to oclif v4),
|
|
104
|
+
// determinePriority is no longer an overridable instance method, so we
|
|
105
|
+
// manually replace the bundled commands with the external plugin's
|
|
106
|
+
// versions using oclif's private _commands Map.
|
|
107
|
+
const externalPlugin = Array.from(config.plugins.values()).find(
|
|
108
|
+
(p) => p.name === '@shopify/cli-hydrogen' && !p.isRoot,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
if (!externalPlugin) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
'[hydrogen-monorepo] Could not find local @shopify/cli-hydrogen plugin. ' +
|
|
114
|
+
'The patch may need updating for the current @shopify/cli version.'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const cmds = config._commands;
|
|
119
|
+
if (!cmds || !(cmds instanceof Map)) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
'[hydrogen-monorepo] Cannot replace bundled commands \u2014 oclif internals changed. ' +
|
|
122
|
+
'The patch may need updating for the current oclif version.'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Delete bundled hydrogen commands (canonical IDs + aliases + hidden aliases)
|
|
127
|
+
for (const command of externalPlugin.commands) {
|
|
128
|
+
if (!command.id.startsWith('hydrogen')) continue;
|
|
129
|
+
cmds.delete(command.id);
|
|
130
|
+
for (const alias of [...(command.aliases ?? []), ...(command.hiddenAliases ?? [])]) {
|
|
131
|
+
cmds.delete(alias);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Re-insert commands from the local plugin. loadCommands handles
|
|
135
|
+
// alias registration and command permutations correctly.
|
|
136
|
+
config.loadCommands(externalPlugin);
|
|
137
|
+
|
|
138
|
+
await run(process.argv.slice(2), config);
|
|
139
|
+
await flush();
|
|
140
|
+
} else {
|
|
141
|
+
// Not in the monorepo \u2014 run the standard @shopify/cli entrypoint.
|
|
142
|
+
const {default: runCLI} = await import('../dist/index.js');
|
|
143
|
+
runCLI({development: false});
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
function generateOriginalContent() {
|
|
148
|
+
return `#!/usr/bin/env node
|
|
149
|
+
|
|
150
|
+
process.removeAllListeners('warning')
|
|
151
|
+
|
|
152
|
+
import runCLI from '../dist/index.js'
|
|
153
|
+
|
|
154
|
+
runCLI({development: false})
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
157
|
+
function applyPatch(runJsPath) {
|
|
158
|
+
let current;
|
|
159
|
+
try {
|
|
160
|
+
current = readFileSync(runJsPath, "utf8");
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (isErrnoException(err) && err.code === "ENOENT") {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`@shopify/cli is not installed (${runJsPath} not found). Run 'pnpm install' first.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
if (isPatchApplied(current)) return false;
|
|
170
|
+
const backupPath = runJsPath + ".backup";
|
|
171
|
+
if (!existsSync(backupPath)) {
|
|
172
|
+
writeFileSync(backupPath, current);
|
|
173
|
+
}
|
|
174
|
+
writeFileSync(runJsPath, generatePatchedContent());
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
function removePatch(runJsPath) {
|
|
178
|
+
let current;
|
|
179
|
+
try {
|
|
180
|
+
current = readFileSync(runJsPath, "utf8");
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (isErrnoException(err) && err.code === "ENOENT") {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
if (!isPatchApplied(current)) return false;
|
|
188
|
+
const backupPath = runJsPath + ".backup";
|
|
189
|
+
const hasBackup = existsSync(backupPath);
|
|
190
|
+
const original = hasBackup ? readFileSync(backupPath, "utf8") : generateOriginalContent();
|
|
191
|
+
writeFileSync(runJsPath, original);
|
|
192
|
+
if (hasBackup) {
|
|
193
|
+
unlinkSync(backupPath);
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { MARKER, applyPatch, generateOriginalContent, generatePatchedContent, getRunJsPath, isPatchApplied, removePatch };
|
|
@@ -22,8 +22,7 @@ const ROUTE_MAP = {
|
|
|
22
22
|
account: "account*",
|
|
23
23
|
search: ["search", "api.predictive-search"],
|
|
24
24
|
robots: "[robots.txt]",
|
|
25
|
-
sitemap: ["[sitemap.xml]", "sitemap.$type.$page[.xml]"]
|
|
26
|
-
tokenlessApi: "api.$version.[graphql.json]"
|
|
25
|
+
sitemap: ["[sitemap.xml]", "sitemap.$type.$page[.xml]"]
|
|
27
26
|
};
|
|
28
27
|
let allRouteTemplateFiles = [];
|
|
29
28
|
async function getResolvedRoutes(routeKeys = Object.keys(ROUTE_MAP)) {
|
|
@@ -187,7 +186,10 @@ async function generateProjectFile(routeFrom, {
|
|
|
187
186
|
return result;
|
|
188
187
|
}
|
|
189
188
|
function replaceAdapters(templateContent, adapter) {
|
|
190
|
-
return templateContent.replace(
|
|
189
|
+
return templateContent.replace(
|
|
190
|
+
/(from\s+['"])react-router(['"])/g,
|
|
191
|
+
`$1${adapter}$2`
|
|
192
|
+
);
|
|
191
193
|
}
|
|
192
194
|
function getDestinationRoute(routeFrom, localePrefix, options) {
|
|
193
195
|
const routePath = routeFrom.replace(ASSETS_STARTER_DIR_ROUTES + "/", "");
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { readFile, writeFile, mkdtemp, rm } from 'node:fs/promises';
|
|
4
|
+
import { join, isAbsolute } from 'node:path';
|
|
5
|
+
import { pipeline } from 'node:stream/promises';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import gunzipMaybe from 'gunzip-maybe';
|
|
8
|
+
import { extract } from 'tar-fs';
|
|
9
|
+
|
|
10
|
+
const WINDOWS_SHELL_OPTS = process.platform === "win32" ? { shell: true } : {};
|
|
11
|
+
const PNPM_PACK_TIMEOUT_IN_MS = 6e4;
|
|
12
|
+
const DEPENDENCY_SECTIONS = [
|
|
13
|
+
"dependencies",
|
|
14
|
+
"devDependencies",
|
|
15
|
+
"peerDependencies",
|
|
16
|
+
"optionalDependencies"
|
|
17
|
+
];
|
|
18
|
+
function hasReplaceableProtocol(version) {
|
|
19
|
+
return version.startsWith("workspace:") || version.startsWith("catalog:");
|
|
20
|
+
}
|
|
21
|
+
async function getPackedTemplatePackageJson(sourceTemplateDir) {
|
|
22
|
+
const tempDir = await mkdtemp(join(tmpdir(), "hydrogen-template-pack-"));
|
|
23
|
+
try {
|
|
24
|
+
const rawPackResult = execFileSync(
|
|
25
|
+
"pnpm",
|
|
26
|
+
["pack", "--pack-destination", tempDir, "--json"],
|
|
27
|
+
{
|
|
28
|
+
cwd: sourceTemplateDir,
|
|
29
|
+
encoding: "utf8",
|
|
30
|
+
timeout: PNPM_PACK_TIMEOUT_IN_MS,
|
|
31
|
+
...WINDOWS_SHELL_OPTS
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
const parsedResult = JSON.parse(rawPackResult.trim());
|
|
35
|
+
const packedTarball = Array.isArray(parsedResult) ? parsedResult[0]?.filename : parsedResult.filename;
|
|
36
|
+
if (!packedTarball) {
|
|
37
|
+
throw new Error("pnpm pack did not return a tarball filename.");
|
|
38
|
+
}
|
|
39
|
+
const tarballPath = isAbsolute(packedTarball) ? packedTarball : join(tempDir, packedTarball);
|
|
40
|
+
await pipeline(
|
|
41
|
+
createReadStream(tarballPath),
|
|
42
|
+
gunzipMaybe(),
|
|
43
|
+
extract(tempDir)
|
|
44
|
+
);
|
|
45
|
+
const packedManifestRaw = await readFile(
|
|
46
|
+
join(tempDir, "package", "package.json"),
|
|
47
|
+
"utf8"
|
|
48
|
+
);
|
|
49
|
+
return JSON.parse(packedManifestRaw);
|
|
50
|
+
} finally {
|
|
51
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function replaceWorkspaceProtocolVersionsInPackageJson({
|
|
55
|
+
packedManifest,
|
|
56
|
+
targetPackageJson
|
|
57
|
+
}) {
|
|
58
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
59
|
+
const targetDeps = targetPackageJson[section];
|
|
60
|
+
if (!targetDeps) continue;
|
|
61
|
+
for (const [name, version] of Object.entries(targetDeps)) {
|
|
62
|
+
if (!hasReplaceableProtocol(version)) continue;
|
|
63
|
+
const packedVersion = packedManifest[section]?.[name];
|
|
64
|
+
if (!packedVersion) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Unable to resolve ${name} from ${section} in packed template manifest.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
targetDeps[name] = packedVersion;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function replaceWorkspaceProtocolVersions({
|
|
74
|
+
sourceTemplateDir,
|
|
75
|
+
targetTemplateDir
|
|
76
|
+
}) {
|
|
77
|
+
const packedManifest = await getPackedTemplatePackageJson(sourceTemplateDir);
|
|
78
|
+
const targetPackageJsonPath = join(targetTemplateDir, "package.json");
|
|
79
|
+
const targetPackageJson = JSON.parse(
|
|
80
|
+
await readFile(targetPackageJsonPath, "utf8")
|
|
81
|
+
);
|
|
82
|
+
replaceWorkspaceProtocolVersionsInPackageJson({
|
|
83
|
+
packedManifest,
|
|
84
|
+
targetPackageJson
|
|
85
|
+
});
|
|
86
|
+
await writeFile(
|
|
87
|
+
targetPackageJsonPath,
|
|
88
|
+
`${JSON.stringify(targetPackageJson, null, 2)}
|
|
89
|
+
`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { WINDOWS_SHELL_OPTS, replaceWorkspaceProtocolVersions };
|
|
@@ -35,7 +35,7 @@ function isAnnotableType(type) {
|
|
|
35
35
|
}
|
|
36
36
|
function normalizeType(type) {
|
|
37
37
|
const normalizedType = type.replace(/^\(\)\s+=>\s+/, "").replace(/typeof import\("[^"]+"\)\./, "typeof ");
|
|
38
|
-
return normalizedType === "SerializeFrom<any>" ? `{
|
|
38
|
+
return normalizedType === "SerializeFrom<any>" ? `{ReturnType<typeof useLoaderData<typeof loader>>}` : `{${normalizedType}}`;
|
|
39
39
|
}
|
|
40
40
|
function generateParameterDocumentation(functionNode, docNode) {
|
|
41
41
|
const params = functionNode.getParameters();
|
|
@@ -28,7 +28,6 @@ function generateTypeDefs(sourceFile, code) {
|
|
|
28
28
|
typedefs.push("");
|
|
29
29
|
const knownGenerics = {
|
|
30
30
|
MetaFunction: "T",
|
|
31
|
-
SerializeFrom: "T",
|
|
32
31
|
Fetcher: "T",
|
|
33
32
|
PartialPredictiveSearchResult: "ItemType, ExtraProps",
|
|
34
33
|
PartialSearchResult: "ItemType"
|
|
@@ -52,7 +51,7 @@ function generateTypeDefs(sourceFile, code) {
|
|
|
52
51
|
}
|
|
53
52
|
if (/(function loader\(|const loader =)/.test(source)) {
|
|
54
53
|
typedefs.push(
|
|
55
|
-
`/** @typedef {
|
|
54
|
+
`/** @typedef {ReturnType<typeof useLoaderData<typeof loader>>} LoaderReturnData */`
|
|
56
55
|
);
|
|
57
56
|
code = code.replace(
|
|
58
57
|
/^\s+?const\s+([\w\d]+|\{[\w\d\s,.]+\})\s+?=\s+?useLoaderData\(\)/gms,
|
|
@@ -72,7 +71,7 @@ function generateTypeDefs(sourceFile, code) {
|
|
|
72
71
|
}
|
|
73
72
|
if (/(function action\(|const action =)/.test(source)) {
|
|
74
73
|
typedefs.push(
|
|
75
|
-
`/** @typedef {
|
|
74
|
+
`/** @typedef {ReturnType<typeof useActionData<typeof action>>} ActionReturnData */`
|
|
76
75
|
);
|
|
77
76
|
code = code.replace(
|
|
78
77
|
/^\s+?const\s+([\w\d]+|\{[\w\d\s,.]+\})\s+?=\s+?useActionData\(\)/gms,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const DEPENDENCY_SECTIONS = [
|
|
2
|
+
"dependencies",
|
|
3
|
+
"devDependencies"
|
|
4
|
+
];
|
|
5
|
+
function parseCatalogFromWorkspaceYaml(yamlContent) {
|
|
6
|
+
const catalogVersions = {};
|
|
7
|
+
const catalogMatch = yamlContent.match(/^catalog:\s*\n((?:[ \t]+.+\n?)*)/m);
|
|
8
|
+
const catalogSection = catalogMatch?.[1];
|
|
9
|
+
if (!catalogSection) return catalogVersions;
|
|
10
|
+
for (const line of catalogSection.split("\n")) {
|
|
11
|
+
const match = line.match(
|
|
12
|
+
/^\s+['"]?([^'":]+?)['"]?\s*:\s*['"]?([^'"]+?)['"]?\s*$/
|
|
13
|
+
);
|
|
14
|
+
if (match?.[1] && match[2]) {
|
|
15
|
+
catalogVersions[match[1].trim()] = match[2].trim();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return catalogVersions;
|
|
19
|
+
}
|
|
20
|
+
function parseWorkspacePackagesFromYaml(yamlContent) {
|
|
21
|
+
const packagesMatch = yamlContent.match(
|
|
22
|
+
/^packages:\s*\n((?:[ \t]+-.+\n?)*)/m
|
|
23
|
+
);
|
|
24
|
+
const packagesSection = packagesMatch?.[1];
|
|
25
|
+
if (!packagesSection) return [];
|
|
26
|
+
return packagesSection.split("\n").map((line) => line.match(/^\s+-\s+(.+)/)?.[1]?.trim()).filter((path) => Boolean(path));
|
|
27
|
+
}
|
|
28
|
+
async function resolveWorkspaceProtocols({
|
|
29
|
+
packageJson,
|
|
30
|
+
catalogVersions,
|
|
31
|
+
resolveWorkspaceVersion,
|
|
32
|
+
fallbackVersion
|
|
33
|
+
}) {
|
|
34
|
+
const resolved = JSON.parse(JSON.stringify(packageJson));
|
|
35
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
36
|
+
const deps = resolved[section];
|
|
37
|
+
if (!deps) continue;
|
|
38
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
39
|
+
if (version.startsWith("workspace:")) {
|
|
40
|
+
const resolvedVersion = await resolveWorkspaceVersion(name);
|
|
41
|
+
deps[name] = resolvedVersion ?? fallbackVersion;
|
|
42
|
+
} else if (version === "catalog:" && catalogVersions[name]) {
|
|
43
|
+
deps[name] = catalogVersions[name];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return resolved;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { parseCatalogFromWorkspaceYaml, parseWorkspacePackagesFromYaml, resolveWorkspaceProtocols };
|