@shopify/cli-hydrogen 8.0.2 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/hydrogen/build.js +75 -98
- package/dist/commands/hydrogen/deploy.js +2 -2
- package/dist/commands/hydrogen/dev.js +149 -218
- package/dist/commands/hydrogen/init.js +2 -10
- package/dist/commands/hydrogen/upgrade.js +79 -66
- package/dist/generator-templates/starter/CHANGELOG.md +28 -0
- package/dist/generator-templates/starter/app/routes/products.$handle.tsx +2 -14
- package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +1 -1
- package/dist/generator-templates/starter/package.json +8 -8
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +1 -1
- package/dist/lib/classic-compiler/build.js +132 -0
- package/dist/lib/classic-compiler/dev.js +262 -0
- package/dist/lib/flags.js +1 -1
- package/dist/lib/onboarding/index.js +16 -2
- package/dist/lib/onboarding/setup-template.mocks.js +72 -0
- package/dist/lib/template-diff.js +7 -3
- package/oclif.manifest.json +35 -244
- package/package.json +6 -6
- package/dist/commands/hydrogen/build-vite.js +0 -140
- package/dist/commands/hydrogen/dev-vite.js +0 -228
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# skeleton
|
|
2
2
|
|
|
3
|
+
## 2024.4.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Add JSdoc to `getSelectedProductOptions` utility and cleanup the skeleton implementation ([#2089](https://github.com/Shopify/hydrogen/pull/2089)) by [@juanpprieto](https://github.com/juanpprieto)
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`286589ee`](https://github.com/Shopify/hydrogen/commit/286589ee281c161ad323e3d45a8b9b859aa5b11f), [`6f5061d9`](https://github.com/Shopify/hydrogen/commit/6f5061d9432f749fde7902548894e98c0d3f899c), [`ae262b61`](https://github.com/Shopify/hydrogen/commit/ae262b616127a7173d23a1a38a6e658af3105ce8), [`2c11ca3b`](https://github.com/Shopify/hydrogen/commit/2c11ca3b7a00ccca2b621dbc29abd319f9598cc8), [`b70f9c2c`](https://github.com/Shopify/hydrogen/commit/b70f9c2c3db8e863c65509097454b9ad7c81cd52), [`17528db1`](https://github.com/Shopify/hydrogen/commit/17528db1eb3d1baa001bafe0684b4bce28d2e271), [`58ea9bb0`](https://github.com/Shopify/hydrogen/commit/58ea9bb0f0eee83ff89e34e2f1f6ac3c4999e213)]:
|
|
10
|
+
- @shopify/cli-hydrogen@8.0.4
|
|
11
|
+
- @shopify/hydrogen@2024.4.2
|
|
12
|
+
|
|
13
|
+
## 1.0.10
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Update `@shopify/cli` dependency to avoid React version mismatches in your project: ([#2059](https://github.com/Shopify/hydrogen/pull/2059)) by [@frandiox](https://github.com/frandiox)
|
|
18
|
+
|
|
19
|
+
```diff
|
|
20
|
+
"dependencies": {
|
|
21
|
+
...
|
|
22
|
+
- "@shopify/cli": "3.58.0",
|
|
23
|
+
+ "@shopify/cli": "3.59.2",
|
|
24
|
+
...
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [[`d2bc720b`](https://github.com/Shopify/hydrogen/commit/d2bc720bb5f7cfb5f42617f98ad2dfcd29891f4b)]:
|
|
29
|
+
- @shopify/cli-hydrogen@8.0.3
|
|
30
|
+
|
|
3
31
|
## 1.0.9
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
|
@@ -26,7 +26,7 @@ import type {
|
|
|
26
26
|
} from '@shopify/hydrogen/storefront-api-types';
|
|
27
27
|
import {getVariantUrl} from '~/lib/variants';
|
|
28
28
|
|
|
29
|
-
export const meta: MetaFunction<typeof loader> = ({data
|
|
29
|
+
export const meta: MetaFunction<typeof loader> = ({data}) => {
|
|
30
30
|
return [{title: `Hydrogen | ${data?.product.title ?? ''}`}];
|
|
31
31
|
};
|
|
32
32
|
|
|
@@ -34,25 +34,13 @@ export async function loader({params, request, context}: LoaderFunctionArgs) {
|
|
|
34
34
|
const {handle} = params;
|
|
35
35
|
const {storefront} = context;
|
|
36
36
|
|
|
37
|
-
const selectedOptions = getSelectedProductOptions(request).filter(
|
|
38
|
-
(option) =>
|
|
39
|
-
// Filter out Shopify predictive search query params
|
|
40
|
-
!option.name.startsWith('_sid') &&
|
|
41
|
-
!option.name.startsWith('_pos') &&
|
|
42
|
-
!option.name.startsWith('_psq') &&
|
|
43
|
-
!option.name.startsWith('_ss') &&
|
|
44
|
-
!option.name.startsWith('_v') &&
|
|
45
|
-
// Filter out third party tracking params
|
|
46
|
-
!option.name.startsWith('fbclid'),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
37
|
if (!handle) {
|
|
50
38
|
throw new Error('Expected product handle to be defined');
|
|
51
39
|
}
|
|
52
40
|
|
|
53
41
|
// await the query for the critical product data
|
|
54
42
|
const {product} = await storefront.query(PRODUCT_QUERY, {
|
|
55
|
-
variables: {handle, selectedOptions},
|
|
43
|
+
variables: {handle, selectedOptions: getSelectedProductOptions(request)},
|
|
56
44
|
});
|
|
57
45
|
|
|
58
46
|
if (!product?.id) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable eslint-comments/disable-enable-pair */
|
|
2
2
|
/* eslint-disable eslint-comments/no-unlimited-disable */
|
|
3
3
|
/* eslint-disable */
|
|
4
|
-
import * as CustomerAccountAPI from '@shopify/hydrogen/customer-account-api-types';
|
|
4
|
+
import type * as CustomerAccountAPI from '@shopify/hydrogen/customer-account-api-types';
|
|
5
5
|
|
|
6
6
|
export type CustomerAddressUpdateMutationVariables = CustomerAccountAPI.Exact<{
|
|
7
7
|
address: CustomerAccountAPI.CustomerAddressInput;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "skeleton",
|
|
3
3
|
"private": true,
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "2024.4.4",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "shopify hydrogen build --codegen",
|
|
@@ -16,22 +16,22 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@remix-run/react": "^2.8.0",
|
|
18
18
|
"@remix-run/server-runtime": "^2.8.0",
|
|
19
|
-
"@shopify/cli": "3.
|
|
20
|
-
"@shopify/cli-hydrogen": "^8.0.
|
|
21
|
-
"@shopify/hydrogen": "2024.4.
|
|
19
|
+
"@shopify/cli": "3.59.2",
|
|
20
|
+
"@shopify/cli-hydrogen": "^8.0.4",
|
|
21
|
+
"@shopify/hydrogen": "2024.4.2",
|
|
22
22
|
"@shopify/remix-oxygen": "^2.0.4",
|
|
23
23
|
"graphql": "^16.6.0",
|
|
24
24
|
"graphql-tag": "^2.12.6",
|
|
25
25
|
"isbot": "^3.8.0",
|
|
26
|
-
"react": "18.2.0",
|
|
27
|
-
"react-dom": "18.2.0"
|
|
26
|
+
"react": "^18.2.0",
|
|
27
|
+
"react-dom": "^18.2.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@graphql-codegen/cli": "5.0.2",
|
|
31
31
|
"@remix-run/dev": "^2.8.0",
|
|
32
32
|
"@remix-run/eslint-config": "^2.8.0",
|
|
33
|
-
"@shopify/hydrogen-codegen": "^0.3.
|
|
34
|
-
"@shopify/mini-oxygen": "^3.0.
|
|
33
|
+
"@shopify/hydrogen-codegen": "^0.3.1",
|
|
34
|
+
"@shopify/mini-oxygen": "^3.0.2",
|
|
35
35
|
"@shopify/oxygen-workers-types": "^4.0.0",
|
|
36
36
|
"@shopify/prettier-config": "^1.1.2",
|
|
37
37
|
"@total-typescript/ts-reset": "^0.4.2",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable eslint-comments/disable-enable-pair */
|
|
2
2
|
/* eslint-disable eslint-comments/no-unlimited-disable */
|
|
3
3
|
/* eslint-disable */
|
|
4
|
-
import * as StorefrontAPI from '@shopify/hydrogen/storefront-api-types';
|
|
4
|
+
import type * as StorefrontAPI from '@shopify/hydrogen/storefront-api-types';
|
|
5
5
|
|
|
6
6
|
export type MoneyFragment = Pick<
|
|
7
7
|
StorefrontAPI.MoneyV2,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { outputInfo, outputContent, outputToken, outputWarn } from '@shopify/cli-kit/node/output';
|
|
2
|
+
import { rmdir, fileSize, glob, readFile, writeFile, fileExists, copyFile } from '@shopify/cli-kit/node/fs';
|
|
3
|
+
import { relativePath, joinPath } from '@shopify/cli-kit/node/path';
|
|
4
|
+
import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
|
|
5
|
+
import colors from '@shopify/cli-kit/node/colors';
|
|
6
|
+
import { getProjectPaths, getRemixConfig, handleRemixImportFail, assertOxygenChecks } from '../remix-config.js';
|
|
7
|
+
import { checkLockfileStatus } from '../check-lockfile.js';
|
|
8
|
+
import { findMissingRoutes } from '../missing-routes.js';
|
|
9
|
+
import { muteRemixLogs, createRemixLogger } from '../log.js';
|
|
10
|
+
import { codegen } from '../codegen.js';
|
|
11
|
+
import { buildBundleAnalysis, getBundleAnalysisSummary } from '../bundle/analyzer.js';
|
|
12
|
+
import { isCI } from '../is-ci.js';
|
|
13
|
+
|
|
14
|
+
const LOG_WORKER_BUILT = "\u{1F4E6} Worker built";
|
|
15
|
+
const WORKER_BUILD_SIZE_LIMIT = 5;
|
|
16
|
+
async function runClassicCompilerBuild({
|
|
17
|
+
directory,
|
|
18
|
+
useCodegen = false,
|
|
19
|
+
codegenConfigPath,
|
|
20
|
+
sourcemap = false,
|
|
21
|
+
disableRouteWarning = false,
|
|
22
|
+
bundleStats = true,
|
|
23
|
+
lockfileCheck = true,
|
|
24
|
+
assetPath
|
|
25
|
+
}) {
|
|
26
|
+
if (!process.env.NODE_ENV) {
|
|
27
|
+
process.env.NODE_ENV = "production";
|
|
28
|
+
}
|
|
29
|
+
if (assetPath) {
|
|
30
|
+
process.env.HYDROGEN_ASSET_BASE_URL = assetPath;
|
|
31
|
+
}
|
|
32
|
+
const { root, buildPath, buildPathClient, buildPathWorkerFile, publicPath } = getProjectPaths(directory);
|
|
33
|
+
if (lockfileCheck) {
|
|
34
|
+
await checkLockfileStatus(root, isCI());
|
|
35
|
+
}
|
|
36
|
+
await muteRemixLogs();
|
|
37
|
+
console.time(LOG_WORKER_BUILT);
|
|
38
|
+
outputInfo(`
|
|
39
|
+
\u{1F3D7}\uFE0F Building in ${process.env.NODE_ENV} mode...`);
|
|
40
|
+
const [remixConfig, [{ build }, { logThrown }, { createFileWatchCache }]] = await Promise.all([
|
|
41
|
+
getRemixConfig(root),
|
|
42
|
+
Promise.all([
|
|
43
|
+
import('@remix-run/dev/dist/compiler/build.js'),
|
|
44
|
+
import('@remix-run/dev/dist/compiler/utils/log.js'),
|
|
45
|
+
import('@remix-run/dev/dist/compiler/fileWatchCache.js')
|
|
46
|
+
]).catch(handleRemixImportFail),
|
|
47
|
+
rmdir(buildPath, { force: true })
|
|
48
|
+
]);
|
|
49
|
+
assertOxygenChecks(remixConfig);
|
|
50
|
+
await Promise.all([
|
|
51
|
+
copyPublicFiles(publicPath, buildPathClient),
|
|
52
|
+
build({
|
|
53
|
+
config: remixConfig,
|
|
54
|
+
options: {
|
|
55
|
+
mode: process.env.NODE_ENV,
|
|
56
|
+
sourcemap
|
|
57
|
+
},
|
|
58
|
+
logger: createRemixLogger(),
|
|
59
|
+
fileWatchCache: createFileWatchCache()
|
|
60
|
+
}).catch((thrown) => {
|
|
61
|
+
logThrown(thrown);
|
|
62
|
+
if (process.env.SHOPIFY_UNIT_TEST) {
|
|
63
|
+
throw thrown;
|
|
64
|
+
} else {
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}),
|
|
68
|
+
useCodegen && codegen({ ...remixConfig, configFilePath: codegenConfigPath })
|
|
69
|
+
]);
|
|
70
|
+
if (process.env.NODE_ENV !== "development") {
|
|
71
|
+
console.timeEnd(LOG_WORKER_BUILT);
|
|
72
|
+
const bundleAnalysisPath = await buildBundleAnalysis(buildPath);
|
|
73
|
+
const sizeMB = await fileSize(buildPathWorkerFile) / (1024 * 1024);
|
|
74
|
+
const formattedSize = colors.yellow(sizeMB.toFixed(2) + " MB");
|
|
75
|
+
outputInfo(
|
|
76
|
+
outputContent` ${colors.dim(
|
|
77
|
+
relativePath(root, buildPathWorkerFile)
|
|
78
|
+
)} ${bundleAnalysisPath ? outputToken.link(formattedSize, bundleAnalysisPath) : formattedSize}\n`
|
|
79
|
+
);
|
|
80
|
+
if (bundleStats && bundleAnalysisPath) {
|
|
81
|
+
outputInfo(
|
|
82
|
+
outputContent`${await getBundleAnalysisSummary(buildPathWorkerFile) || "\n"}\n │\n └─── ${outputToken.link(
|
|
83
|
+
"Complete analysis: " + bundleAnalysisPath,
|
|
84
|
+
bundleAnalysisPath
|
|
85
|
+
)}\n\n`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (sizeMB >= WORKER_BUILD_SIZE_LIMIT) {
|
|
89
|
+
outputWarn(
|
|
90
|
+
`\u{1F6A8} Smaller worker bundles are faster to deploy and run.${remixConfig.serverMinify ? "" : "\n Minify your bundle by adding `serverMinify: true` to remix.config.js."}
|
|
91
|
+
Learn more about optimizing your worker bundle file: https://h2o.fyi/debugging/bundle-size
|
|
92
|
+
`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!disableRouteWarning) {
|
|
97
|
+
const missingRoutes = findMissingRoutes(remixConfig);
|
|
98
|
+
if (missingRoutes.length) {
|
|
99
|
+
const packageManager = await getPackageManager(root);
|
|
100
|
+
const exec = packageManager === "npm" ? "npx" : packageManager;
|
|
101
|
+
outputWarn(
|
|
102
|
+
`Heads up: Shopify stores have a number of standard routes that aren\u2019t set up yet.
|
|
103
|
+
Some functionality and backlinks might not work as expected until these are created or redirects are set up.
|
|
104
|
+
This build is missing ${missingRoutes.length} route${missingRoutes.length > 1 ? "s" : ""}. For more details, run \`${exec} shopify hydrogen check routes\`.
|
|
105
|
+
`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (process.env.NODE_ENV !== "development") {
|
|
110
|
+
await cleanClientSourcemaps(buildPathClient);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function cleanClientSourcemaps(buildPathClient) {
|
|
114
|
+
const bundleFiles = await glob(joinPath(buildPathClient, "**/*.js"));
|
|
115
|
+
await Promise.all(
|
|
116
|
+
bundleFiles.map(async (filePath) => {
|
|
117
|
+
const file = await readFile(filePath);
|
|
118
|
+
return await writeFile(
|
|
119
|
+
filePath,
|
|
120
|
+
file.replace(/\/\/# sourceMappingURL=.+\.js\.map$/gm, "")
|
|
121
|
+
);
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
async function copyPublicFiles(publicPath, buildPathClient) {
|
|
126
|
+
if (!await fileExists(publicPath)) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
return copyFile(publicPath, buildPathClient);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export { copyPublicFiles, runClassicCompilerBuild };
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { outputInfo, outputDebug } from '@shopify/cli-kit/node/output';
|
|
3
|
+
import { fileExists } from '@shopify/cli-kit/node/fs';
|
|
4
|
+
import { renderFatalError } from '@shopify/cli-kit/node/ui';
|
|
5
|
+
import { relativePath, resolvePath } from '@shopify/cli-kit/node/path';
|
|
6
|
+
import { copyPublicFiles } from './build.js';
|
|
7
|
+
import { getProjectPaths, assertOxygenChecks, handleRemixImportFail, getRemixConfig } from '../remix-config.js';
|
|
8
|
+
import { setH2OVerbose, isH2Verbose, muteDevLogs, createRemixLogger, enhanceH2Logs } from '../log.js';
|
|
9
|
+
import { DEFAULT_APP_PORT } from '../flags.js';
|
|
10
|
+
import { buildAssetsUrl, startMiniOxygen } from '../mini-oxygen/index.js';
|
|
11
|
+
import { addVirtualRoutes } from '../virtual-routes.js';
|
|
12
|
+
import { spawnCodegenProcess } from '../codegen.js';
|
|
13
|
+
import { getAllEnvironmentVariables } from '../environment-variables.js';
|
|
14
|
+
import { setupLiveReload } from '../live-reload.js';
|
|
15
|
+
import { checkRemixVersions } from '../remix-version-check.js';
|
|
16
|
+
import { displayDevUpgradeNotice } from '../../commands/hydrogen/upgrade.js';
|
|
17
|
+
import { findPort } from '../find-port.js';
|
|
18
|
+
import { getDevConfigInBackground, startTunnelAndPushConfig, isMockShop, notifyIssueWithTunnelAndMockShop } from '../dev-shared.js';
|
|
19
|
+
import { getCliCommand } from '../shell.js';
|
|
20
|
+
|
|
21
|
+
const LOG_REBUILDING = "\u{1F9F1} Rebuilding...";
|
|
22
|
+
const LOG_REBUILT = "\u{1F680} Rebuilt";
|
|
23
|
+
async function runClassicCompilerDev({
|
|
24
|
+
port: appPort,
|
|
25
|
+
path: appPath,
|
|
26
|
+
codegen: useCodegen = false,
|
|
27
|
+
legacyRuntime = false,
|
|
28
|
+
codegenConfigPath,
|
|
29
|
+
disableVirtualRoutes,
|
|
30
|
+
env: envHandle,
|
|
31
|
+
envBranch,
|
|
32
|
+
debug = false,
|
|
33
|
+
sourcemap = true,
|
|
34
|
+
disableVersionCheck = false,
|
|
35
|
+
inspectorPort,
|
|
36
|
+
customerAccountPush: customerAccountPushFlag = false,
|
|
37
|
+
shouldLiveReload = true,
|
|
38
|
+
cliConfig,
|
|
39
|
+
verbose
|
|
40
|
+
}) {
|
|
41
|
+
if (!process.env.NODE_ENV)
|
|
42
|
+
process.env.NODE_ENV = "development";
|
|
43
|
+
if (verbose)
|
|
44
|
+
setH2OVerbose();
|
|
45
|
+
if (!isH2Verbose())
|
|
46
|
+
muteDevLogs();
|
|
47
|
+
const { root, publicPath, buildPathClient, buildPathWorkerFile } = getProjectPaths(appPath);
|
|
48
|
+
const copyFilesPromise = copyPublicFiles(publicPath, buildPathClient);
|
|
49
|
+
const cliCommandPromise = getCliCommand(root);
|
|
50
|
+
const reloadConfig = async () => {
|
|
51
|
+
const config = await getRemixConfig(root);
|
|
52
|
+
return disableVirtualRoutes ? config : addVirtualRoutes(config).catch((error) => {
|
|
53
|
+
outputDebug(
|
|
54
|
+
"Could not add virtual routes: " + (error?.stack ?? error?.message ?? error)
|
|
55
|
+
);
|
|
56
|
+
return config;
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const getFilePaths = (file) => {
|
|
60
|
+
const fileRelative = relativePath(root, file);
|
|
61
|
+
return [fileRelative, resolvePath(root, fileRelative)];
|
|
62
|
+
};
|
|
63
|
+
const serverBundleExists = () => fileExists(buildPathWorkerFile);
|
|
64
|
+
if (!appPort) {
|
|
65
|
+
appPort = await findPort(DEFAULT_APP_PORT);
|
|
66
|
+
}
|
|
67
|
+
const assetsPort = legacyRuntime ? 0 : await findPort(appPort + 100);
|
|
68
|
+
if (assetsPort) {
|
|
69
|
+
process.env.HYDROGEN_ASSET_BASE_URL = await buildAssetsUrl(assetsPort);
|
|
70
|
+
}
|
|
71
|
+
const backgroundPromise = getDevConfigInBackground(
|
|
72
|
+
root,
|
|
73
|
+
customerAccountPushFlag
|
|
74
|
+
);
|
|
75
|
+
const tunnelPromise = cliConfig && backgroundPromise.then(({ customerAccountPush, storefrontId }) => {
|
|
76
|
+
if (customerAccountPush) {
|
|
77
|
+
return startTunnelAndPushConfig(
|
|
78
|
+
root,
|
|
79
|
+
cliConfig,
|
|
80
|
+
appPort,
|
|
81
|
+
storefrontId
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const remixConfig = await reloadConfig();
|
|
86
|
+
assertOxygenChecks(remixConfig);
|
|
87
|
+
const envPromise = backgroundPromise.then(
|
|
88
|
+
({ fetchRemote, localVariables }) => getAllEnvironmentVariables({
|
|
89
|
+
root,
|
|
90
|
+
fetchRemote,
|
|
91
|
+
envBranch,
|
|
92
|
+
envHandle,
|
|
93
|
+
localVariables
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
const [{ watch }, { createFileWatchCache }] = await Promise.all([
|
|
97
|
+
import('@remix-run/dev/dist/compiler/watch.js'),
|
|
98
|
+
import('@remix-run/dev/dist/compiler/fileWatchCache.js')
|
|
99
|
+
]).catch(handleRemixImportFail);
|
|
100
|
+
let isInitialBuild = true;
|
|
101
|
+
let initialBuildDurationMs = 0;
|
|
102
|
+
let initialBuildStartTimeMs = Date.now();
|
|
103
|
+
const liveReload = shouldLiveReload ? await setupLiveReload(remixConfig.dev?.port ?? 8002) : void 0;
|
|
104
|
+
let miniOxygen;
|
|
105
|
+
let codegenProcess;
|
|
106
|
+
async function safeStartMiniOxygen() {
|
|
107
|
+
if (miniOxygen)
|
|
108
|
+
return;
|
|
109
|
+
const { allVariables, logInjectedVariables } = await envPromise;
|
|
110
|
+
miniOxygen = await startMiniOxygen(
|
|
111
|
+
{
|
|
112
|
+
root,
|
|
113
|
+
debug,
|
|
114
|
+
appPort,
|
|
115
|
+
assetsPort,
|
|
116
|
+
inspectorPort,
|
|
117
|
+
watch: !liveReload,
|
|
118
|
+
buildPathWorkerFile,
|
|
119
|
+
buildPathClient,
|
|
120
|
+
env: allVariables
|
|
121
|
+
},
|
|
122
|
+
legacyRuntime
|
|
123
|
+
);
|
|
124
|
+
logInjectedVariables();
|
|
125
|
+
const host = (await tunnelPromise)?.host ?? miniOxygen.listeningAt;
|
|
126
|
+
const cliCommand = await cliCommandPromise;
|
|
127
|
+
enhanceH2Logs({ host, cliCommand, ...remixConfig });
|
|
128
|
+
const { storefrontTitle } = await backgroundPromise;
|
|
129
|
+
miniOxygen.showBanner({
|
|
130
|
+
appName: storefrontTitle,
|
|
131
|
+
headlinePrefix: initialBuildDurationMs > 0 ? `Initial build: ${initialBuildDurationMs}ms
|
|
132
|
+
` : "",
|
|
133
|
+
host
|
|
134
|
+
});
|
|
135
|
+
if (useCodegen) {
|
|
136
|
+
codegenProcess = spawnCodegenProcess({
|
|
137
|
+
...remixConfig,
|
|
138
|
+
configFilePath: codegenConfigPath
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
checkRemixVersions();
|
|
142
|
+
if (!disableVersionCheck) {
|
|
143
|
+
displayDevUpgradeNotice({ targetPath: appPath });
|
|
144
|
+
}
|
|
145
|
+
if (customerAccountPushFlag && isMockShop(allVariables)) {
|
|
146
|
+
notifyIssueWithTunnelAndMockShop(cliCommand);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const fileWatchCache = createFileWatchCache();
|
|
150
|
+
let skipRebuildLogs = false;
|
|
151
|
+
const closeWatcher = await watch(
|
|
152
|
+
{
|
|
153
|
+
config: remixConfig,
|
|
154
|
+
options: {
|
|
155
|
+
mode: process.env.NODE_ENV,
|
|
156
|
+
sourcemap
|
|
157
|
+
},
|
|
158
|
+
fileWatchCache,
|
|
159
|
+
logger: createRemixLogger()
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
reloadConfig,
|
|
163
|
+
onBuildStart(ctx) {
|
|
164
|
+
if (!isInitialBuild && !skipRebuildLogs) {
|
|
165
|
+
outputInfo(LOG_REBUILDING);
|
|
166
|
+
console.time(LOG_REBUILT);
|
|
167
|
+
}
|
|
168
|
+
liveReload?.onBuildStart(ctx);
|
|
169
|
+
},
|
|
170
|
+
onBuildManifest: liveReload?.onBuildManifest,
|
|
171
|
+
async onBuildFinish(context, duration, succeeded) {
|
|
172
|
+
if (isInitialBuild) {
|
|
173
|
+
await copyFilesPromise;
|
|
174
|
+
initialBuildDurationMs = Date.now() - initialBuildStartTimeMs;
|
|
175
|
+
isInitialBuild = false;
|
|
176
|
+
} else if (!skipRebuildLogs) {
|
|
177
|
+
skipRebuildLogs = false;
|
|
178
|
+
console.timeEnd(LOG_REBUILT);
|
|
179
|
+
if (!miniOxygen)
|
|
180
|
+
console.log("");
|
|
181
|
+
}
|
|
182
|
+
if (!miniOxygen && !await serverBundleExists()) {
|
|
183
|
+
return renderFatalError({
|
|
184
|
+
name: "BuildError",
|
|
185
|
+
type: 0,
|
|
186
|
+
message: "MiniOxygen cannot start because the server bundle has not been generated.",
|
|
187
|
+
skipOclifErrorHandling: true,
|
|
188
|
+
tryMessage: "This is likely due to an error in your app and Remix is unable to compile. Try fixing the app and MiniOxygen will start."
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
if (succeeded) {
|
|
192
|
+
if (!miniOxygen) {
|
|
193
|
+
await safeStartMiniOxygen();
|
|
194
|
+
} else if (liveReload) {
|
|
195
|
+
await miniOxygen.reload();
|
|
196
|
+
}
|
|
197
|
+
liveReload?.onAppReady(context);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
async onFileCreated(file) {
|
|
201
|
+
const [relative, absolute] = getFilePaths(file);
|
|
202
|
+
outputInfo(`
|
|
203
|
+
\u{1F4C4} File created: ${relative}`);
|
|
204
|
+
if (absolute.startsWith(publicPath)) {
|
|
205
|
+
await copyPublicFiles(
|
|
206
|
+
absolute,
|
|
207
|
+
absolute.replace(publicPath, buildPathClient)
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
async onFileChanged(file) {
|
|
212
|
+
fileWatchCache.invalidateFile(file);
|
|
213
|
+
const [relative, absolute] = getFilePaths(file);
|
|
214
|
+
outputInfo(`
|
|
215
|
+
\u{1F4C4} File changed: ${relative}`);
|
|
216
|
+
if (relative.endsWith(".env")) {
|
|
217
|
+
skipRebuildLogs = true;
|
|
218
|
+
const { fetchRemote } = await backgroundPromise;
|
|
219
|
+
const { allVariables, logInjectedVariables } = await getAllEnvironmentVariables({
|
|
220
|
+
root,
|
|
221
|
+
fetchRemote,
|
|
222
|
+
envBranch,
|
|
223
|
+
envHandle
|
|
224
|
+
});
|
|
225
|
+
logInjectedVariables();
|
|
226
|
+
await miniOxygen.reload({
|
|
227
|
+
env: allVariables
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (absolute.startsWith(publicPath)) {
|
|
231
|
+
await copyPublicFiles(
|
|
232
|
+
absolute,
|
|
233
|
+
absolute.replace(publicPath, buildPathClient)
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
async onFileDeleted(file) {
|
|
238
|
+
fileWatchCache.invalidateFile(file);
|
|
239
|
+
const [relative, absolute] = getFilePaths(file);
|
|
240
|
+
outputInfo(`
|
|
241
|
+
\u{1F4C4} File deleted: ${relative}`);
|
|
242
|
+
if (absolute.startsWith(publicPath)) {
|
|
243
|
+
await fs.unlink(absolute.replace(publicPath, buildPathClient));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
return {
|
|
249
|
+
getUrl: () => miniOxygen.listeningAt,
|
|
250
|
+
async close() {
|
|
251
|
+
codegenProcess?.removeAllListeners("close");
|
|
252
|
+
codegenProcess?.kill("SIGINT");
|
|
253
|
+
await Promise.allSettled([
|
|
254
|
+
closeWatcher(),
|
|
255
|
+
miniOxygen?.close(),
|
|
256
|
+
Promise.resolve(tunnelPromise).then((tunnel) => tunnel?.cleanup?.())
|
|
257
|
+
]);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export { runClassicCompilerDev };
|
package/dist/lib/flags.js
CHANGED
|
@@ -23,7 +23,7 @@ const commonFlags = {
|
|
|
23
23
|
legacyRuntime: {
|
|
24
24
|
"legacy-runtime": Flags.boolean({
|
|
25
25
|
description: "Runs the app in a Node.js sandbox instead of an Oxygen worker.",
|
|
26
|
-
env: "
|
|
26
|
+
env: "SHOPIFY_HYDROGEN_FLAG_LEGACY_RUNTIME"
|
|
27
27
|
})
|
|
28
28
|
},
|
|
29
29
|
force: {
|
|
@@ -1,2 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { AbortController } from '@shopify/cli-kit/node/abort';
|
|
2
|
+
import { setupLocalStarterTemplate } from './local.js';
|
|
3
|
+
import { setupRemoteTemplate } from './remote.js';
|
|
4
|
+
|
|
5
|
+
async function setupTemplate(options) {
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
try {
|
|
8
|
+
const template = options.template;
|
|
9
|
+
return template ? await setupRemoteTemplate({ ...options, template }, controller) : await setupLocalStarterTemplate(options, controller);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
controller.abort();
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { setupTemplate };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { vi } from 'vitest';
|
|
3
|
+
import { remove, createSymlink } from 'fs-extra/esm';
|
|
4
|
+
import { writeFile } from '@shopify/cli-kit/node/fs';
|
|
5
|
+
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
6
|
+
import { getRepoNodeModules } from '../build.js';
|
|
7
|
+
|
|
8
|
+
const { renderTasksHook } = vi.hoisted(() => ({ renderTasksHook: vi.fn() }));
|
|
9
|
+
vi.mock("../template-downloader.js", async () => ({
|
|
10
|
+
downloadMonorepoTemplates: () => Promise.resolve({
|
|
11
|
+
version: "",
|
|
12
|
+
templatesDir: fileURLToPath(
|
|
13
|
+
new URL("../../../../../templates", import.meta.url)
|
|
14
|
+
),
|
|
15
|
+
examplesDir: fileURLToPath(
|
|
16
|
+
new URL("../../../../../examples", import.meta.url)
|
|
17
|
+
)
|
|
18
|
+
}),
|
|
19
|
+
downloadExternalRepo: () => Promise.resolve({
|
|
20
|
+
templateDir: fileURLToPath(
|
|
21
|
+
new URL("../../../../../templates/skeleton", import.meta.url)
|
|
22
|
+
)
|
|
23
|
+
})
|
|
24
|
+
}));
|
|
25
|
+
vi.mock("@shopify/cli-kit/node/ui", async () => {
|
|
26
|
+
const original = await vi.importActual("@shopify/cli-kit/node/ui");
|
|
27
|
+
return {
|
|
28
|
+
...original,
|
|
29
|
+
renderConfirmationPrompt: vi.fn(),
|
|
30
|
+
renderSelectPrompt: vi.fn(),
|
|
31
|
+
renderTextPrompt: vi.fn(),
|
|
32
|
+
renderTasks: vi.fn(async (args) => {
|
|
33
|
+
await original.renderTasks(args);
|
|
34
|
+
renderTasksHook();
|
|
35
|
+
})
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
vi.mock(
|
|
39
|
+
"@shopify/cli-kit/node/node-package-manager",
|
|
40
|
+
async (importOriginal) => {
|
|
41
|
+
const original = await importOriginal();
|
|
42
|
+
return {
|
|
43
|
+
...original,
|
|
44
|
+
getPackageManager: () => Promise.resolve("npm"),
|
|
45
|
+
packageManagerFromUserAgent: () => "npm",
|
|
46
|
+
installNodeModules: vi.fn(async ({ directory }) => {
|
|
47
|
+
renderTasksHook.mockImplementationOnce(async () => {
|
|
48
|
+
await writeFile(`${directory}/package-lock.json`, "{}");
|
|
49
|
+
});
|
|
50
|
+
await remove(joinPath(directory, "node_modules")).catch(() => {
|
|
51
|
+
});
|
|
52
|
+
await createSymlink(
|
|
53
|
+
await getRepoNodeModules(),
|
|
54
|
+
joinPath(directory, "node_modules")
|
|
55
|
+
);
|
|
56
|
+
})
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
vi.mock("./common.js", async (importOriginal) => {
|
|
61
|
+
const original = await importOriginal();
|
|
62
|
+
return Object.keys(original).reduce((acc, item) => {
|
|
63
|
+
const key = item;
|
|
64
|
+
const value = original[key];
|
|
65
|
+
if (typeof value === "function") {
|
|
66
|
+
acc[key] = vi.fn(value);
|
|
67
|
+
} else {
|
|
68
|
+
acc[key] = value;
|
|
69
|
+
}
|
|
70
|
+
return acc;
|
|
71
|
+
}, {});
|
|
72
|
+
});
|
|
@@ -66,9 +66,10 @@ ${colors.dim(
|
|
|
66
66
|
return targetDirectory;
|
|
67
67
|
}
|
|
68
68
|
async function applyTemplateDiff(targetDirectory, diffDirectory, templateDir = getStarterDir()) {
|
|
69
|
-
const diffPkgJson = await
|
|
70
|
-
joinPath(diffDirectory, "package.json")
|
|
71
|
-
|
|
69
|
+
const [diffPkgJson, templatePkgJson] = await Promise.all([
|
|
70
|
+
readAndParsePackageJson(joinPath(diffDirectory, "package.json")),
|
|
71
|
+
readAndParsePackageJson(joinPath(templateDir, "package.json"))
|
|
72
|
+
]);
|
|
72
73
|
const diffOptions = diffPkgJson["h2:diff"] ?? {};
|
|
73
74
|
const createFilter = (re, skipFiles) => (filepath) => {
|
|
74
75
|
const filename = relativePath(templateDir, filepath);
|
|
@@ -88,6 +89,9 @@ async function applyTemplateDiff(targetDirectory, diffDirectory, templateDir = g
|
|
|
88
89
|
await mergePackageJson(diffDirectory, targetDirectory, {
|
|
89
90
|
ignoredKeys: ["h2:diff"],
|
|
90
91
|
onResult: (pkgJson) => {
|
|
92
|
+
if (pkgJson.dependencies && templatePkgJson.dependencies) {
|
|
93
|
+
pkgJson.dependencies["@shopify/cli-hydrogen"] = templatePkgJson.dependencies["@shopify/cli-hydrogen"] ?? "*";
|
|
94
|
+
}
|
|
91
95
|
for (const key of ["build", "dev"]) {
|
|
92
96
|
const scriptLine = pkgJson.scripts?.[key];
|
|
93
97
|
if (pkgJson.scripts?.[key] && typeof scriptLine === "string") {
|