@shopify/cli-hydrogen 13.0.0 → 13.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/dist/assets/hydrogen/starter/CHANGELOG.md +8 -0
- package/dist/assets/hydrogen/starter/README.md +4 -4
- package/dist/assets/hydrogen/starter/package.json +1 -1
- package/dist/assets/hydrogen/starter/vite.config.ts +6 -0
- package/dist/commands/hydrogen/deploy.js +37 -37
- package/dist/commands/hydrogen/env/push.js +26 -3
- package/dist/commands/hydrogen/link.js +40 -7
- package/dist/commands/hydrogen/upgrade.js +7 -5
- package/dist/hooks/init.js +5 -14
- package/dist/index.d.ts +5 -0
- package/dist/lib/hydrogen-command-policy.js +45 -0
- package/dist/lib/vite-config.js +42 -19
- package/oclif.manifest.json +54 -2
- 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
|
|
@@ -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: {
|
|
@@ -47,7 +47,7 @@ class Deploy extends Command {
|
|
|
47
47
|
}),
|
|
48
48
|
force: Flags.boolean({
|
|
49
49
|
char: "f",
|
|
50
|
-
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.",
|
|
51
51
|
default: false,
|
|
52
52
|
env: "SHOPIFY_HYDROGEN_FLAG_FORCE",
|
|
53
53
|
required: false
|
|
@@ -166,7 +166,7 @@ async function runDeploy(options) {
|
|
|
166
166
|
env: envHandle,
|
|
167
167
|
envBranch,
|
|
168
168
|
environmentFile,
|
|
169
|
-
force
|
|
169
|
+
force,
|
|
170
170
|
forceClientSourcemap = false,
|
|
171
171
|
noVerify,
|
|
172
172
|
lockfileCheck,
|
|
@@ -188,7 +188,7 @@ async function runDeploy(options) {
|
|
|
188
188
|
if (error instanceof GitDirectoryNotCleanError) {
|
|
189
189
|
isCleanGit = false;
|
|
190
190
|
}
|
|
191
|
-
if (!
|
|
191
|
+
if (!force && !isCleanGit) {
|
|
192
192
|
let errorMessage = "Uncommitted changes detected";
|
|
193
193
|
let changedFiles = void 0;
|
|
194
194
|
const nextSteps = [
|
|
@@ -355,11 +355,11 @@ ${changedFiles.trimEnd()}`;
|
|
|
355
355
|
workerDir = workerDirFlag ?? workerDir;
|
|
356
356
|
} else {
|
|
357
357
|
const viteConfig = await getViteConfig(root, ssrEntry).catch(() => null);
|
|
358
|
-
const outputDirs =
|
|
358
|
+
const outputDirs = resolveDeploymentOutputDirs({
|
|
359
359
|
root,
|
|
360
|
-
viteConfig,
|
|
361
|
-
|
|
362
|
-
|
|
360
|
+
viteOutputDirs: viteConfig,
|
|
361
|
+
assetsDirFlag,
|
|
362
|
+
workerDirFlag
|
|
363
363
|
});
|
|
364
364
|
assetsDir = outputDirs.assetsDir;
|
|
365
365
|
workerDir = outputDirs.workerDir;
|
|
@@ -388,7 +388,7 @@ ${changedFiles.trimEnd()}`;
|
|
|
388
388
|
workerDir,
|
|
389
389
|
overriddenEnvironmentVariables
|
|
390
390
|
};
|
|
391
|
-
if (!isCI && !config.defaultEnvironment && (userProvidedEnvironmentTag || userChosenEnvironmentTag)) {
|
|
391
|
+
if (!isCI && !config.defaultEnvironment && (userProvidedEnvironmentTag || userChosenEnvironmentTag) && !force) {
|
|
392
392
|
let chosenEnvironment = findEnvironmentByBranchOrThrow(
|
|
393
393
|
deploymentData.environments,
|
|
394
394
|
config.environmentTag
|
|
@@ -422,6 +422,7 @@ Continue?`.value
|
|
|
422
422
|
}
|
|
423
423
|
);
|
|
424
424
|
let deployError = null;
|
|
425
|
+
let buildError = null;
|
|
425
426
|
let resolveDeploy;
|
|
426
427
|
let rejectDeploy;
|
|
427
428
|
const deployPromise = new Promise((resolve, reject) => {
|
|
@@ -466,21 +467,26 @@ Continue?`.value
|
|
|
466
467
|
config.buildCommand = buildCommand ?? DEFAULT_BUILD_COMMAND;
|
|
467
468
|
} else {
|
|
468
469
|
hooks.buildFunction = async (assetPath) => {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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;
|
|
474
489
|
}
|
|
475
|
-
await runBuild({
|
|
476
|
-
directory: root,
|
|
477
|
-
assetPath,
|
|
478
|
-
lockfileCheck,
|
|
479
|
-
sourcemap: true,
|
|
480
|
-
forceClientSourcemap,
|
|
481
|
-
useCodegen: false,
|
|
482
|
-
entry: ssrEntry
|
|
483
|
-
});
|
|
484
490
|
};
|
|
485
491
|
}
|
|
486
492
|
const uploadStart = async () => {
|
|
@@ -542,27 +548,21 @@ Continue?`.value
|
|
|
542
548
|
}
|
|
543
549
|
resolveDeploy();
|
|
544
550
|
}).catch((error) => {
|
|
545
|
-
rejectDeploy(deployError || error);
|
|
551
|
+
rejectDeploy(deployError || buildError || error);
|
|
546
552
|
});
|
|
547
553
|
return deployPromise;
|
|
548
554
|
}
|
|
549
|
-
|
|
555
|
+
function resolveDeploymentOutputDirs({
|
|
550
556
|
root,
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
557
|
+
viteOutputDirs,
|
|
558
|
+
assetsDirFlag,
|
|
559
|
+
workerDirFlag
|
|
554
560
|
}) {
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
workerDir: "dist/server"
|
|
558
|
-
};
|
|
559
|
-
const viteOutputDirs = viteConfig ? {
|
|
560
|
-
assetsDir: relativePath(root, viteConfig.clientOutDir),
|
|
561
|
-
workerDir: relativePath(root, viteConfig.serverOutDir)
|
|
562
|
-
} : void 0;
|
|
561
|
+
const viteAssetsDir = viteOutputDirs && relativePath(root, viteOutputDirs.clientOutDir);
|
|
562
|
+
const viteWorkerDir = viteOutputDirs && relativePath(root, viteOutputDirs.serverOutDir);
|
|
563
563
|
return {
|
|
564
|
-
assetsDir:
|
|
565
|
-
workerDir:
|
|
564
|
+
assetsDir: assetsDirFlag ?? viteAssetsDir ?? "dist/client",
|
|
565
|
+
workerDir: workerDirFlag ?? viteWorkerDir ?? "dist/server"
|
|
566
566
|
};
|
|
567
567
|
}
|
|
568
568
|
function isHydrogenPreviewVersion(version) {
|
|
@@ -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",
|
|
@@ -3,7 +3,7 @@ import Command from '@shopify/cli-kit/node/base-command';
|
|
|
3
3
|
import { basename } from '@shopify/cli-kit/node/path';
|
|
4
4
|
import { renderSuccess, renderConfirmationPrompt, renderWarning, renderTextPrompt, renderTasks } from '@shopify/cli-kit/node/ui';
|
|
5
5
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
6
|
-
import { commonFlags } from '../../lib/flags.js';
|
|
6
|
+
import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
|
|
7
7
|
import { getStorefronts } from '../../lib/graphql/admin/link-storefront.js';
|
|
8
8
|
import { setStorefront } from '../../lib/shopify-config.js';
|
|
9
9
|
import { createStorefront } from '../../lib/graphql/admin/create-storefront.js';
|
|
@@ -23,28 +23,45 @@ class Link extends Command {
|
|
|
23
23
|
static flags = {
|
|
24
24
|
...commonFlags.force,
|
|
25
25
|
...commonFlags.path,
|
|
26
|
+
...commonFlags.shop,
|
|
26
27
|
storefront: Flags.string({
|
|
27
28
|
description: `The name of a Hydrogen Storefront (e.g. "Jane's Apparel")`,
|
|
28
|
-
env: "SHOPIFY_HYDROGEN_STOREFRONT"
|
|
29
|
+
env: "SHOPIFY_HYDROGEN_STOREFRONT",
|
|
30
|
+
exclusive: ["create-storefront", "name"]
|
|
31
|
+
}),
|
|
32
|
+
"create-storefront": Flags.boolean({
|
|
33
|
+
description: "Create a new Hydrogen storefront.",
|
|
34
|
+
env: "SHOPIFY_HYDROGEN_FLAG_CREATE_STOREFRONT",
|
|
35
|
+
exclusive: ["storefront"]
|
|
36
|
+
}),
|
|
37
|
+
name: Flags.string({
|
|
38
|
+
description: "The name to use when creating a new Hydrogen storefront.",
|
|
39
|
+
env: "SHOPIFY_HYDROGEN_FLAG_NAME",
|
|
40
|
+
exclusive: ["storefront"]
|
|
29
41
|
})
|
|
30
42
|
};
|
|
31
43
|
async run() {
|
|
32
44
|
const { flags } = await this.parse(Link);
|
|
33
|
-
await runLink(flags);
|
|
45
|
+
await runLink(flagsToCamelObject(flags));
|
|
34
46
|
}
|
|
35
47
|
}
|
|
36
48
|
async function runLink({
|
|
49
|
+
createStorefront: flagCreateStorefront,
|
|
37
50
|
force,
|
|
38
51
|
path: root = process.cwd(),
|
|
39
|
-
|
|
52
|
+
shop,
|
|
53
|
+
storefront: flagStorefront,
|
|
54
|
+
name
|
|
40
55
|
}) {
|
|
41
56
|
const [{ session, config }, cliCommand] = await Promise.all([
|
|
42
|
-
login(root),
|
|
57
|
+
login(root, shop),
|
|
43
58
|
getCliCommand()
|
|
44
59
|
]);
|
|
45
60
|
const linkedStore = await linkStorefront(root, session, config, {
|
|
46
61
|
force,
|
|
62
|
+
flagCreateStorefront,
|
|
47
63
|
flagStorefront,
|
|
64
|
+
storefrontName: name,
|
|
48
65
|
cliCommand
|
|
49
66
|
});
|
|
50
67
|
if (!linkedStore) return;
|
|
@@ -61,10 +78,19 @@ async function runLink({
|
|
|
61
78
|
}
|
|
62
79
|
async function linkStorefront(root, session, config, {
|
|
63
80
|
force = false,
|
|
81
|
+
flagCreateStorefront,
|
|
64
82
|
flagStorefront,
|
|
83
|
+
storefrontName,
|
|
65
84
|
cliCommand,
|
|
66
85
|
storefronts
|
|
67
86
|
}) {
|
|
87
|
+
storefrontName = storefrontName?.trim() || void 0;
|
|
88
|
+
if (flagStorefront && (flagCreateStorefront || storefrontName)) {
|
|
89
|
+
throw new AbortError(
|
|
90
|
+
"`--storefront` cannot be used with storefront creation flags.",
|
|
91
|
+
"Use `--storefront` to link an existing storefront or `--name` to create a new one."
|
|
92
|
+
);
|
|
93
|
+
}
|
|
68
94
|
if (!config.shop) {
|
|
69
95
|
throw new AbortError("No shop found in local config, login first.");
|
|
70
96
|
}
|
|
@@ -100,6 +126,13 @@ async function linkStorefront(root, session, config, {
|
|
|
100
126
|
});
|
|
101
127
|
return;
|
|
102
128
|
}
|
|
129
|
+
} else if (flagCreateStorefront || storefrontName) {
|
|
130
|
+
selectedStorefront = await createNewStorefront(
|
|
131
|
+
root,
|
|
132
|
+
session,
|
|
133
|
+
storefronts,
|
|
134
|
+
storefrontName
|
|
135
|
+
);
|
|
103
136
|
} else {
|
|
104
137
|
selectedStorefront = await handleStorefrontSelection(storefronts);
|
|
105
138
|
if (!selectedStorefront) {
|
|
@@ -113,7 +146,7 @@ async function linkStorefront(root, session, config, {
|
|
|
113
146
|
await setStorefront(root, selectedStorefront);
|
|
114
147
|
return selectedStorefront;
|
|
115
148
|
}
|
|
116
|
-
async function createNewStorefront(root, session, storefronts) {
|
|
149
|
+
async function createNewStorefront(root, session, storefronts, storefrontName) {
|
|
117
150
|
const projectDirectory = basename(root);
|
|
118
151
|
let defaultProjectName = titleize(projectDirectory);
|
|
119
152
|
const nameAlreadyUsed = storefronts.some(
|
|
@@ -122,7 +155,7 @@ async function createNewStorefront(root, session, storefronts) {
|
|
|
122
155
|
if (nameAlreadyUsed) {
|
|
123
156
|
defaultProjectName = generateRandomName();
|
|
124
157
|
}
|
|
125
|
-
const projectName = await renderTextPrompt({
|
|
158
|
+
const projectName = storefrontName ?? await renderTextPrompt({
|
|
126
159
|
message: "New storefront name",
|
|
127
160
|
defaultValue: defaultProjectName
|
|
128
161
|
});
|
|
@@ -49,7 +49,7 @@ let CACHED_CHANGELOG = null;
|
|
|
49
49
|
async function runUpgrade({
|
|
50
50
|
appPath,
|
|
51
51
|
version: targetVersion,
|
|
52
|
-
force
|
|
52
|
+
force = false
|
|
53
53
|
}) {
|
|
54
54
|
if (targetVersion === "next") {
|
|
55
55
|
const isInTests = process.env.SHOPIFY_UNIT_TEST === "1";
|
|
@@ -115,7 +115,7 @@ async function runUpgrade({
|
|
|
115
115
|
currentDependencies,
|
|
116
116
|
selectedRelease
|
|
117
117
|
});
|
|
118
|
-
confirmed = await displayConfirmation({
|
|
118
|
+
confirmed = force || await displayConfirmation({
|
|
119
119
|
cumulativeRelease,
|
|
120
120
|
selectedRelease,
|
|
121
121
|
targetVersion
|
|
@@ -125,7 +125,8 @@ async function runUpgrade({
|
|
|
125
125
|
appPath,
|
|
126
126
|
cumulativeRelease,
|
|
127
127
|
currentVersion,
|
|
128
|
-
selectedRelease
|
|
128
|
+
selectedRelease,
|
|
129
|
+
force
|
|
129
130
|
});
|
|
130
131
|
await upgradeNodeModules({
|
|
131
132
|
appPath,
|
|
@@ -880,7 +881,8 @@ async function generateUpgradeInstructionsFile({
|
|
|
880
881
|
appPath,
|
|
881
882
|
cumulativeRelease,
|
|
882
883
|
currentVersion,
|
|
883
|
-
selectedRelease
|
|
884
|
+
selectedRelease,
|
|
885
|
+
force = false
|
|
884
886
|
}) {
|
|
885
887
|
let filename = "";
|
|
886
888
|
const { featuresMd, breakingChangesMd } = cumulativeRelease.features.filter((feature) => feature.steps).reduce(
|
|
@@ -955,7 +957,7 @@ The following packages have been removed as part of this upgrade:
|
|
|
955
957
|
if (!await fileExists(filePath)) {
|
|
956
958
|
await touchFile(filePath);
|
|
957
959
|
} else {
|
|
958
|
-
const overwriteMdFile = await renderConfirmationPrompt({
|
|
960
|
+
const overwriteMdFile = force || await renderConfirmationPrompt({
|
|
959
961
|
message: `A previous upgrade instructions file already exists for this version.
|
|
960
962
|
Do you want to overwrite it?`,
|
|
961
963
|
defaultValue: false
|
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>;
|
|
@@ -175,6 +177,9 @@ declare class Link extends Command {
|
|
|
175
177
|
static description: string;
|
|
176
178
|
static flags: {
|
|
177
179
|
storefront: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
|
|
180
|
+
'create-storefront': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
|
|
181
|
+
name: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
|
|
182
|
+
shop: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
|
|
178
183
|
path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
|
|
179
184
|
force: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
|
|
180
185
|
};
|
|
@@ -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.environments.ssr?.build.outDir ?? resolvedViteConfig.build.outDir;
|
|
42
|
-
const clientOutDir = resolvedViteConfig.environments.client?.build.outDir ?? 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,
|
|
@@ -805,6 +805,24 @@
|
|
|
805
805
|
"hasDynamicHelp": false,
|
|
806
806
|
"multiple": false,
|
|
807
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"
|
|
808
826
|
}
|
|
809
827
|
},
|
|
810
828
|
"hasDynamicHelp": false,
|
|
@@ -1132,13 +1150,47 @@
|
|
|
1132
1150
|
"multiple": false,
|
|
1133
1151
|
"type": "option"
|
|
1134
1152
|
},
|
|
1153
|
+
"shop": {
|
|
1154
|
+
"char": "s",
|
|
1155
|
+
"description": "Shop URL. It can be the shop prefix (janes-apparel) or the full myshopify.com URL (janes-apparel.myshopify.com, https://janes-apparel.myshopify.com).",
|
|
1156
|
+
"env": "SHOPIFY_SHOP",
|
|
1157
|
+
"name": "shop",
|
|
1158
|
+
"hasDynamicHelp": false,
|
|
1159
|
+
"multiple": false,
|
|
1160
|
+
"type": "option"
|
|
1161
|
+
},
|
|
1135
1162
|
"storefront": {
|
|
1136
1163
|
"description": "The name of a Hydrogen Storefront (e.g. \"Jane's Apparel\")",
|
|
1137
1164
|
"env": "SHOPIFY_HYDROGEN_STOREFRONT",
|
|
1165
|
+
"exclusive": [
|
|
1166
|
+
"create-storefront",
|
|
1167
|
+
"name"
|
|
1168
|
+
],
|
|
1138
1169
|
"name": "storefront",
|
|
1139
1170
|
"hasDynamicHelp": false,
|
|
1140
1171
|
"multiple": false,
|
|
1141
1172
|
"type": "option"
|
|
1173
|
+
},
|
|
1174
|
+
"create-storefront": {
|
|
1175
|
+
"description": "Create a new Hydrogen storefront.",
|
|
1176
|
+
"env": "SHOPIFY_HYDROGEN_FLAG_CREATE_STOREFRONT",
|
|
1177
|
+
"exclusive": [
|
|
1178
|
+
"storefront"
|
|
1179
|
+
],
|
|
1180
|
+
"name": "create-storefront",
|
|
1181
|
+
"allowNo": false,
|
|
1182
|
+
"type": "boolean"
|
|
1183
|
+
},
|
|
1184
|
+
"name": {
|
|
1185
|
+
"description": "The name to use when creating a new Hydrogen storefront.",
|
|
1186
|
+
"env": "SHOPIFY_HYDROGEN_FLAG_NAME",
|
|
1187
|
+
"exclusive": [
|
|
1188
|
+
"storefront"
|
|
1189
|
+
],
|
|
1190
|
+
"name": "name",
|
|
1191
|
+
"hasDynamicHelp": false,
|
|
1192
|
+
"multiple": false,
|
|
1193
|
+
"type": "option"
|
|
1142
1194
|
}
|
|
1143
1195
|
},
|
|
1144
1196
|
"hasDynamicHelp": false,
|
|
@@ -1693,5 +1745,5 @@
|
|
|
1693
1745
|
]
|
|
1694
1746
|
}
|
|
1695
1747
|
},
|
|
1696
|
-
"version": "13.0.
|
|
1748
|
+
"version": "13.0.2"
|
|
1697
1749
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
|
-
"version": "13.0.
|
|
7
|
+
"version": "13.0.2",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"repository": {
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"@react-router/dev": "~7.16.0",
|
|
59
59
|
"graphql-config": "^5.0.3",
|
|
60
60
|
"vite": "^5.1.0 || ^6.2.0 || ^7.0.0 || ^8.0.0",
|
|
61
|
-
"@shopify/
|
|
62
|
-
"@shopify/
|
|
61
|
+
"@shopify/mini-oxygen": "4.2.0",
|
|
62
|
+
"@shopify/hydrogen-codegen": "0.3.3"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"@graphql-codegen/cli": {
|