@shopify/cli-hydrogen 5.1.0 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/hydrogen/build.js +5 -4
- package/dist/commands/hydrogen/check.js +2 -2
- package/dist/commands/hydrogen/codegen-unstable.js +1 -1
- package/dist/commands/hydrogen/dev.js +12 -6
- package/dist/commands/hydrogen/preview.js +1 -1
- package/dist/commands/hydrogen/setup/css.js +1 -1
- package/dist/commands/hydrogen/setup/markets.js +1 -1
- package/dist/commands/hydrogen/setup.js +1 -1
- package/dist/generator-templates/starter/app/root.tsx +7 -13
- package/dist/generator-templates/starter/app/styles/app.css +1 -1
- package/dist/generator-templates/starter/package.json +6 -6
- package/dist/lib/log.js +32 -1
- package/dist/lib/remix-config.js +135 -0
- package/dist/lib/remix-version-check.js +51 -0
- package/dist/lib/remix-version-check.test.js +38 -0
- package/dist/lib/remix-version-interop.js +2 -2
- package/dist/lib/remix-version-interop.test.js +1 -1
- package/dist/lib/setups/routes/generate.js +1 -1
- package/dist/lib/setups/routes/generate.test.js +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +9 -10
- package/dist/lib/config.js +0 -141
|
@@ -5,11 +5,11 @@ import { rmdir, fileSize, glob, removeFile, copyFile } from '@shopify/cli-kit/no
|
|
|
5
5
|
import { resolvePath, relativePath, joinPath } from '@shopify/cli-kit/node/path';
|
|
6
6
|
import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
|
|
7
7
|
import colors from '@shopify/cli-kit/node/colors';
|
|
8
|
-
import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
|
|
8
|
+
import { getProjectPaths, getRemixConfig, assertOxygenChecks } from '../../lib/remix-config.js';
|
|
9
9
|
import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js';
|
|
10
10
|
import { checkLockfileStatus } from '../../lib/check-lockfile.js';
|
|
11
11
|
import { findMissingRoutes } from '../../lib/missing-routes.js';
|
|
12
|
-
import {
|
|
12
|
+
import { muteRemixLogs, createRemixLogger } from '../../lib/log.js';
|
|
13
13
|
import { codegen } from '../../lib/codegen.js';
|
|
14
14
|
|
|
15
15
|
const LOG_WORKER_BUILT = "\u{1F4E6} Worker built";
|
|
@@ -57,7 +57,7 @@ async function runBuild({
|
|
|
57
57
|
process.env.NODE_ENV = "production";
|
|
58
58
|
}
|
|
59
59
|
const { root, buildPath, buildPathClient, buildPathWorkerFile, publicPath } = getProjectPaths(appPath);
|
|
60
|
-
await checkLockfileStatus(root);
|
|
60
|
+
await Promise.all([checkLockfileStatus(root), muteRemixLogs()]);
|
|
61
61
|
console.time(LOG_WORKER_BUILT);
|
|
62
62
|
outputInfo(`
|
|
63
63
|
\u{1F3D7}\uFE0F Building in ${process.env.NODE_ENV} mode...`);
|
|
@@ -68,15 +68,16 @@ async function runBuild({
|
|
|
68
68
|
import('@remix-run/dev/dist/compiler/fileWatchCache.js'),
|
|
69
69
|
rmdir(buildPath, { force: true })
|
|
70
70
|
]);
|
|
71
|
+
assertOxygenChecks(remixConfig);
|
|
71
72
|
await Promise.all([
|
|
72
73
|
copyPublicFiles(publicPath, buildPathClient),
|
|
73
74
|
build({
|
|
74
75
|
config: remixConfig,
|
|
75
76
|
options: {
|
|
76
77
|
mode: process.env.NODE_ENV,
|
|
77
|
-
onWarning: warnOnce,
|
|
78
78
|
sourcemap
|
|
79
79
|
},
|
|
80
|
+
logger: createRemixLogger(),
|
|
80
81
|
fileWatchCache: createFileWatchCache()
|
|
81
82
|
}).catch((thrown) => {
|
|
82
83
|
logThrown(thrown);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
2
2
|
import { resolvePath } from '@shopify/cli-kit/node/path';
|
|
3
3
|
import { commonFlags } from '../../lib/flags.js';
|
|
4
|
-
import { getRemixConfig } from '../../lib/config.js';
|
|
4
|
+
import { getRemixConfig } from '../../lib/remix-config.js';
|
|
5
5
|
import { logMissingRoutes, findMissingRoutes } from '../../lib/missing-routes.js';
|
|
6
6
|
import { Args } from '@oclif/core';
|
|
7
7
|
|
|
@@ -29,7 +29,7 @@ class GenerateRoute extends Command {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
async function runCheckRoutes({ directory }) {
|
|
32
|
-
const remixConfig = await getRemixConfig(directory
|
|
32
|
+
const remixConfig = await getRemixConfig(directory);
|
|
33
33
|
logMissingRoutes(findMissingRoutes(remixConfig));
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -2,7 +2,7 @@ import path from 'path';
|
|
|
2
2
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
3
3
|
import { renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
4
4
|
import { Flags } from '@oclif/core';
|
|
5
|
-
import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
|
|
5
|
+
import { getProjectPaths, getRemixConfig } from '../../lib/remix-config.js';
|
|
6
6
|
import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
|
|
7
7
|
import { codegen } from '../../lib/codegen.js';
|
|
8
8
|
|
|
@@ -5,8 +5,8 @@ import { fileExists } from '@shopify/cli-kit/node/fs';
|
|
|
5
5
|
import { renderFatalError } from '@shopify/cli-kit/node/ui';
|
|
6
6
|
import colors from '@shopify/cli-kit/node/colors';
|
|
7
7
|
import { copyPublicFiles } from './build.js';
|
|
8
|
-
import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
|
|
9
|
-
import { muteDevLogs,
|
|
8
|
+
import { getProjectPaths, assertOxygenChecks, getRemixConfig } from '../../lib/remix-config.js';
|
|
9
|
+
import { muteDevLogs, muteRemixLogs, createRemixLogger, enhanceH2Logs } from '../../lib/log.js';
|
|
10
10
|
import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js';
|
|
11
11
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
12
12
|
import { Flags } from '@oclif/core';
|
|
@@ -16,6 +16,7 @@ import { addVirtualRoutes } from '../../lib/virtual-routes.js';
|
|
|
16
16
|
import { spawnCodegenProcess } from '../../lib/codegen.js';
|
|
17
17
|
import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
|
|
18
18
|
import { getConfig } from '../../lib/shopify-config.js';
|
|
19
|
+
import { checkRemixVersions } from '../../lib/remix-version-check.js';
|
|
19
20
|
|
|
20
21
|
const LOG_REBUILDING = "\u{1F9F1} Rebuilding...";
|
|
21
22
|
const LOG_REBUILT = "\u{1F680} Rebuilt";
|
|
@@ -67,6 +68,7 @@ async function runDev({
|
|
|
67
68
|
if (!process.env.NODE_ENV)
|
|
68
69
|
process.env.NODE_ENV = "development";
|
|
69
70
|
muteDevLogs();
|
|
71
|
+
await muteRemixLogs();
|
|
70
72
|
if (debug)
|
|
71
73
|
(await import('node:inspector')).open();
|
|
72
74
|
const { root, publicPath, buildPathClient, buildPathWorkerFile } = getProjectPaths(appPath);
|
|
@@ -86,7 +88,11 @@ async function runDev({
|
|
|
86
88
|
return [fileRelative, path.resolve(root, fileRelative)];
|
|
87
89
|
};
|
|
88
90
|
const serverBundleExists = () => fileExists(buildPathWorkerFile);
|
|
89
|
-
const { shop, storefront } = await
|
|
91
|
+
const [remixConfig, { shop, storefront }] = await Promise.all([
|
|
92
|
+
reloadConfig(),
|
|
93
|
+
getConfig(root)
|
|
94
|
+
]);
|
|
95
|
+
assertOxygenChecks(remixConfig);
|
|
90
96
|
const fetchRemote = !!shop && !!storefront?.id;
|
|
91
97
|
const envPromise = getAllEnvironmentVariables({ root, fetchRemote, envBranch });
|
|
92
98
|
const [{ watch }, { createFileWatchCache }] = await Promise.all([
|
|
@@ -120,11 +126,11 @@ View GraphiQL API browser: ${graphiqlUrl}`)]
|
|
|
120
126
|
if (useCodegen) {
|
|
121
127
|
spawnCodegenProcess({ ...remixConfig, configFilePath: codegenConfigPath });
|
|
122
128
|
}
|
|
129
|
+
checkRemixVersions();
|
|
123
130
|
const showUpgrade = await checkingHydrogenVersion;
|
|
124
131
|
if (showUpgrade)
|
|
125
132
|
showUpgrade();
|
|
126
133
|
}
|
|
127
|
-
const remixConfig = await reloadConfig();
|
|
128
134
|
const fileWatchCache = createFileWatchCache();
|
|
129
135
|
let skipRebuildLogs = false;
|
|
130
136
|
await watch(
|
|
@@ -132,10 +138,10 @@ View GraphiQL API browser: ${graphiqlUrl}`)]
|
|
|
132
138
|
config: remixConfig,
|
|
133
139
|
options: {
|
|
134
140
|
mode: process.env.NODE_ENV,
|
|
135
|
-
onWarning: warnOnce,
|
|
136
141
|
sourcemap
|
|
137
142
|
},
|
|
138
|
-
fileWatchCache
|
|
143
|
+
fileWatchCache,
|
|
144
|
+
logger: createRemixLogger()
|
|
139
145
|
},
|
|
140
146
|
{
|
|
141
147
|
reloadConfig,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
2
2
|
import { muteDevLogs } from '../../lib/log.js';
|
|
3
|
-
import { getProjectPaths } from '../../lib/config.js';
|
|
3
|
+
import { getProjectPaths } from '../../lib/remix-config.js';
|
|
4
4
|
import { commonFlags } from '../../lib/flags.js';
|
|
5
5
|
import { startMiniOxygen } from '../../lib/mini-oxygen.js';
|
|
6
6
|
|
|
@@ -4,7 +4,7 @@ import Command from '@shopify/cli-kit/node/base-command';
|
|
|
4
4
|
import { renderTasks, renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
5
5
|
import { getPackageManager, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
|
|
6
6
|
import { Args } from '@oclif/core';
|
|
7
|
-
import { getRemixConfig } from '../../../lib/config.js';
|
|
7
|
+
import { getRemixConfig } from '../../../lib/remix-config.js';
|
|
8
8
|
import { SETUP_CSS_STRATEGIES, renderCssPrompt, setupCssStrategy, CSS_STRATEGY_NAME_MAP } from '../../../lib/setups/css/index.js';
|
|
9
9
|
|
|
10
10
|
class SetupCSS extends Command {
|
|
@@ -3,7 +3,7 @@ import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
|
|
|
3
3
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
4
4
|
import { renderTasks, renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
5
5
|
import { Args } from '@oclif/core';
|
|
6
|
-
import { getRemixConfig } from '../../../lib/config.js';
|
|
6
|
+
import { getRemixConfig } from '../../../lib/remix-config.js';
|
|
7
7
|
import { SETUP_I18N_STRATEGIES, renderI18nPrompt, setupI18nStrategy, I18N_STRATEGY_NAME_MAP } from '../../../lib/setups/i18n/index.js';
|
|
8
8
|
|
|
9
9
|
class SetupMarkets extends Command {
|
|
@@ -5,7 +5,7 @@ import { resolvePath, basename } from '@shopify/cli-kit/node/path';
|
|
|
5
5
|
import { copyFile } from '@shopify/cli-kit/node/fs';
|
|
6
6
|
import { commonFlags, overrideFlag, flagsToCamelObject } from '../../lib/flags.js';
|
|
7
7
|
import { renderI18nPrompt, setupI18nStrategy } from '../../lib/setups/i18n/index.js';
|
|
8
|
-
import { getRemixConfig } from '../../lib/config.js';
|
|
8
|
+
import { getRemixConfig } from '../../lib/remix-config.js';
|
|
9
9
|
import { handleRouteGeneration, generateProjectEntries, handleCliShortcut, renderProjectReady } from '../../lib/onboarding/common.js';
|
|
10
10
|
import { getCliCommand } from '../../lib/shell.js';
|
|
11
11
|
import { generateProjectFile } from '../../lib/setups/routes/generate.js';
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isRouteErrorResponse,
|
|
3
|
-
useCatch,
|
|
4
|
-
useMatches,
|
|
5
|
-
useRouteError,
|
|
6
|
-
} from '@remix-run/react';
|
|
7
|
-
import {
|
|
8
|
-
defer,
|
|
9
|
-
type LoaderArgs,
|
|
10
|
-
type ErrorBoundaryComponent,
|
|
11
|
-
} from '@shopify/remix-oxygen';
|
|
1
|
+
import {defer, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
12
2
|
import {
|
|
13
3
|
Links,
|
|
14
4
|
Meta,
|
|
15
5
|
Outlet,
|
|
16
6
|
Scripts,
|
|
17
|
-
|
|
7
|
+
useCatch,
|
|
8
|
+
useMatches,
|
|
9
|
+
useRouteError,
|
|
18
10
|
useLoaderData,
|
|
11
|
+
ScrollRestoration,
|
|
12
|
+
isRouteErrorResponse,
|
|
19
13
|
} from '@remix-run/react';
|
|
20
14
|
import type {CustomerAccessToken} from '@shopify/hydrogen-react/storefront-api-types';
|
|
21
15
|
import type {HydrogenSession} from '../server';
|
|
@@ -144,7 +138,7 @@ export function ErrorBoundary() {
|
|
|
144
138
|
);
|
|
145
139
|
}
|
|
146
140
|
|
|
147
|
-
export const ErrorBoundaryV1
|
|
141
|
+
export const ErrorBoundaryV1 = ({error}: {error: Error}) => {
|
|
148
142
|
// eslint-disable-next-line no-console
|
|
149
143
|
console.error(error);
|
|
150
144
|
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
},
|
|
14
14
|
"prettier": "@shopify/prettier-config",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@remix-run/react": "1.
|
|
17
|
-
"@shopify/cli": "3.
|
|
18
|
-
"@shopify/cli-hydrogen": "^5.1.
|
|
19
|
-
"@shopify/hydrogen": "^2023.7.
|
|
20
|
-
"@shopify/remix-oxygen": "^1.1.
|
|
16
|
+
"@remix-run/react": "1.19.1",
|
|
17
|
+
"@shopify/cli": "3.48.0",
|
|
18
|
+
"@shopify/cli-hydrogen": "^5.1.1",
|
|
19
|
+
"@shopify/hydrogen": "^2023.7.1",
|
|
20
|
+
"@shopify/remix-oxygen": "^1.1.2",
|
|
21
21
|
"graphql": "^16.6.0",
|
|
22
22
|
"graphql-tag": "^2.12.6",
|
|
23
23
|
"isbot": "^3.6.6",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"react-dom": "^18.2.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@remix-run/dev": "1.
|
|
28
|
+
"@remix-run/dev": "1.19.1",
|
|
29
29
|
"@shopify/oxygen-workers-types": "^3.17.2",
|
|
30
30
|
"@shopify/prettier-config": "^1.1.2",
|
|
31
31
|
"@total-typescript/ts-reset": "^0.4.2",
|
package/dist/lib/log.js
CHANGED
|
@@ -199,5 +199,36 @@ const warnOnce = (string) => {
|
|
|
199
199
|
warnings.add(string);
|
|
200
200
|
}
|
|
201
201
|
};
|
|
202
|
+
function createRemixLogger() {
|
|
203
|
+
const noop = () => {
|
|
204
|
+
};
|
|
205
|
+
const buildMessageBody = (message, details) => `In Remix:
|
|
206
|
+
|
|
207
|
+
` + colors.bold(message) + (details ? "\n\n" + details.join("\n") : "");
|
|
208
|
+
return {
|
|
209
|
+
dev: noop,
|
|
210
|
+
info: noop,
|
|
211
|
+
debug: noop,
|
|
212
|
+
warn: (message, options) => {
|
|
213
|
+
renderWarning({ body: buildMessageBody(message, options?.details) });
|
|
214
|
+
},
|
|
215
|
+
error: (message, options) => {
|
|
216
|
+
renderFatalError({
|
|
217
|
+
name: "error",
|
|
218
|
+
type: 0,
|
|
219
|
+
message: buildMessageBody(message, options?.details),
|
|
220
|
+
tryMessage: ""
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
async function muteRemixLogs() {
|
|
226
|
+
try {
|
|
227
|
+
const { logger } = await import('@remix-run/dev/dist/tux/logger.js');
|
|
228
|
+
logger.warn = logger.debug = logger.info = () => {
|
|
229
|
+
};
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
202
233
|
|
|
203
|
-
export { addMessageReplacers, enhanceH2Logs, muteAuthLogs, muteDevLogs, resetAllLogs, warnOnce };
|
|
234
|
+
export { addMessageReplacers, createRemixLogger, enhanceH2Logs, muteAuthLogs, muteDevLogs, muteRemixLogs, resetAllLogs, warnOnce };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { readdir } from 'node:fs/promises';
|
|
5
|
+
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
6
|
+
import { outputWarn } from '@shopify/cli-kit/node/output';
|
|
7
|
+
import { fileExists } from '@shopify/cli-kit/node/fs';
|
|
8
|
+
|
|
9
|
+
const BUILD_DIR = "dist";
|
|
10
|
+
const CLIENT_SUBDIR = "client";
|
|
11
|
+
const WORKER_SUBDIR = "worker";
|
|
12
|
+
const oxygenServerMainFields = ["browser", "module", "main"];
|
|
13
|
+
function getProjectPaths(appPath, entry) {
|
|
14
|
+
const root = appPath ?? process.cwd();
|
|
15
|
+
const publicPath = path.join(root, "public");
|
|
16
|
+
const buildPath = path.join(root, BUILD_DIR);
|
|
17
|
+
const buildPathClient = path.join(buildPath, CLIENT_SUBDIR);
|
|
18
|
+
const buildPathWorkerFile = path.join(buildPath, WORKER_SUBDIR, "index.js");
|
|
19
|
+
return {
|
|
20
|
+
root,
|
|
21
|
+
buildPath,
|
|
22
|
+
buildPathClient,
|
|
23
|
+
buildPathWorkerFile,
|
|
24
|
+
publicPath
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
async function getRemixConfig(root, mode = process.env.NODE_ENV) {
|
|
28
|
+
const { readConfig } = await import('@remix-run/dev/dist/config.js');
|
|
29
|
+
const config = await readConfig(root, mode);
|
|
30
|
+
if (process.env.LOCAL_DEV) {
|
|
31
|
+
const packagesPath = fileURLToPath(new URL("../../..", import.meta.url));
|
|
32
|
+
config.watchPaths ??= [];
|
|
33
|
+
config.watchPaths.push(
|
|
34
|
+
...(await readdir(packagesPath)).map(
|
|
35
|
+
(pkg) => pkg === "hydrogen-react" ? path.resolve(packagesPath, pkg, "dist", "browser-dev", "index.mjs") : path.resolve(packagesPath, pkg, "dist", "development", "index.js")
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
config.watchPaths.push(
|
|
39
|
+
path.join(packagesPath, "cli", "dist", "virtual-routes", "**", "*")
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return config;
|
|
43
|
+
}
|
|
44
|
+
function assertOxygenChecks(config) {
|
|
45
|
+
try {
|
|
46
|
+
createRequire(import.meta.url).resolve("@shopify/remix-oxygen");
|
|
47
|
+
} catch {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!config.serverEntryPoint) {
|
|
51
|
+
throw new AbortError(
|
|
52
|
+
"Could not find a server entry point.",
|
|
53
|
+
"Please add a server option to your remix.config.js pointing to an Oxygen worker entry file."
|
|
54
|
+
);
|
|
55
|
+
} else {
|
|
56
|
+
assertEntryFileExists(config.rootDirectory, config.serverEntryPoint);
|
|
57
|
+
}
|
|
58
|
+
if (config.serverPlatform !== "neutral") {
|
|
59
|
+
throw new AbortError(
|
|
60
|
+
'The serverPlatform in remix.config.js must be "neutral".'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (config.serverModuleFormat !== "esm") {
|
|
64
|
+
throw new AbortError(
|
|
65
|
+
'The serverModuleFormat in remix.config.js must be "esm".'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (config.serverDependenciesToBundle !== "all") {
|
|
69
|
+
throw new AbortError(
|
|
70
|
+
'The serverDependenciesToBundle in remix.config.js must be "all".'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (!config.serverConditions?.includes("worker")) {
|
|
74
|
+
throw new AbortError(
|
|
75
|
+
'The serverConditions in remix.config.js must include "worker".'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (process.env.NODE_ENV === "development" && !config.serverConditions?.includes("development")) {
|
|
79
|
+
outputWarn(
|
|
80
|
+
"Add `process.env.NODE_ENV` value to serverConditions in remix.config.js to enable debugging features in development."
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (!config.serverMainFields || !oxygenServerMainFields.every((v, i) => config.serverMainFields?.[i] === v)) {
|
|
84
|
+
throw new AbortError(
|
|
85
|
+
`The serverMainFields in remix.config.js must be ${JSON.stringify(
|
|
86
|
+
oxygenServerMainFields
|
|
87
|
+
)}.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const cdnUrl = process.env.HYDROGEN_ASSET_BASE_URL;
|
|
91
|
+
if (cdnUrl && !config.publicPath.startsWith(cdnUrl)) {
|
|
92
|
+
throw new AbortError(
|
|
93
|
+
"The publicPath in remix.config.js must be prepended with the value of `process.env.HYDROGEN_ASSET_BASE_URL`."
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const expectedServerBuildPath = path.join(
|
|
97
|
+
BUILD_DIR,
|
|
98
|
+
WORKER_SUBDIR,
|
|
99
|
+
"index.js"
|
|
100
|
+
);
|
|
101
|
+
if (config.serverBuildPath !== path.resolve(config.rootDirectory, expectedServerBuildPath)) {
|
|
102
|
+
throw new AbortError(
|
|
103
|
+
`The serverBuildPath in remix.config.js must be "${expectedServerBuildPath}".`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
const expectedAssetsBuildDirectory = path.join(BUILD_DIR, CLIENT_SUBDIR);
|
|
107
|
+
if (!config.assetsBuildDirectory.startsWith(
|
|
108
|
+
path.resolve(config.rootDirectory, expectedAssetsBuildDirectory)
|
|
109
|
+
)) {
|
|
110
|
+
throw new AbortError(
|
|
111
|
+
`The assetsBuildDirectory in remix.config.js must be in "${expectedAssetsBuildDirectory}".`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function assertEntryFileExists(root, fileRelative) {
|
|
116
|
+
const fileAbsolute = path.resolve(root, fileRelative);
|
|
117
|
+
const exists = await fileExists(fileAbsolute);
|
|
118
|
+
if (!exists) {
|
|
119
|
+
if (!path.extname(fileAbsolute)) {
|
|
120
|
+
const files = await readdir(path.dirname(fileAbsolute));
|
|
121
|
+
const exists2 = files.some((file) => {
|
|
122
|
+
const { name, ext } = path.parse(file);
|
|
123
|
+
return name === path.basename(fileAbsolute) && /^\.[jt]s$/.test(ext);
|
|
124
|
+
});
|
|
125
|
+
if (exists2)
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
throw new AbortError(
|
|
129
|
+
`Entry file "${fileRelative}" not found.`,
|
|
130
|
+
"Please ensure the file exists and that the path is correctly added to the `server` property in remix.config.js."
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { assertOxygenChecks, getProjectPaths, getRemixConfig };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { renderWarning } from '@shopify/cli-kit/node/ui';
|
|
4
|
+
|
|
5
|
+
function checkRemixVersions() {
|
|
6
|
+
const require2 = createRequire(import.meta.url);
|
|
7
|
+
const hydrogenPkgJson = require2(fileURLToPath(
|
|
8
|
+
new URL("../../package.json", import.meta.url)
|
|
9
|
+
));
|
|
10
|
+
const requiredVersionInHydrogen = hydrogenPkgJson.dependencies["@remix-run/dev"];
|
|
11
|
+
const pkgs = [
|
|
12
|
+
"dev",
|
|
13
|
+
"react",
|
|
14
|
+
"server-runtime",
|
|
15
|
+
"css-bundle",
|
|
16
|
+
"node",
|
|
17
|
+
"express",
|
|
18
|
+
"eslint-config"
|
|
19
|
+
].map((name) => getRemixPackageVersion(require2, name));
|
|
20
|
+
const outOfSyncPkgs = pkgs.filter(
|
|
21
|
+
(pkg) => pkg.version && pkg.version !== requiredVersionInHydrogen
|
|
22
|
+
);
|
|
23
|
+
if (outOfSyncPkgs.length === 0)
|
|
24
|
+
return;
|
|
25
|
+
const items = outOfSyncPkgs.reduce((acc, item) => {
|
|
26
|
+
if (item.version) {
|
|
27
|
+
acc.push(`${item.name}@${item.version}`);
|
|
28
|
+
}
|
|
29
|
+
return acc;
|
|
30
|
+
}, []);
|
|
31
|
+
renderWarning({
|
|
32
|
+
headline: `The current version of Hydrogen requires Remix @${requiredVersionInHydrogen}. The following packages are out of sync:`,
|
|
33
|
+
body: [
|
|
34
|
+
{ list: { items } },
|
|
35
|
+
"\nPlease ensure your Remix packages have the same version and are in sync with Hydrogen."
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function getRemixPackageVersion(require2, name) {
|
|
40
|
+
const pkgName = "@remix-run/" + name;
|
|
41
|
+
const result = { name: pkgName, version: "" };
|
|
42
|
+
try {
|
|
43
|
+
const pkgJsonPath = require2.resolve(`${pkgName}/package.json`);
|
|
44
|
+
const pkgJson = require2(pkgJsonPath);
|
|
45
|
+
result.version = pkgJson.version;
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { checkRemixVersions };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { vi, describe, it, expect } from 'vitest';
|
|
2
|
+
import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
|
|
3
|
+
import { checkRemixVersions } from './remix-version-check.js';
|
|
4
|
+
|
|
5
|
+
const requireMock = vi.fn();
|
|
6
|
+
vi.mock("node:module", async () => {
|
|
7
|
+
const { createRequire } = await vi.importActual(
|
|
8
|
+
"node:module"
|
|
9
|
+
);
|
|
10
|
+
return {
|
|
11
|
+
createRequire: (url) => {
|
|
12
|
+
const actualRequire = createRequire(url);
|
|
13
|
+
requireMock.mockImplementation((mod) => actualRequire(mod));
|
|
14
|
+
const require2 = requireMock;
|
|
15
|
+
require2.resolve = actualRequire.resolve.bind(actualRequire);
|
|
16
|
+
return require2;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
describe("remix-version-check", () => {
|
|
21
|
+
it("does nothing when versions are in sync", () => {
|
|
22
|
+
const outputMock = mockAndCaptureOutput();
|
|
23
|
+
checkRemixVersions();
|
|
24
|
+
expect(outputMock.warn()).toBe("");
|
|
25
|
+
});
|
|
26
|
+
it("warns when versions are out of sync", () => {
|
|
27
|
+
const expectedVersion = "42.0.0-test";
|
|
28
|
+
vi.mocked(requireMock).mockReturnValueOnce({
|
|
29
|
+
dependencies: { "@remix-run/dev": expectedVersion }
|
|
30
|
+
});
|
|
31
|
+
const outputMock = mockAndCaptureOutput();
|
|
32
|
+
checkRemixVersions();
|
|
33
|
+
const output = outputMock.warn();
|
|
34
|
+
expect(output).toMatch(`Hydrogen requires Remix @${expectedVersion}`);
|
|
35
|
+
expect(output).toMatch(`@remix-run/dev@`);
|
|
36
|
+
expect(output).toMatch(`@remix-run/react@`);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
|
-
import { getRemixConfig } from './config.js';
|
|
2
|
+
import { getRemixConfig } from './remix-config.js';
|
|
3
3
|
|
|
4
4
|
function isRemixV2() {
|
|
5
5
|
try {
|
|
@@ -13,7 +13,7 @@ function isRemixV2() {
|
|
|
13
13
|
async function getV2Flags(root, remixConfigFuture) {
|
|
14
14
|
const isV2 = isRemixV2();
|
|
15
15
|
const futureFlags = {
|
|
16
|
-
...!isV2 && (remixConfigFuture ?? (await getRemixConfig(root
|
|
16
|
+
...!isV2 && (remixConfigFuture ?? (await getRemixConfig(root)).future)
|
|
17
17
|
};
|
|
18
18
|
return {
|
|
19
19
|
isV2Meta: isV2 || !!futureFlags.v2_meta,
|
|
@@ -58,7 +58,7 @@ describe("remix-version-interop", () => {
|
|
|
58
58
|
return <div>stuff</div>;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export const ErrorBoundaryV1
|
|
61
|
+
export const ErrorBoundaryV1 = ({error}: {error: Error}) => {
|
|
62
62
|
console.error(error);
|
|
63
63
|
|
|
64
64
|
return <div>There was an error.</div>;
|
|
@@ -7,7 +7,7 @@ import { transpileFile } from '../../../lib/transpile-ts.js';
|
|
|
7
7
|
import { getCodeFormatOptions, formatCode } from '../../../lib/format-code.js';
|
|
8
8
|
import { getTemplateAppFile, GENERATOR_ROUTE_DIR, getStarterDir } from '../../../lib/build.js';
|
|
9
9
|
import { getV2Flags, convertTemplateToRemixVersion, convertRouteToV1 } from '../../../lib/remix-version-interop.js';
|
|
10
|
-
import { getRemixConfig } from '
|
|
10
|
+
import { getRemixConfig } from '../../remix-config.js';
|
|
11
11
|
import { findFileWithExtension } from '../../file.js';
|
|
12
12
|
|
|
13
13
|
const NO_LOCALE_PATTERNS = [/robots\.txt/];
|
|
@@ -5,7 +5,7 @@ import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
|
|
|
5
5
|
import { fileExists, readFile, mkdir, writeFile } from '@shopify/cli-kit/node/fs';
|
|
6
6
|
import { joinPath, dirname } from '@shopify/cli-kit/node/path';
|
|
7
7
|
import { getTemplateAppFile } from '../../../lib/build.js';
|
|
8
|
-
import { getRemixConfig } from '
|
|
8
|
+
import { getRemixConfig } from '../../remix-config.js';
|
|
9
9
|
|
|
10
10
|
const readProjectFile = (dirs, fileBasename, ext = "tsx") => readFile(joinPath(dirs.appDirectory, `${fileBasename}.${ext}`));
|
|
11
11
|
describe("generate/route", () => {
|
|
@@ -13,7 +13,7 @@ describe("generate/route", () => {
|
|
|
13
13
|
vi.resetAllMocks();
|
|
14
14
|
vi.mock("@shopify/cli-kit/node/output");
|
|
15
15
|
vi.mock("@shopify/cli-kit/node/ui");
|
|
16
|
-
vi.mock("../../config.js", async () => ({ getRemixConfig: vi.fn() }));
|
|
16
|
+
vi.mock("../../remix-config.js", async () => ({ getRemixConfig: vi.fn() }));
|
|
17
17
|
});
|
|
18
18
|
describe("generateRoutes", () => {
|
|
19
19
|
it("generates all routes with correct configuration", async () => {
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
|
-
"version": "5.1.
|
|
7
|
+
"version": "5.1.1",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsup --config ./tsup.config.ts && node scripts/build-check.mjs",
|
|
12
|
-
"dev": "tsup --watch --config ./tsup.config.ts",
|
|
11
|
+
"build": "rm -rf dist && tsup --config ./tsup.config.ts && node scripts/build-check.mjs",
|
|
12
|
+
"dev": "rm -rf dist && tsup --watch --config ./tsup.config.ts",
|
|
13
13
|
"typecheck": "tsc --noEmit",
|
|
14
|
-
"generate:manifest": "
|
|
14
|
+
"generate:manifest": "node scripts/generate-manifest.mjs",
|
|
15
15
|
"test": "cross-env SHOPIFY_UNIT_TEST=1 vitest run --test-timeout=15000",
|
|
16
16
|
"test:watch": "cross-env SHOPIFY_UNIT_TEST=1 vitest --test-timeout=15000"
|
|
17
17
|
},
|
|
@@ -22,22 +22,21 @@
|
|
|
22
22
|
"@types/prettier": "^2.7.2",
|
|
23
23
|
"@types/recursive-readdir": "^2.2.1",
|
|
24
24
|
"@types/tar-fs": "^2.0.1",
|
|
25
|
-
"oclif": "3.9.1",
|
|
26
25
|
"tempy": "^3.0.0",
|
|
27
26
|
"type-fest": "^3.6.0",
|
|
28
27
|
"vitest": "^0.33.0"
|
|
29
28
|
},
|
|
30
29
|
"peerDependencies": {
|
|
31
|
-
"@remix-run/react": "
|
|
30
|
+
"@remix-run/react": "1.19.1",
|
|
32
31
|
"@shopify/hydrogen-react": "^2023.7.0",
|
|
33
|
-
"@shopify/remix-oxygen": "^1.1.
|
|
32
|
+
"@shopify/remix-oxygen": "^1.1.2"
|
|
34
33
|
},
|
|
35
34
|
"dependencies": {
|
|
36
35
|
"@ast-grep/napi": "^0.5.3",
|
|
37
36
|
"@graphql-codegen/cli": "3.3.1",
|
|
38
|
-
"@oclif/core": "2.8.
|
|
39
|
-
"@remix-run/dev": "1.
|
|
40
|
-
"@shopify/cli-kit": "3.
|
|
37
|
+
"@oclif/core": "2.8.11",
|
|
38
|
+
"@remix-run/dev": "1.19.1",
|
|
39
|
+
"@shopify/cli-kit": "3.48.0",
|
|
41
40
|
"@shopify/hydrogen-codegen": "^0.0.2",
|
|
42
41
|
"@shopify/mini-oxygen": "^1.7.0",
|
|
43
42
|
"ansi-escapes": "^6.2.0",
|
package/dist/lib/config.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { renderFatalError } from '@shopify/cli-kit/node/ui';
|
|
2
|
-
import { outputWarn } from '@shopify/cli-kit/node/output';
|
|
3
|
-
import { fileExists } from '@shopify/cli-kit/node/fs';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import fs from 'fs/promises';
|
|
7
|
-
|
|
8
|
-
const BUILD_DIR = "dist";
|
|
9
|
-
const CLIENT_SUBDIR = "client";
|
|
10
|
-
const WORKER_SUBDIR = "worker";
|
|
11
|
-
const oxygenServerMainFields = ["browser", "module", "main"];
|
|
12
|
-
function getProjectPaths(appPath, entry) {
|
|
13
|
-
const root = appPath ?? process.cwd();
|
|
14
|
-
const publicPath = path.join(root, "public");
|
|
15
|
-
const buildPath = path.join(root, BUILD_DIR);
|
|
16
|
-
const buildPathClient = path.join(buildPath, CLIENT_SUBDIR);
|
|
17
|
-
const buildPathWorkerFile = path.join(buildPath, WORKER_SUBDIR, "index.js");
|
|
18
|
-
return {
|
|
19
|
-
root,
|
|
20
|
-
buildPath,
|
|
21
|
-
buildPathClient,
|
|
22
|
-
buildPathWorkerFile,
|
|
23
|
-
publicPath
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
async function getRemixConfig(root, skipOxygenChecks = false, mode = process.env.NODE_ENV) {
|
|
27
|
-
const { readConfig } = await import('@remix-run/dev/dist/config.js');
|
|
28
|
-
const config = await readConfig(root, mode);
|
|
29
|
-
if (!skipOxygenChecks) {
|
|
30
|
-
if (!config.serverEntryPoint) {
|
|
31
|
-
throwConfigError(
|
|
32
|
-
"Could not find a server entry point.",
|
|
33
|
-
"Please add a server option to your remix.config.js pointing to an Oxygen worker entry file."
|
|
34
|
-
);
|
|
35
|
-
} else {
|
|
36
|
-
assertEntryFileExists(config.rootDirectory, config.serverEntryPoint);
|
|
37
|
-
}
|
|
38
|
-
if (config.serverPlatform !== "neutral") {
|
|
39
|
-
throwConfigError(
|
|
40
|
-
'The serverPlatform in remix.config.js must be "neutral".'
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
if (config.serverModuleFormat !== "esm") {
|
|
44
|
-
throwConfigError(
|
|
45
|
-
'The serverModuleFormat in remix.config.js must be "esm".'
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
if (config.serverDependenciesToBundle !== "all") {
|
|
49
|
-
throwConfigError(
|
|
50
|
-
'The serverDependenciesToBundle in remix.config.js must be "all".'
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
if (!config.serverConditions?.includes("worker")) {
|
|
54
|
-
throwConfigError(
|
|
55
|
-
'The serverConditions in remix.config.js must include "worker".'
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
if (process.env.NODE_ENV === "development" && !config.serverConditions?.includes("development")) {
|
|
59
|
-
outputWarn(
|
|
60
|
-
"Add `process.env.NODE_ENV` value to serverConditions in remix.config.js to enable debugging features in development."
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
if (!config.serverMainFields || !oxygenServerMainFields.every(
|
|
64
|
-
(v, i) => config.serverMainFields?.[i] === v
|
|
65
|
-
)) {
|
|
66
|
-
throwConfigError(
|
|
67
|
-
`The serverMainFields in remix.config.js must be ${JSON.stringify(
|
|
68
|
-
oxygenServerMainFields
|
|
69
|
-
)}.`
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
const cdnUrl = process.env.HYDROGEN_ASSET_BASE_URL;
|
|
73
|
-
if (cdnUrl && !config.publicPath.startsWith(cdnUrl)) {
|
|
74
|
-
throwConfigError(
|
|
75
|
-
"The publicPath in remix.config.js must be prepended with the value of `process.env.HYDROGEN_ASSET_BASE_URL`."
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
const expectedServerBuildPath = path.join(
|
|
79
|
-
BUILD_DIR,
|
|
80
|
-
WORKER_SUBDIR,
|
|
81
|
-
"index.js"
|
|
82
|
-
);
|
|
83
|
-
if (config.serverBuildPath !== path.resolve(root, expectedServerBuildPath)) {
|
|
84
|
-
throwConfigError(
|
|
85
|
-
`The serverBuildPath in remix.config.js must be "${expectedServerBuildPath}".`
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
const expectedAssetsBuildDirectory = path.join(BUILD_DIR, CLIENT_SUBDIR);
|
|
89
|
-
if (!config.assetsBuildDirectory.startsWith(
|
|
90
|
-
path.resolve(root, expectedAssetsBuildDirectory)
|
|
91
|
-
)) {
|
|
92
|
-
throwConfigError(
|
|
93
|
-
`The assetsBuildDirectory in remix.config.js must be in "${expectedAssetsBuildDirectory}".`
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (process.env.LOCAL_DEV) {
|
|
98
|
-
const packagesPath = fileURLToPath(new URL("../../..", import.meta.url));
|
|
99
|
-
config.watchPaths ??= [];
|
|
100
|
-
config.watchPaths.push(
|
|
101
|
-
...(await fs.readdir(packagesPath)).map(
|
|
102
|
-
(pkg) => pkg === "hydrogen-react" ? path.resolve(packagesPath, pkg, "dist", "browser-dev", "index.mjs") : path.resolve(packagesPath, pkg, "dist", "development", "index.js")
|
|
103
|
-
)
|
|
104
|
-
);
|
|
105
|
-
config.watchPaths.push(
|
|
106
|
-
path.join(packagesPath, "cli", "dist", "virtual-routes", "**", "*")
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
return config;
|
|
110
|
-
}
|
|
111
|
-
function throwConfigError(message, tryMessage = null) {
|
|
112
|
-
renderFatalError({
|
|
113
|
-
name: "ConfigError",
|
|
114
|
-
type: 0,
|
|
115
|
-
message,
|
|
116
|
-
tryMessage
|
|
117
|
-
});
|
|
118
|
-
process.exit(1);
|
|
119
|
-
}
|
|
120
|
-
async function assertEntryFileExists(root, fileRelative) {
|
|
121
|
-
const fileAbsolute = path.resolve(root, fileRelative);
|
|
122
|
-
const exists = await fileExists(fileAbsolute);
|
|
123
|
-
if (!exists) {
|
|
124
|
-
if (!path.extname(fileAbsolute)) {
|
|
125
|
-
const { readdir } = await import('fs/promises');
|
|
126
|
-
const files = await readdir(path.dirname(fileAbsolute));
|
|
127
|
-
const exists2 = files.some((file) => {
|
|
128
|
-
const { name, ext } = path.parse(file);
|
|
129
|
-
return name === path.basename(fileAbsolute) && /^\.[jt]s$/.test(ext);
|
|
130
|
-
});
|
|
131
|
-
if (exists2)
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
throwConfigError(
|
|
135
|
-
`Entry file "${fileRelative}" not found.`,
|
|
136
|
-
"Please ensure the file exists and that the path is correctly added to the `server` property in remix.config.js."
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export { assertEntryFileExists, getProjectPaths, getRemixConfig };
|