@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.
@@ -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, location}) => {
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": "1.0.9",
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.58.0",
20
- "@shopify/cli-hydrogen": "^8.0.2",
21
- "@shopify/hydrogen": "2024.4.1",
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.0",
34
- "@shopify/mini-oxygen": "^3.0.1",
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: "SHOPIFY_HYDROGEN_FLAG_WORKER"
26
+ env: "SHOPIFY_HYDROGEN_FLAG_LEGACY_RUNTIME"
27
27
  })
28
28
  },
29
29
  force: {
@@ -1,2 +1,16 @@
1
- export { setupLocalStarterTemplate } from './local.js';
2
- export { setupRemoteTemplate } from './remote.js';
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 readAndParsePackageJson(
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") {