@shopify/cli-hydrogen 13.0.4 → 13.0.6
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 +14 -0
- package/dist/assets/hydrogen/starter/package.json +3 -3
- package/dist/commands/hydrogen/env/pull.js +2 -2
- package/dist/commands/hydrogen/init.js +1 -1
- package/dist/lib/onboarding/index.js +1 -1
- package/dist/lib/onboarding/remote.js +12 -21
- package/dist/lib/onboarding/setup-template.mocks.js +1 -9
- package/dist/lib/template-downloader.js +2 -84
- package/oclif.manifest.json +2 -2
- package/package.json +2 -2
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# skeleton
|
|
2
2
|
|
|
3
|
+
## 2026.4.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`27d87085aca32f8b0e8ac0cf9eb4d9e6eb016b99`](https://github.com/Shopify/hydrogen/commit/27d87085aca32f8b0e8ac0cf9eb4d9e6eb016b99)]:
|
|
8
|
+
- @shopify/hydrogen@2026.4.7
|
|
9
|
+
|
|
10
|
+
## 2026.4.7
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [[`76cdc6c538dc680a3f1ce96a6d704d463afde0ce`](https://github.com/Shopify/hydrogen/commit/76cdc6c538dc680a3f1ce96a6d704d463afde0ce), [`3ef7ec23096ad29e701073b35a892f0938a0acf4`](https://github.com/Shopify/hydrogen/commit/3ef7ec23096ad29e701073b35a892f0938a0acf4)]:
|
|
15
|
+
- @shopify/hydrogen@2026.4.6
|
|
16
|
+
|
|
3
17
|
## 2026.4.6
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "skeleton",
|
|
3
3
|
"private": true,
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "2026.4.
|
|
5
|
+
"version": "2026.4.8",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "shopify hydrogen build --codegen",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"prettier": "@shopify/prettier-config",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@shopify/hydrogen": "2026.4.
|
|
17
|
+
"@shopify/hydrogen": "2026.4.7",
|
|
18
18
|
"graphql": "^16.10.0",
|
|
19
19
|
"graphql-tag": "^2.12.6",
|
|
20
20
|
"isbot": "^5.1.22",
|
|
@@ -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.2.
|
|
35
|
+
"@shopify/mini-oxygen": "4.2.3",
|
|
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",
|
|
@@ -20,8 +20,8 @@ function needsQuoting(value) {
|
|
|
20
20
|
}
|
|
21
21
|
function quoteEnvValue(value) {
|
|
22
22
|
if (!needsQuoting(value)) return value;
|
|
23
|
-
|
|
24
|
-
return `"${
|
|
23
|
+
if (!/['\r\n]/.test(value)) return `'${value}'`;
|
|
24
|
+
return `"${value.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}"`;
|
|
25
25
|
}
|
|
26
26
|
class EnvPull extends Command {
|
|
27
27
|
static descriptionWithMarkdown = "Pulls environment variables from the linked Hydrogen storefront and writes them to an `.env` file.";
|
|
@@ -25,7 +25,7 @@ class Init extends Command {
|
|
|
25
25
|
env: "SHOPIFY_HYDROGEN_FLAG_LANGUAGE"
|
|
26
26
|
}),
|
|
27
27
|
template: Flags.string({
|
|
28
|
-
description: "Scaffolds project based on
|
|
28
|
+
description: "Scaffolds project based on a template: `skeleton` (the default), `demo-store` or a URL to a git repository.",
|
|
29
29
|
env: "SHOPIFY_HYDROGEN_FLAG_TEMPLATE"
|
|
30
30
|
}),
|
|
31
31
|
...commonFlags.installDeps,
|
|
@@ -6,7 +6,7 @@ async function setupTemplate(options) {
|
|
|
6
6
|
const controller = new AbortController();
|
|
7
7
|
try {
|
|
8
8
|
const template = options.template;
|
|
9
|
-
return template ? await setupRemoteTemplate({ ...options, template }, controller) : await setupLocalStarterTemplate(options, controller);
|
|
9
|
+
return template && template !== "skeleton" ? await setupRemoteTemplate({ ...options, template }, controller) : await setupLocalStarterTemplate(options, controller);
|
|
10
10
|
} catch (error) {
|
|
11
11
|
controller.abort();
|
|
12
12
|
throw error;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { readdir } from 'node:fs/promises';
|
|
2
1
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
3
2
|
import { copyFile, fileExists } from '@shopify/cli-kit/node/fs';
|
|
4
3
|
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
5
4
|
import { renderTasks, renderInfo } from '@shopify/cli-kit/node/ui';
|
|
6
|
-
import { downloadExternalRepo
|
|
5
|
+
import { downloadExternalRepo } from '../template-downloader.js';
|
|
7
6
|
import { getCliCommand } from '../shell.js';
|
|
8
7
|
import { handleProjectLocation, createAbortHandler, handleLanguage, createInitialCommit, handleDependencies, commitAll, renderProjectReady } from './common.js';
|
|
9
8
|
|
|
@@ -13,7 +12,7 @@ async function setupRemoteTemplate(options, controller) {
|
|
|
13
12
|
const project = await handleProjectLocation({ ...options, controller });
|
|
14
13
|
if (!project) return;
|
|
15
14
|
const abort = createAbortHandler(controller, project);
|
|
16
|
-
const backgroundDownloadPromise = appTemplate.includes("/") ? getExternalTemplate(appTemplate, controller.signal).catch(abort) :
|
|
15
|
+
const backgroundDownloadPromise = appTemplate.includes("/") ? getExternalTemplate(appTemplate, controller.signal).catch(abort) : rejectUnknownTemplate(appTemplate).catch(abort);
|
|
17
16
|
const downloaded = await backgroundDownloadPromise;
|
|
18
17
|
if (controller.signal.aborted) return;
|
|
19
18
|
let backgroundWorkPromise = Promise.resolve().then(async () => {
|
|
@@ -86,26 +85,18 @@ async function getExternalTemplate(appTemplate, signal) {
|
|
|
86
85
|
const { templateDir } = await downloadExternalRepo(appTemplate, signal);
|
|
87
86
|
return { sourcePath: templateDir };
|
|
88
87
|
}
|
|
89
|
-
async function
|
|
90
|
-
const { templatesDir, examplesDir } = await downloadMonorepoTemplates({
|
|
91
|
-
signal
|
|
92
|
-
});
|
|
93
|
-
const skeletonPath = joinPath(templatesDir, "skeleton");
|
|
94
|
-
const templatePath = joinPath(templatesDir, appTemplate);
|
|
95
|
-
const examplePath = joinPath(examplesDir, appTemplate);
|
|
96
|
-
if (await fileExists(templatePath)) {
|
|
97
|
-
return { skeletonPath, sourcePath: templatePath };
|
|
98
|
-
}
|
|
99
|
-
if (await fileExists(examplePath)) {
|
|
100
|
-
return { skeletonPath, sourcePath: examplePath };
|
|
101
|
-
}
|
|
102
|
-
const availableTemplates = (await Promise.all([readdir(examplesDir), readdir(templatesDir)]).catch(
|
|
103
|
-
() => []
|
|
104
|
-
)).flat().filter((name) => name !== "skeleton" && !name.endsWith(".md")).concat("demo-store").sort();
|
|
88
|
+
async function rejectUnknownTemplate(appTemplate) {
|
|
105
89
|
throw new AbortError(
|
|
106
90
|
`Unknown value in \`--template\` flag "${appTemplate}".
|
|
107
|
-
Skip the flag or provide the name of a template or
|
|
108
|
-
|
|
91
|
+
Skip the flag or provide the name of a template or a URL to a git repository.`,
|
|
92
|
+
{
|
|
93
|
+
list: {
|
|
94
|
+
title: "Available templates:",
|
|
95
|
+
// Note: skeleton uses the local starter flow, and demo-store is
|
|
96
|
+
// handled as an external template
|
|
97
|
+
items: ["skeleton", "demo-store"]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
109
100
|
);
|
|
110
101
|
}
|
|
111
102
|
|
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import { rm, symlink } from 'node:fs/promises';
|
|
2
2
|
import { vi } from 'vitest';
|
|
3
3
|
import { writeFile } from '@shopify/cli-kit/node/fs';
|
|
4
|
-
import {
|
|
4
|
+
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
5
5
|
import { getSkeletonSourceDir, getSkeletonNodeModules } from '../build.js';
|
|
6
6
|
|
|
7
7
|
const { renderTasksHook } = vi.hoisted(() => ({ renderTasksHook: vi.fn() }));
|
|
8
8
|
vi.mock("../template-downloader.js", async () => ({
|
|
9
|
-
downloadMonorepoTemplates: () => Promise.resolve({
|
|
10
|
-
version: "",
|
|
11
|
-
templatesDir: dirname(getSkeletonSourceDir()),
|
|
12
|
-
examplesDir: dirname(getSkeletonSourceDir()).replace(
|
|
13
|
-
"templates",
|
|
14
|
-
"examples"
|
|
15
|
-
)
|
|
16
|
-
}),
|
|
17
9
|
downloadExternalRepo: () => Promise.resolve({
|
|
18
10
|
templateDir: getSkeletonSourceDir()
|
|
19
11
|
})
|
|
@@ -1,92 +1,10 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { pipeline } from 'stream/promises';
|
|
3
|
-
import gunzipMaybe from 'gunzip-maybe';
|
|
4
|
-
import { extract } from 'tar-fs';
|
|
5
|
-
import { fetch } from '@shopify/cli-kit/node/http';
|
|
6
1
|
import { parseGitHubRepositoryURL } from '@shopify/cli-kit/node/github';
|
|
7
2
|
import { fileExists, mkdir, rmdir } from '@shopify/cli-kit/node/fs';
|
|
8
3
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
9
|
-
import {
|
|
4
|
+
import { getAssetsDir } from './build.js';
|
|
10
5
|
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
11
6
|
import { downloadGitRepository } from '@shopify/cli-kit/node/git';
|
|
12
7
|
|
|
13
|
-
const REPO_RELEASES_URL = `https://api.github.com/repos/shopify/hydrogen/releases/latest`;
|
|
14
|
-
const getTryMessage = (status) => status === 403 ? `If you are using a VPN, WARP, or similar service, consider disabling it momentarily.` : void 0;
|
|
15
|
-
async function getLatestReleaseDownloadUrl(signal) {
|
|
16
|
-
const response = await fetch(REPO_RELEASES_URL, { signal });
|
|
17
|
-
if (!response.ok || response.status >= 400) {
|
|
18
|
-
throw new AbortError(
|
|
19
|
-
`Failed to fetch the latest release information. Status ${response.status} ${response.statusText.replace(/\.$/, "")}.`,
|
|
20
|
-
getTryMessage(response.status)
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
const release = await response.json();
|
|
24
|
-
return {
|
|
25
|
-
// @shopify/package-name@version => package-name@version
|
|
26
|
-
version: release.name.split("/").pop() ?? release.name,
|
|
27
|
-
url: release.tarball_url
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
async function downloadMonorepoTarball(url, storageDir, signal) {
|
|
31
|
-
const response = await fetch(url, { signal });
|
|
32
|
-
if (!response.ok || response.status >= 400) {
|
|
33
|
-
throw new AbortError(
|
|
34
|
-
`Failed to download the latest release files. Status ${response.status} ${response.statusText}}`,
|
|
35
|
-
getTryMessage(response.status)
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
await pipeline(
|
|
39
|
-
// Download
|
|
40
|
-
response.body,
|
|
41
|
-
// Decompress
|
|
42
|
-
gunzipMaybe(),
|
|
43
|
-
// Unpack
|
|
44
|
-
extract(storageDir, {
|
|
45
|
-
strip: 1,
|
|
46
|
-
filter: (name) => {
|
|
47
|
-
name = name.replace(storageDir, "");
|
|
48
|
-
return !name.startsWith(path.normalize("/templates/")) && !name.startsWith(path.normalize("/examples/"));
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
async function downloadMonorepoTemplates({
|
|
54
|
-
signal
|
|
55
|
-
} = {}) {
|
|
56
|
-
if (isHydrogenMonorepo && process.env.FORCE_TEMPLATES_SOURCE !== "remote") {
|
|
57
|
-
const templatesDir = path.dirname(getSkeletonSourceDir());
|
|
58
|
-
return {
|
|
59
|
-
version: "local",
|
|
60
|
-
templatesDir,
|
|
61
|
-
examplesDir: path.resolve(templatesDir, "..", "examples")
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
try {
|
|
65
|
-
const { version, url } = await getLatestReleaseDownloadUrl(signal);
|
|
66
|
-
const templateStoragePath = await getAssetsDir("internal-templates");
|
|
67
|
-
if (!await fileExists(templateStoragePath)) {
|
|
68
|
-
await mkdir(templateStoragePath);
|
|
69
|
-
}
|
|
70
|
-
const templateStorageVersionPath = path.join(templateStoragePath, version);
|
|
71
|
-
if (!await fileExists(templateStorageVersionPath)) {
|
|
72
|
-
await downloadMonorepoTarball(url, templateStorageVersionPath, signal);
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
version,
|
|
76
|
-
templatesDir: path.join(templateStorageVersionPath, "templates"),
|
|
77
|
-
examplesDir: path.join(templateStorageVersionPath, "examples")
|
|
78
|
-
};
|
|
79
|
-
} catch (e) {
|
|
80
|
-
const error = e;
|
|
81
|
-
throw new AbortError(
|
|
82
|
-
`Could not download Hydrogen templates from GitHub.
|
|
83
|
-
Please check your internet connection and the following error:
|
|
84
|
-
|
|
85
|
-
` + error.message,
|
|
86
|
-
error.tryMessage
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
8
|
async function downloadExternalRepo(appTemplate, signal) {
|
|
91
9
|
const parsed = parseGitHubRepositoryURL(appTemplate);
|
|
92
10
|
if (parsed.isErr()) {
|
|
@@ -113,4 +31,4 @@ async function downloadExternalRepo(appTemplate, signal) {
|
|
|
113
31
|
return { templateDir };
|
|
114
32
|
}
|
|
115
33
|
|
|
116
|
-
export { downloadExternalRepo
|
|
34
|
+
export { downloadExternalRepo };
|
package/oclif.manifest.json
CHANGED
|
@@ -1045,7 +1045,7 @@
|
|
|
1045
1045
|
"type": "option"
|
|
1046
1046
|
},
|
|
1047
1047
|
"template": {
|
|
1048
|
-
"description": "Scaffolds project based on
|
|
1048
|
+
"description": "Scaffolds project based on a template: `skeleton` (the default), `demo-store` or a URL to a git repository.",
|
|
1049
1049
|
"env": "SHOPIFY_HYDROGEN_FLAG_TEMPLATE",
|
|
1050
1050
|
"name": "template",
|
|
1051
1051
|
"hasDynamicHelp": false,
|
|
@@ -1751,5 +1751,5 @@
|
|
|
1751
1751
|
]
|
|
1752
1752
|
}
|
|
1753
1753
|
},
|
|
1754
|
-
"version": "13.0.
|
|
1754
|
+
"version": "13.0.6"
|
|
1755
1755
|
}
|
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.6",
|
|
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.2.
|
|
62
|
+
"@shopify/mini-oxygen": "4.2.3"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"@graphql-codegen/cli": {
|