@shopify/cli-hydrogen 12.0.2 → 13.0.1
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 +8 -0
- package/dist/assets/hydrogen/starter/README.md +4 -4
- package/dist/assets/hydrogen/starter/package.json +2 -2
- package/dist/assets/hydrogen/starter/vite.config.ts +6 -0
- package/dist/commands/hydrogen/deploy.js +85 -40
- package/dist/commands/hydrogen/env/push.js +26 -3
- package/dist/hooks/init.js +5 -14
- package/dist/index.d.ts +2 -0
- package/dist/lib/hydrogen-command-policy.js +45 -0
- package/dist/lib/vite-config.js +42 -19
- package/oclif.manifest.json +39 -3
- package/package.json +3 -3
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# skeleton
|
|
2
2
|
|
|
3
|
+
## 2026.4.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Add an explicit `~` app alias to new Hydrogen projects. ([#3852](https://github.com/Shopify/hydrogen/pull/3852)) by [@andguy95](https://github.com/andguy95)
|
|
8
|
+
|
|
9
|
+
JavaScript projects use `jsconfig.json`, which is not reliably covered by Vite's native `resolve.tsconfigPaths` behavior. New projects now define Hydrogen's `~/` import convention directly in the Vite config so imports like `~/assets/favicon.svg` work in both TypeScript and JavaScript projects.
|
|
10
|
+
|
|
3
11
|
## 2026.4.4
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Hydrogen template: Skeleton
|
|
2
2
|
|
|
3
|
-
Hydrogen is Shopify’s stack for headless commerce. Hydrogen is designed to dovetail with [
|
|
3
|
+
Hydrogen is Shopify’s stack for headless commerce. Hydrogen is designed to dovetail with [React Router](https://reactrouter.com/), the modern multi-strategy router for React. This template contains a **minimal setup** of components, queries and tooling to get started with Hydrogen.
|
|
4
4
|
|
|
5
5
|
[Check out Hydrogen docs](https://shopify.dev/custom-storefronts/hydrogen)
|
|
6
|
-
[Get familiar with
|
|
6
|
+
[Get familiar with React Router](https://reactrouter.com/start/framework/routing)
|
|
7
7
|
|
|
8
8
|
## What's included
|
|
9
9
|
|
|
10
|
-
-
|
|
10
|
+
- React Router
|
|
11
11
|
- Hydrogen
|
|
12
12
|
- Oxygen
|
|
13
13
|
- Vite
|
|
@@ -22,7 +22,7 @@ Hydrogen is Shopify’s stack for headless commerce. Hydrogen is designed to dov
|
|
|
22
22
|
|
|
23
23
|
**Requirements:**
|
|
24
24
|
|
|
25
|
-
- Node.js version
|
|
25
|
+
- Node.js version 22.x or 24.x
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
npm create @shopify/hydrogen@latest
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "skeleton",
|
|
3
3
|
"private": true,
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "2026.4.
|
|
5
|
+
"version": "2026.4.5",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "shopify hydrogen build --codegen",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@react-router/fs-routes": "7.16.0",
|
|
33
33
|
"@shopify/cli": "3.93.2",
|
|
34
34
|
"@shopify/hydrogen-codegen": "0.3.3",
|
|
35
|
-
"@shopify/mini-oxygen": "4.
|
|
35
|
+
"@shopify/mini-oxygen": "4.2.0",
|
|
36
36
|
"@shopify/oxygen-workers-types": "^4.1.6",
|
|
37
37
|
"@shopify/prettier-config": "^1.1.2",
|
|
38
38
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {fileURLToPath} from 'node:url';
|
|
1
2
|
import {defineConfig} from 'vite';
|
|
2
3
|
import {hydrogen} from '@shopify/hydrogen/vite';
|
|
3
4
|
import {oxygen} from '@shopify/mini-oxygen/vite';
|
|
@@ -6,6 +7,11 @@ import {reactRouter} from '@react-router/dev/vite';
|
|
|
6
7
|
export default defineConfig({
|
|
7
8
|
plugins: [hydrogen(), oxygen(), reactRouter()],
|
|
8
9
|
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
// Vite's native tsconfig path resolver does not cover JavaScript
|
|
12
|
+
// projects that use jsconfig.json, so define Hydrogen's app alias here.
|
|
13
|
+
'~': fileURLToPath(new URL('./app', import.meta.url)),
|
|
14
|
+
},
|
|
9
15
|
tsconfigPaths: true,
|
|
10
16
|
},
|
|
11
17
|
build: {
|
|
@@ -21,6 +21,7 @@ import { isClassicProject, getProjectPaths } from '../../lib/remix-config.js';
|
|
|
21
21
|
import { packageManagers } from '../../lib/package-managers.js';
|
|
22
22
|
|
|
23
23
|
const DEPLOY_OUTPUT_FILE_HANDLE = "h2_deploy_log.json";
|
|
24
|
+
const DEFAULT_BUILD_COMMAND = "node --run build";
|
|
24
25
|
const deploymentLogger = (message, level = "info") => {
|
|
25
26
|
if (level === "error" || level === "warn") {
|
|
26
27
|
outputWarn(message);
|
|
@@ -46,7 +47,7 @@ class Deploy extends Command {
|
|
|
46
47
|
}),
|
|
47
48
|
force: Flags.boolean({
|
|
48
49
|
char: "f",
|
|
49
|
-
description: "Forces a deployment to proceed if there are uncommitted changes in its Git repository.",
|
|
50
|
+
description: "Forces a deployment to proceed if there are uncommitted changes in its Git repository, and skips confirmation prompts for non-preview environments.",
|
|
50
51
|
default: false,
|
|
51
52
|
env: "SHOPIFY_HYDROGEN_FLAG_FORCE",
|
|
52
53
|
required: false
|
|
@@ -69,7 +70,17 @@ class Deploy extends Command {
|
|
|
69
70
|
dependsOn: ["auth-bypass-token"]
|
|
70
71
|
}),
|
|
71
72
|
"build-command": Flags.string({
|
|
72
|
-
description: "Specify a build command to run before deploying. If not specified,
|
|
73
|
+
description: "Specify a build command to run before deploying. If not specified, the Hydrogen build pipeline will be used. When custom output directories are configured, defaults to `node --run build`.",
|
|
74
|
+
required: false
|
|
75
|
+
}),
|
|
76
|
+
"assets-dir": Flags.string({
|
|
77
|
+
description: "Directory containing the client assets to deploy, relative to the project root. Defaults to the detected Vite client output directory, then falls back to `dist/client`.",
|
|
78
|
+
env: "SHOPIFY_HYDROGEN_FLAG_ASSETS_DIR",
|
|
79
|
+
required: false
|
|
80
|
+
}),
|
|
81
|
+
"worker-dir": Flags.string({
|
|
82
|
+
description: "Directory containing the Oxygen worker entry point (`index.js` or `index.mjs`), relative to the project root. Defaults to the detected Vite server output directory, then falls back to `dist/server`.",
|
|
83
|
+
env: "SHOPIFY_HYDROGEN_FLAG_WORKER_DIR",
|
|
73
84
|
required: false
|
|
74
85
|
}),
|
|
75
86
|
...commonFlags.lockfileCheck,
|
|
@@ -155,17 +166,19 @@ async function runDeploy(options) {
|
|
|
155
166
|
env: envHandle,
|
|
156
167
|
envBranch,
|
|
157
168
|
environmentFile,
|
|
158
|
-
force
|
|
169
|
+
force,
|
|
159
170
|
forceClientSourcemap = false,
|
|
160
171
|
noVerify,
|
|
161
172
|
lockfileCheck,
|
|
162
173
|
jsonOutput,
|
|
163
174
|
path: root,
|
|
164
175
|
shop,
|
|
176
|
+
assetsDir: assetsDirFlag,
|
|
165
177
|
metadataUrl,
|
|
166
178
|
metadataUser,
|
|
167
179
|
metadataVersion,
|
|
168
|
-
entry: ssrEntry
|
|
180
|
+
entry: ssrEntry,
|
|
181
|
+
workerDir: workerDirFlag
|
|
169
182
|
} = options;
|
|
170
183
|
let { metadataDescription } = options;
|
|
171
184
|
let isCleanGit = true;
|
|
@@ -175,7 +188,7 @@ async function runDeploy(options) {
|
|
|
175
188
|
if (error instanceof GitDirectoryNotCleanError) {
|
|
176
189
|
isCleanGit = false;
|
|
177
190
|
}
|
|
178
|
-
if (!
|
|
191
|
+
if (!force && !isCleanGit) {
|
|
179
192
|
let errorMessage = "Uncommitted changes detected";
|
|
180
193
|
let changedFiles = void 0;
|
|
181
194
|
const nextSteps = [
|
|
@@ -335,16 +348,22 @@ ${changedFiles.trimEnd()}`;
|
|
|
335
348
|
let assetsDir = "dist/client";
|
|
336
349
|
let workerDir = "dist/worker";
|
|
337
350
|
const isClassicCompiler = await isClassicProject(root);
|
|
338
|
-
|
|
351
|
+
const metadataHydrogenVersion = getHydrogenVersion({ appPath: root });
|
|
352
|
+
const shouldUseDefaultBuildCommand = !buildCommand && !isClassicCompiler && (assetsDirFlag || workerDirFlag || isHydrogenPreviewVersion(metadataHydrogenVersion));
|
|
353
|
+
if (isClassicCompiler) {
|
|
354
|
+
assetsDir = assetsDirFlag ?? assetsDir;
|
|
355
|
+
workerDir = workerDirFlag ?? workerDir;
|
|
356
|
+
} else {
|
|
339
357
|
const viteConfig = await getViteConfig(root, ssrEntry).catch(() => null);
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
358
|
+
const outputDirs = resolveDeploymentOutputDirs({
|
|
359
|
+
root,
|
|
360
|
+
viteOutputDirs: viteConfig,
|
|
361
|
+
assetsDirFlag,
|
|
362
|
+
workerDirFlag
|
|
363
|
+
});
|
|
364
|
+
assetsDir = outputDirs.assetsDir;
|
|
365
|
+
workerDir = outputDirs.workerDir;
|
|
346
366
|
}
|
|
347
|
-
const metadataHydrogenVersion = await getHydrogenVersion({ appPath: root });
|
|
348
367
|
const config = {
|
|
349
368
|
assetsDir,
|
|
350
369
|
bugsnag: true,
|
|
@@ -369,7 +388,7 @@ ${changedFiles.trimEnd()}`;
|
|
|
369
388
|
workerDir,
|
|
370
389
|
overriddenEnvironmentVariables
|
|
371
390
|
};
|
|
372
|
-
if (!isCI && !config.defaultEnvironment && (userProvidedEnvironmentTag || userChosenEnvironmentTag)) {
|
|
391
|
+
if (!isCI && !config.defaultEnvironment && (userProvidedEnvironmentTag || userChosenEnvironmentTag) && !force) {
|
|
373
392
|
let chosenEnvironment = findEnvironmentByBranchOrThrow(
|
|
374
393
|
deploymentData.environments,
|
|
375
394
|
config.environmentTag
|
|
@@ -403,6 +422,7 @@ Continue?`.value
|
|
|
403
422
|
}
|
|
404
423
|
);
|
|
405
424
|
let deployError = null;
|
|
425
|
+
let buildError = null;
|
|
406
426
|
let resolveDeploy;
|
|
407
427
|
let rejectDeploy;
|
|
408
428
|
const deployPromise = new Promise((resolve, reject) => {
|
|
@@ -436,7 +456,7 @@ Continue?`.value
|
|
|
436
456
|
);
|
|
437
457
|
}
|
|
438
458
|
};
|
|
439
|
-
if (buildCommand) {
|
|
459
|
+
if (buildCommand || shouldUseDefaultBuildCommand) {
|
|
440
460
|
if (forceClientSourcemap) {
|
|
441
461
|
console.log("");
|
|
442
462
|
renderInfo({
|
|
@@ -444,24 +464,29 @@ Continue?`.value
|
|
|
444
464
|
body: "Client sourcemaps will not be generated."
|
|
445
465
|
});
|
|
446
466
|
}
|
|
447
|
-
config.buildCommand = buildCommand;
|
|
467
|
+
config.buildCommand = buildCommand ?? DEFAULT_BUILD_COMMAND;
|
|
448
468
|
} else {
|
|
449
469
|
hooks.buildFunction = async (assetPath) => {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
470
|
+
try {
|
|
471
|
+
outputInfo(
|
|
472
|
+
outputContent`${colors.whiteBright("Building project...")}`.value
|
|
473
|
+
);
|
|
474
|
+
if (isClassicCompiler) {
|
|
475
|
+
throw new AbortError(REMIX_COMPILER_ERROR_MESSAGE);
|
|
476
|
+
}
|
|
477
|
+
await runBuild({
|
|
478
|
+
directory: root,
|
|
479
|
+
assetPath,
|
|
480
|
+
lockfileCheck,
|
|
481
|
+
sourcemap: true,
|
|
482
|
+
forceClientSourcemap,
|
|
483
|
+
useCodegen: false,
|
|
484
|
+
entry: ssrEntry
|
|
485
|
+
});
|
|
486
|
+
} catch (error) {
|
|
487
|
+
buildError = error;
|
|
488
|
+
throw error;
|
|
455
489
|
}
|
|
456
|
-
await runBuild({
|
|
457
|
-
directory: root,
|
|
458
|
-
assetPath,
|
|
459
|
-
lockfileCheck,
|
|
460
|
-
sourcemap: true,
|
|
461
|
-
forceClientSourcemap,
|
|
462
|
-
useCodegen: false,
|
|
463
|
-
entry: ssrEntry
|
|
464
|
-
});
|
|
465
490
|
};
|
|
466
491
|
}
|
|
467
492
|
const uploadStart = async () => {
|
|
@@ -523,19 +548,39 @@ Continue?`.value
|
|
|
523
548
|
}
|
|
524
549
|
resolveDeploy();
|
|
525
550
|
}).catch((error) => {
|
|
526
|
-
rejectDeploy(deployError || error);
|
|
551
|
+
rejectDeploy(deployError || buildError || error);
|
|
527
552
|
});
|
|
528
553
|
return deployPromise;
|
|
529
554
|
}
|
|
530
|
-
|
|
555
|
+
function resolveDeploymentOutputDirs({
|
|
556
|
+
root,
|
|
557
|
+
viteOutputDirs,
|
|
558
|
+
assetsDirFlag,
|
|
559
|
+
workerDirFlag
|
|
560
|
+
}) {
|
|
561
|
+
const viteAssetsDir = viteOutputDirs && relativePath(root, viteOutputDirs.clientOutDir);
|
|
562
|
+
const viteWorkerDir = viteOutputDirs && relativePath(root, viteOutputDirs.serverOutDir);
|
|
563
|
+
return {
|
|
564
|
+
assetsDir: assetsDirFlag ?? viteAssetsDir ?? "dist/client",
|
|
565
|
+
workerDir: workerDirFlag ?? viteWorkerDir ?? "dist/server"
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
function isHydrogenPreviewVersion(version) {
|
|
569
|
+
return version?.startsWith("0.0.0-preview-") ?? false;
|
|
570
|
+
}
|
|
571
|
+
function getHydrogenVersion({ appPath }) {
|
|
531
572
|
const { root } = getProjectPaths(appPath);
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
require2
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
573
|
+
try {
|
|
574
|
+
const require2 = createRequire(import.meta.url);
|
|
575
|
+
const packageJson = require2(
|
|
576
|
+
require2.resolve("@shopify/hydrogen/package.json", {
|
|
577
|
+
paths: [root]
|
|
578
|
+
})
|
|
579
|
+
);
|
|
580
|
+
return typeof packageJson.version === "string" ? packageJson.version : void 0;
|
|
581
|
+
} catch {
|
|
582
|
+
return void 0;
|
|
583
|
+
}
|
|
539
584
|
}
|
|
540
585
|
|
|
541
|
-
export { Deploy as default, deploymentLogger, getHydrogenVersion, runDeploy };
|
|
586
|
+
export { Deploy as default, deploymentLogger, getHydrogenVersion, resolveDeploymentOutputDirs, runDeploy };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
2
3
|
import { diffLines } from 'diff';
|
|
3
4
|
import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
|
|
4
5
|
import { login } from '../../../lib/auth.js';
|
|
@@ -19,7 +20,17 @@ class EnvPush extends Command {
|
|
|
19
20
|
static flags = {
|
|
20
21
|
...commonFlags.env,
|
|
21
22
|
...commonFlags.envFile,
|
|
22
|
-
...commonFlags.path
|
|
23
|
+
...commonFlags.path,
|
|
24
|
+
force: Flags.boolean({
|
|
25
|
+
char: "f",
|
|
26
|
+
description: "Push environment variable changes without confirmation.",
|
|
27
|
+
env: "SHOPIFY_HYDROGEN_FLAG_FORCE"
|
|
28
|
+
}),
|
|
29
|
+
"dry-run": Flags.boolean({
|
|
30
|
+
description: "Preview environment variable changes without pushing them.",
|
|
31
|
+
env: "SHOPIFY_HYDROGEN_FLAG_DRY_RUN",
|
|
32
|
+
exclusive: ["force"]
|
|
33
|
+
})
|
|
23
34
|
};
|
|
24
35
|
async run() {
|
|
25
36
|
const { flags } = await this.parse(EnvPush);
|
|
@@ -27,8 +38,10 @@ class EnvPush extends Command {
|
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
40
|
async function runEnvPush({
|
|
41
|
+
dryRun = false,
|
|
30
42
|
env: envHandle,
|
|
31
43
|
envFile,
|
|
44
|
+
force = false,
|
|
32
45
|
path = process.cwd()
|
|
33
46
|
}) {
|
|
34
47
|
let validatedEnvironment;
|
|
@@ -114,8 +127,18 @@ async function runEnvPush({
|
|
|
114
127
|
body: "No changes to your environment variables."
|
|
115
128
|
});
|
|
116
129
|
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
130
|
+
}
|
|
131
|
+
const diff = diffLines(comparableRemoteVars, compareableLocalVars);
|
|
132
|
+
if (dryRun) {
|
|
133
|
+
renderInfo({
|
|
134
|
+
body: outputContent`The following changes would be made to your environment variables for ${validatedEnvironment.name}:
|
|
135
|
+
|
|
136
|
+
${outputToken.linesDiff(diff)}
|
|
137
|
+
No changes were pushed because --dry-run was used.`.value
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!force) {
|
|
119
142
|
const confirmPush = await renderConfirmationPrompt({
|
|
120
143
|
confirmationMessage: "Yes, confirm changes",
|
|
121
144
|
cancellationMessage: "No, make changes later",
|
package/dist/hooks/init.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
1
|
import { spawnSync } from 'node:child_process';
|
|
3
2
|
import { outputNewline, outputDebug } from '@shopify/cli-kit/node/output';
|
|
4
|
-
import { cwd, resolvePath
|
|
3
|
+
import { cwd, resolvePath } from '@shopify/cli-kit/node/path';
|
|
5
4
|
import { renderWarning } from '@shopify/cli-kit/node/ui';
|
|
5
|
+
import { isHydrogenProject, applyHydrogenCommandPolicy } from '../lib/hydrogen-command-policy.js';
|
|
6
6
|
|
|
7
7
|
const hook = async function(options) {
|
|
8
8
|
if (!options.id?.startsWith("hydrogen:") || options.id === "hydrogen:init") {
|
|
@@ -36,6 +36,9 @@ const hook = async function(options) {
|
|
|
36
36
|
});
|
|
37
37
|
process.exit(1);
|
|
38
38
|
}
|
|
39
|
+
if (applyHydrogenCommandPolicy({ id: options.id, projectPath })) {
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
39
42
|
if (commandNeedsVM(options.id, options.argv) && !process.execArgv.includes(EXPERIMENTAL_VM_MODULES_FLAG) && !(process.env.NODE_OPTIONS ?? "").includes(EXPERIMENTAL_VM_MODULES_FLAG)) {
|
|
40
43
|
outputDebug(
|
|
41
44
|
`Restarting CLI process with ${EXPERIMENTAL_VM_MODULES_FLAG} flag.`
|
|
@@ -50,18 +53,6 @@ const EXPERIMENTAL_VM_MODULES_FLAG = "--experimental-vm-modules";
|
|
|
50
53
|
function commandNeedsVM(id = "", argv = []) {
|
|
51
54
|
return id === "hydrogen:debug:cpu";
|
|
52
55
|
}
|
|
53
|
-
function isHydrogenProject(projectPath) {
|
|
54
|
-
try {
|
|
55
|
-
const require2 = createRequire(import.meta.url);
|
|
56
|
-
const { dependencies, scripts } = require2(
|
|
57
|
-
joinPath(projectPath, "package.json")
|
|
58
|
-
);
|
|
59
|
-
return !!dependencies?.["@shopify/hydrogen"] || // Diff examples don't have dependencies:
|
|
60
|
-
!!scripts?.dev?.includes("--diff");
|
|
61
|
-
} catch {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
56
|
var init_default = hook;
|
|
66
57
|
|
|
67
58
|
export { init_default as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -126,6 +126,8 @@ declare class EnvPull extends Command {
|
|
|
126
126
|
declare class EnvPush extends Command {
|
|
127
127
|
static description: string;
|
|
128
128
|
static flags: {
|
|
129
|
+
force: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
|
|
130
|
+
'dry-run': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
|
|
129
131
|
path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
|
|
130
132
|
'env-file': _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
|
|
131
133
|
env: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { outputNewline } from '@shopify/cli-kit/node/output';
|
|
3
|
+
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
4
|
+
import { renderError } from '@shopify/cli-kit/node/ui';
|
|
5
|
+
|
|
6
|
+
function applyHydrogenCommandPolicy({
|
|
7
|
+
id,
|
|
8
|
+
projectPath
|
|
9
|
+
}) {
|
|
10
|
+
if (!id || !isHydrogenProject(projectPath) || !isHydrogenCommandDisabled(projectPath, id)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
outputNewline();
|
|
14
|
+
renderError({
|
|
15
|
+
headline: `\`shopify ${id.replace(/:/g, " ")}\` is not supported by this version of Hydrogen`,
|
|
16
|
+
body: "The installed version of @shopify/hydrogen disables this command.",
|
|
17
|
+
nextSteps: ["Use your framework or package tooling instead."]
|
|
18
|
+
});
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
function isHydrogenCommandDisabled(projectPath, id) {
|
|
22
|
+
try {
|
|
23
|
+
const require2 = createRequire(joinPath(projectPath, "package.json"));
|
|
24
|
+
const hydrogenPackageJson = require2("@shopify/hydrogen/package.json");
|
|
25
|
+
const disabledCommands = hydrogenPackageJson?.shopify?.cli?.disabledCommands;
|
|
26
|
+
return Array.isArray(disabledCommands) && disabledCommands.includes(id);
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function isHydrogenProject(projectPath) {
|
|
32
|
+
try {
|
|
33
|
+
const require2 = createRequire(import.meta.url);
|
|
34
|
+
const projectPackageJson = require2(joinPath(projectPath, "package.json"));
|
|
35
|
+
return [
|
|
36
|
+
projectPackageJson?.dependencies,
|
|
37
|
+
projectPackageJson?.devDependencies,
|
|
38
|
+
projectPackageJson?.peerDependencies
|
|
39
|
+
].some((dependencies) => !!dependencies?.["@shopify/hydrogen"]);
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { applyHydrogenCommandPolicy, isHydrogenCommandDisabled, isHydrogenProject };
|
package/dist/lib/vite-config.js
CHANGED
|
@@ -37,9 +37,7 @@ async function getViteConfig(root, ssrEntryFlag) {
|
|
|
37
37
|
mode,
|
|
38
38
|
mode
|
|
39
39
|
);
|
|
40
|
-
const { appDirectory, serverBuildFile, routes } =
|
|
41
|
-
const serverOutDir = resolvedViteConfig.build.outDir;
|
|
42
|
-
const clientOutDir = serverOutDir.replace(/server$/, "client");
|
|
40
|
+
const { appDirectory, clientOutDir, serverBuildFile, serverOutDir, routes } = getBuildConfigFromVite(resolvedViteConfig);
|
|
43
41
|
const rollupOutput = resolvedViteConfig.build.rollupOptions.output;
|
|
44
42
|
const { entryFileNames } = (Array.isArray(rollupOutput) ? rollupOutput[0] : rollupOutput) ?? {};
|
|
45
43
|
const serverOutFile = joinPath(
|
|
@@ -68,33 +66,58 @@ async function getViteConfig(root, ssrEntryFlag) {
|
|
|
68
66
|
}
|
|
69
67
|
};
|
|
70
68
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
function resolveViteOutputDirs({
|
|
70
|
+
routingBuildDirectory,
|
|
71
|
+
serverEnvironmentOutDir,
|
|
72
|
+
clientEnvironmentOutDir,
|
|
73
|
+
buildOutDir
|
|
74
|
+
}) {
|
|
75
|
+
if (routingBuildDirectory) {
|
|
76
|
+
return {
|
|
77
|
+
clientOutDir: joinPath(routingBuildDirectory, "client"),
|
|
78
|
+
serverOutDir: joinPath(routingBuildDirectory, "server")
|
|
79
|
+
};
|
|
78
80
|
}
|
|
81
|
+
const serverOutDir = serverEnvironmentOutDir ?? buildOutDir;
|
|
82
|
+
const clientOutDir = clientEnvironmentOutDir && clientEnvironmentOutDir !== serverOutDir ? clientEnvironmentOutDir : serverOutDir.replace(/server$/, "client");
|
|
83
|
+
return {
|
|
84
|
+
clientOutDir,
|
|
85
|
+
serverOutDir
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function getBuildConfigFromVite(viteConfig) {
|
|
89
|
+
const buildConfig = getReactRouterConfigFromVite(viteConfig) ?? getRemixConfigFromVite(viteConfig);
|
|
90
|
+
const { clientOutDir, serverOutDir } = resolveViteOutputDirs({
|
|
91
|
+
routingBuildDirectory: buildConfig?.buildDirectory,
|
|
92
|
+
serverEnvironmentOutDir: viteConfig.environments?.ssr?.build.outDir,
|
|
93
|
+
clientEnvironmentOutDir: viteConfig.environments?.client?.build.outDir,
|
|
94
|
+
buildOutDir: viteConfig.build.outDir
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
...buildConfig,
|
|
98
|
+
clientOutDir,
|
|
99
|
+
serverOutDir
|
|
100
|
+
};
|
|
79
101
|
}
|
|
80
102
|
function getReactRouterConfigFromVite(viteConfig) {
|
|
81
|
-
if (!viteConfig.__reactRouterPluginContext)
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
const { appDirectory, serverBuildFile, routes } = viteConfig.__reactRouterPluginContext.reactRouterConfig;
|
|
103
|
+
if (!viteConfig.__reactRouterPluginContext) return;
|
|
104
|
+
const { appDirectory, buildDirectory, serverBuildFile, routes } = viteConfig.__reactRouterPluginContext.reactRouterConfig;
|
|
85
105
|
return {
|
|
86
106
|
appDirectory,
|
|
107
|
+
buildDirectory,
|
|
87
108
|
serverBuildFile,
|
|
88
109
|
routes
|
|
89
110
|
};
|
|
90
111
|
}
|
|
91
112
|
function getRemixConfigFromVite(viteConfig) {
|
|
92
113
|
const { remixConfig } = findHydrogenPlugin(viteConfig)?.api?.getPluginOptions() ?? {};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
114
|
+
if (remixConfig) {
|
|
115
|
+
return {
|
|
116
|
+
appDirectory: remixConfig.appDirectory,
|
|
117
|
+
serverBuildFile: remixConfig.serverBuildFile,
|
|
118
|
+
routes: remixConfig.routes
|
|
119
|
+
};
|
|
120
|
+
}
|
|
98
121
|
}
|
|
99
122
|
function findPlugin(config, name) {
|
|
100
123
|
return config.plugins.find((plugin) => plugin.name === name);
|
package/oclif.manifest.json
CHANGED
|
@@ -353,7 +353,7 @@
|
|
|
353
353
|
},
|
|
354
354
|
"force": {
|
|
355
355
|
"char": "f",
|
|
356
|
-
"description": "Forces a deployment to proceed if there are uncommitted changes in its Git repository.",
|
|
356
|
+
"description": "Forces a deployment to proceed if there are uncommitted changes in its Git repository, and skips confirmation prompts for non-preview environments.",
|
|
357
357
|
"env": "SHOPIFY_HYDROGEN_FLAG_FORCE",
|
|
358
358
|
"name": "force",
|
|
359
359
|
"required": false,
|
|
@@ -388,13 +388,31 @@
|
|
|
388
388
|
"type": "option"
|
|
389
389
|
},
|
|
390
390
|
"build-command": {
|
|
391
|
-
"description": "Specify a build command to run before deploying. If not specified,
|
|
391
|
+
"description": "Specify a build command to run before deploying. If not specified, the Hydrogen build pipeline will be used. When custom output directories are configured, defaults to `node --run build`.",
|
|
392
392
|
"name": "build-command",
|
|
393
393
|
"required": false,
|
|
394
394
|
"hasDynamicHelp": false,
|
|
395
395
|
"multiple": false,
|
|
396
396
|
"type": "option"
|
|
397
397
|
},
|
|
398
|
+
"assets-dir": {
|
|
399
|
+
"description": "Directory containing the client assets to deploy, relative to the project root. Defaults to the detected Vite client output directory, then falls back to `dist/client`.",
|
|
400
|
+
"env": "SHOPIFY_HYDROGEN_FLAG_ASSETS_DIR",
|
|
401
|
+
"name": "assets-dir",
|
|
402
|
+
"required": false,
|
|
403
|
+
"hasDynamicHelp": false,
|
|
404
|
+
"multiple": false,
|
|
405
|
+
"type": "option"
|
|
406
|
+
},
|
|
407
|
+
"worker-dir": {
|
|
408
|
+
"description": "Directory containing the Oxygen worker entry point (`index.js` or `index.mjs`), relative to the project root. Defaults to the detected Vite server output directory, then falls back to `dist/server`.",
|
|
409
|
+
"env": "SHOPIFY_HYDROGEN_FLAG_WORKER_DIR",
|
|
410
|
+
"name": "worker-dir",
|
|
411
|
+
"required": false,
|
|
412
|
+
"hasDynamicHelp": false,
|
|
413
|
+
"multiple": false,
|
|
414
|
+
"type": "option"
|
|
415
|
+
},
|
|
398
416
|
"lockfile-check": {
|
|
399
417
|
"description": "Checks that there is exactly one valid lockfile in the project. Defaults to `true`. Deactivate with `--no-lockfile-check`.",
|
|
400
418
|
"env": "SHOPIFY_HYDROGEN_FLAG_LOCKFILE_CHECK",
|
|
@@ -787,6 +805,24 @@
|
|
|
787
805
|
"hasDynamicHelp": false,
|
|
788
806
|
"multiple": false,
|
|
789
807
|
"type": "option"
|
|
808
|
+
},
|
|
809
|
+
"force": {
|
|
810
|
+
"char": "f",
|
|
811
|
+
"description": "Push environment variable changes without confirmation.",
|
|
812
|
+
"env": "SHOPIFY_HYDROGEN_FLAG_FORCE",
|
|
813
|
+
"name": "force",
|
|
814
|
+
"allowNo": false,
|
|
815
|
+
"type": "boolean"
|
|
816
|
+
},
|
|
817
|
+
"dry-run": {
|
|
818
|
+
"description": "Preview environment variable changes without pushing them.",
|
|
819
|
+
"env": "SHOPIFY_HYDROGEN_FLAG_DRY_RUN",
|
|
820
|
+
"exclusive": [
|
|
821
|
+
"force"
|
|
822
|
+
],
|
|
823
|
+
"name": "dry-run",
|
|
824
|
+
"allowNo": false,
|
|
825
|
+
"type": "boolean"
|
|
790
826
|
}
|
|
791
827
|
},
|
|
792
828
|
"hasDynamicHelp": false,
|
|
@@ -1675,5 +1711,5 @@
|
|
|
1675
1711
|
]
|
|
1676
1712
|
}
|
|
1677
1713
|
},
|
|
1678
|
-
"version": "
|
|
1714
|
+
"version": "13.0.1"
|
|
1679
1715
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
|
-
"version": "
|
|
7
|
+
"version": "13.0.1",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"repository": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"graphql-config": "^5.0.3",
|
|
60
60
|
"vite": "^5.1.0 || ^6.2.0 || ^7.0.0 || ^8.0.0",
|
|
61
61
|
"@shopify/hydrogen-codegen": "0.3.3",
|
|
62
|
-
"@shopify/mini-oxygen": "4.
|
|
62
|
+
"@shopify/mini-oxygen": "4.2.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"@graphql-codegen/cli": {
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"oclif.manifest.json"
|
|
99
99
|
],
|
|
100
100
|
"engines": {
|
|
101
|
-
"node": "^
|
|
101
|
+
"node": "^22 || ^24"
|
|
102
102
|
},
|
|
103
103
|
"oclif": {
|
|
104
104
|
"commands": "dist/commands",
|