@shopify/cli-hydrogen 5.2.1 → 5.2.3
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 +7 -5
- package/dist/commands/hydrogen/dev.js +9 -4
- package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +1 -1
- package/dist/generator-templates/starter/package.json +3 -3
- package/dist/generator-templates/starter/remix.env.d.ts +1 -0
- package/dist/generator-templates/starter/server.ts +1 -1
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +2 -2
- package/dist/lib/live-reload.js +2 -1
- package/dist/lib/mini-oxygen.js +41 -13
- package/dist/lib/onboarding/common.js +20 -9
- package/dist/lib/remix-config.js +12 -2
- package/dist/lib/remix-version-check.js +7 -4
- package/dist/lib/remix-version-check.test.js +1 -1
- package/dist/lib/request-events.js +84 -0
- package/dist/lib/setups/routes/generate.js +2 -2
- package/dist/lib/transpile-ts.js +21 -23
- package/dist/lib/virtual-routes.js +11 -9
- package/dist/virtual-routes/components/FlameChartWrapper.jsx +125 -0
- package/dist/virtual-routes/routes/debug-network.jsx +289 -0
- package/dist/virtual-routes/virtual-root.jsx +7 -4
- package/oclif.manifest.json +1 -1
- package/package.json +26 -11
|
@@ -5,7 +5,7 @@ import { rmdir, fileSize, glob, removeFile, fileExists, copyFile } from '@shopif
|
|
|
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, assertOxygenChecks } from '../../lib/remix-config.js';
|
|
8
|
+
import { getProjectPaths, getRemixConfig, handleRemixImportFail, 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';
|
|
@@ -61,11 +61,13 @@ async function runBuild({
|
|
|
61
61
|
console.time(LOG_WORKER_BUILT);
|
|
62
62
|
outputInfo(`
|
|
63
63
|
\u{1F3D7}\uFE0F Building in ${process.env.NODE_ENV} mode...`);
|
|
64
|
-
const [remixConfig, { build }, { logThrown }, { createFileWatchCache }] = await Promise.all([
|
|
64
|
+
const [remixConfig, [{ build }, { logThrown }, { createFileWatchCache }]] = await Promise.all([
|
|
65
65
|
getRemixConfig(root),
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
Promise.all([
|
|
67
|
+
import('@remix-run/dev/dist/compiler/build.js'),
|
|
68
|
+
import('@remix-run/dev/dist/compiler/utils/log.js'),
|
|
69
|
+
import('@remix-run/dev/dist/compiler/fileWatchCache.js')
|
|
70
|
+
]).catch(handleRemixImportFail),
|
|
69
71
|
rmdir(buildPath, { force: true })
|
|
70
72
|
]);
|
|
71
73
|
assertOxygenChecks(remixConfig);
|
|
@@ -5,7 +5,7 @@ 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, assertOxygenChecks, getRemixConfig } from '../../lib/remix-config.js';
|
|
8
|
+
import { getProjectPaths, assertOxygenChecks, handleRemixImportFail, getRemixConfig } from '../../lib/remix-config.js';
|
|
9
9
|
import { muteDevLogs, createRemixLogger, enhanceH2Logs } from '../../lib/log.js';
|
|
10
10
|
import { commonFlags, deprecated, flagsToCamelObject, DEFAULT_PORT } from '../../lib/flags.js';
|
|
11
11
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
@@ -98,7 +98,7 @@ async function runDev({
|
|
|
98
98
|
const [{ watch }, { createFileWatchCache }] = await Promise.all([
|
|
99
99
|
import('@remix-run/dev/dist/compiler/watch.js'),
|
|
100
100
|
import('@remix-run/dev/dist/compiler/fileWatchCache.js')
|
|
101
|
-
]);
|
|
101
|
+
]).catch(handleRemixImportFail);
|
|
102
102
|
let isInitialBuild = true;
|
|
103
103
|
let initialBuildDurationMs = 0;
|
|
104
104
|
let initialBuildStartTimeMs = Date.now();
|
|
@@ -116,13 +116,18 @@ async function runDev({
|
|
|
116
116
|
env: await envPromise
|
|
117
117
|
});
|
|
118
118
|
const graphiqlUrl = `${miniOxygen.listeningAt}/graphiql`;
|
|
119
|
+
const debugNetworkUrl = `${miniOxygen.listeningAt}/debug-network`;
|
|
119
120
|
enhanceH2Logs({ graphiqlUrl, ...remixConfig });
|
|
120
121
|
miniOxygen.showBanner({
|
|
121
122
|
appName: storefront ? colors.cyan(storefront?.title) : void 0,
|
|
122
123
|
headlinePrefix: initialBuildDurationMs > 0 ? `Initial build: ${initialBuildDurationMs}ms
|
|
123
124
|
` : "",
|
|
124
|
-
extraLines: [
|
|
125
|
-
|
|
125
|
+
extraLines: [
|
|
126
|
+
colors.dim(`
|
|
127
|
+
View GraphiQL API browser: ${graphiqlUrl}`),
|
|
128
|
+
colors.dim(`
|
|
129
|
+
View server-side network requests: ${debugNetworkUrl}`)
|
|
130
|
+
]
|
|
126
131
|
});
|
|
127
132
|
if (useCodegen) {
|
|
128
133
|
spawnCodegenProcess({ ...remixConfig, configFilePath: codegenConfigPath });
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@remix-run/react": "1.19.1",
|
|
17
17
|
"@shopify/cli": "3.48.0",
|
|
18
|
-
"@shopify/cli-hydrogen": "^5.2.
|
|
19
|
-
"@shopify/hydrogen": "^2023.7.
|
|
20
|
-
"@shopify/remix-oxygen": "^1.1.
|
|
18
|
+
"@shopify/cli-hydrogen": "^5.2.3",
|
|
19
|
+
"@shopify/hydrogen": "^2023.7.7",
|
|
20
|
+
"@shopify/remix-oxygen": "^1.1.4",
|
|
21
21
|
"graphql": "^16.6.0",
|
|
22
22
|
"graphql-tag": "^2.12.6",
|
|
23
23
|
"isbot": "^3.6.6",
|
|
@@ -70,7 +70,7 @@ export default {
|
|
|
70
70
|
const handleRequest = createRequestHandler({
|
|
71
71
|
build: remixBuild,
|
|
72
72
|
mode: process.env.NODE_ENV,
|
|
73
|
-
getLoadContext: () => ({session, storefront, env,
|
|
73
|
+
getLoadContext: () => ({session, storefront, cart, env, waitUntil}),
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
const response = await handleRequest(request);
|
|
@@ -1227,7 +1227,7 @@ export type CollectionQuery = {
|
|
|
1227
1227
|
>;
|
|
1228
1228
|
pageInfo: Pick<
|
|
1229
1229
|
StorefrontAPI.PageInfo,
|
|
1230
|
-
'hasPreviousPage' | 'hasNextPage' | 'endCursor'
|
|
1230
|
+
'hasPreviousPage' | 'hasNextPage' | 'endCursor' | 'startCursor'
|
|
1231
1231
|
>;
|
|
1232
1232
|
};
|
|
1233
1233
|
}
|
|
@@ -1819,7 +1819,7 @@ interface GeneratedQueryTypes {
|
|
|
1819
1819
|
return: BlogsQuery;
|
|
1820
1820
|
variables: BlogsQueryVariables;
|
|
1821
1821
|
};
|
|
1822
|
-
'#graphql\n #graphql\n fragment MoneyProductItem on MoneyV2 {\n amount\n currencyCode\n }\n fragment ProductItem on Product {\n id\n handle\n title\n featuredImage {\n id\n altText\n url\n width\n height\n }\n priceRange {\n minVariantPrice {\n ...MoneyProductItem\n }\n maxVariantPrice {\n ...MoneyProductItem\n }\n }\n variants(first: 1) {\n nodes {\n selectedOptions {\n name\n value\n }\n }\n }\n }\n\n query Collection(\n $handle: String!\n $country: CountryCode\n $language: LanguageCode\n $first: Int\n $last: Int\n $startCursor: String\n $endCursor: String\n ) @inContext(country: $country, language: $language) {\n collection(handle: $handle) {\n id\n handle\n title\n description\n products(\n first: $first,\n last: $last,\n before: $startCursor,\n after: $endCursor\n ) {\n nodes {\n ...ProductItem\n }\n pageInfo {\n hasPreviousPage\n hasNextPage\n
|
|
1822
|
+
'#graphql\n #graphql\n fragment MoneyProductItem on MoneyV2 {\n amount\n currencyCode\n }\n fragment ProductItem on Product {\n id\n handle\n title\n featuredImage {\n id\n altText\n url\n width\n height\n }\n priceRange {\n minVariantPrice {\n ...MoneyProductItem\n }\n maxVariantPrice {\n ...MoneyProductItem\n }\n }\n variants(first: 1) {\n nodes {\n selectedOptions {\n name\n value\n }\n }\n }\n }\n\n query Collection(\n $handle: String!\n $country: CountryCode\n $language: LanguageCode\n $first: Int\n $last: Int\n $startCursor: String\n $endCursor: String\n ) @inContext(country: $country, language: $language) {\n collection(handle: $handle) {\n id\n handle\n title\n description\n products(\n first: $first,\n last: $last,\n before: $startCursor,\n after: $endCursor\n ) {\n nodes {\n ...ProductItem\n }\n pageInfo {\n hasPreviousPage\n hasNextPage\n endCursor\n startCursor\n }\n }\n }\n }\n': {
|
|
1823
1823
|
return: CollectionQuery;
|
|
1824
1824
|
variables: CollectionQueryVariables;
|
|
1825
1825
|
};
|
package/dist/lib/live-reload.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
|
+
import { handleRemixImportFail } from './remix-config.js';
|
|
2
3
|
|
|
3
4
|
async function setupLiveReload(devServerPort) {
|
|
4
5
|
try {
|
|
@@ -7,7 +8,7 @@ async function setupLiveReload(devServerPort) {
|
|
|
7
8
|
import('@remix-run/dev/dist/devServer_unstable/socket.js'),
|
|
8
9
|
import('@remix-run/dev/dist/devServer_unstable/hdr.js'),
|
|
9
10
|
import('@remix-run/dev/dist/result.js')
|
|
10
|
-
]);
|
|
11
|
+
]).catch(handleRemixImportFail);
|
|
11
12
|
const state = {};
|
|
12
13
|
const server = http.createServer(function(req, res) {
|
|
13
14
|
res.writeHead(200);
|
package/dist/lib/mini-oxygen.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
1
3
|
import { outputToken, outputInfo, outputContent } from '@shopify/cli-kit/node/output';
|
|
2
4
|
import { resolvePath } from '@shopify/cli-kit/node/path';
|
|
3
5
|
import { readFile, fileExists } from '@shopify/cli-kit/node/fs';
|
|
4
6
|
import colors from '@shopify/cli-kit/node/colors';
|
|
5
7
|
import { renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
6
|
-
import { startServer } from '@shopify/mini-oxygen';
|
|
8
|
+
import { startServer, Request } from '@shopify/mini-oxygen';
|
|
7
9
|
import { DEFAULT_PORT } from './flags.js';
|
|
10
|
+
import { clearHistory, streamRequestEvents, DEV_ROUTES, logRequestEvent } from './request-events.js';
|
|
8
11
|
|
|
9
12
|
async function startMiniOxygen({
|
|
10
13
|
root,
|
|
@@ -16,8 +19,23 @@ async function startMiniOxygen({
|
|
|
16
19
|
env
|
|
17
20
|
}) {
|
|
18
21
|
const dotenvPath = resolvePath(root, ".env");
|
|
22
|
+
const asyncLocalStorage = new AsyncLocalStorage();
|
|
23
|
+
const serviceBindings = {
|
|
24
|
+
H2O_LOG_EVENT: {
|
|
25
|
+
fetch: (request) => logRequestEvent(
|
|
26
|
+
new Request(request.url, {
|
|
27
|
+
headers: {
|
|
28
|
+
...Object.fromEntries(request.headers.entries()),
|
|
29
|
+
// Merge some headers from the parent request
|
|
30
|
+
...asyncLocalStorage.getStore()
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
};
|
|
19
36
|
const miniOxygen = await startServer({
|
|
20
37
|
script: await readFile(buildPathWorkerFile),
|
|
38
|
+
workerFile: buildPathWorkerFile,
|
|
21
39
|
assetsDir: buildPathClient,
|
|
22
40
|
publicPath: "",
|
|
23
41
|
port,
|
|
@@ -26,19 +44,29 @@ async function startMiniOxygen({
|
|
|
26
44
|
modules: true,
|
|
27
45
|
env: {
|
|
28
46
|
...env,
|
|
29
|
-
...process.env
|
|
47
|
+
...process.env,
|
|
48
|
+
...serviceBindings
|
|
30
49
|
},
|
|
31
50
|
envPath: !env && await fileExists(dotenvPath) ? dotenvPath : void 0,
|
|
32
51
|
log: () => {
|
|
33
52
|
},
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
|
|
53
|
+
async onRequest(request, defaultDispatcher) {
|
|
54
|
+
const url = new URL(request.url);
|
|
55
|
+
if (url.pathname === "/debug-network-server") {
|
|
56
|
+
return request.method === "DELETE" ? clearHistory() : streamRequestEvents(request);
|
|
57
|
+
}
|
|
58
|
+
let requestId = request.headers.get("request-id");
|
|
59
|
+
if (!requestId) {
|
|
60
|
+
requestId = randomUUID();
|
|
61
|
+
request.headers.set("request-id", requestId);
|
|
62
|
+
}
|
|
63
|
+
const response = await asyncLocalStorage.run(
|
|
64
|
+
{ "request-id": requestId, purpose: request.headers.get("purpose") },
|
|
65
|
+
() => defaultDispatcher(request)
|
|
66
|
+
);
|
|
67
|
+
logResponse(request, response);
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
42
70
|
});
|
|
43
71
|
const listeningAt = `http://localhost:${miniOxygen.port}`;
|
|
44
72
|
return {
|
|
@@ -49,7 +77,8 @@ async function startMiniOxygen({
|
|
|
49
77
|
if (options.env) {
|
|
50
78
|
nextOptions.env = {
|
|
51
79
|
...options.env,
|
|
52
|
-
...process.env
|
|
80
|
+
...process.env,
|
|
81
|
+
...serviceBindings
|
|
53
82
|
};
|
|
54
83
|
}
|
|
55
84
|
if (options.worker) {
|
|
@@ -73,9 +102,8 @@ async function startMiniOxygen({
|
|
|
73
102
|
function logResponse(request, response) {
|
|
74
103
|
try {
|
|
75
104
|
const url = new URL(request.url);
|
|
76
|
-
if (
|
|
105
|
+
if (DEV_ROUTES.has(url.pathname))
|
|
77
106
|
return;
|
|
78
|
-
}
|
|
79
107
|
const isProxy = !!response.url && response.url !== request.url;
|
|
80
108
|
const isDataRequest = !isProxy && url.searchParams.has("_data");
|
|
81
109
|
let route = request.url.replace(url.origin, "");
|
|
@@ -2,7 +2,7 @@ import { readdir } from 'node:fs/promises';
|
|
|
2
2
|
import { packageManagerUsedForCreating, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
|
|
3
3
|
import { renderConfirmationPrompt, renderInfo, renderTextPrompt, renderSelectPrompt, renderFatalError, renderWarning, renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
4
4
|
import { hyphenate, capitalize } from '@shopify/cli-kit/common/string';
|
|
5
|
-
import { resolvePath, basename
|
|
5
|
+
import { joinPath, resolvePath, basename } from '@shopify/cli-kit/node/path';
|
|
6
6
|
import { initializeGitRepository, addAllToGitFromDirectory, createGitCommit } from '@shopify/cli-kit/node/git';
|
|
7
7
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
8
8
|
import { rmdir, writeFile, fileExists, isDirectory } from '@shopify/cli-kit/node/fs';
|
|
@@ -47,14 +47,25 @@ async function handleRouteGeneration(controller, flagRoutes) {
|
|
|
47
47
|
needsRouteGeneration,
|
|
48
48
|
setupRoutes: async (directory, language, i18nStrategy) => {
|
|
49
49
|
if (needsRouteGeneration) {
|
|
50
|
-
const result = await generateRoutes(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
const result = await generateRoutes(
|
|
51
|
+
{
|
|
52
|
+
routeName: routesToScaffold,
|
|
53
|
+
directory,
|
|
54
|
+
force: true,
|
|
55
|
+
typescript: language === "ts",
|
|
56
|
+
localePrefix: i18nStrategy === "subfolders" ? "locale" : false,
|
|
57
|
+
signal: controller.signal
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
rootDirectory: directory,
|
|
61
|
+
appDirectory: joinPath(directory, "app"),
|
|
62
|
+
future: {
|
|
63
|
+
v2_errorBoundary: true,
|
|
64
|
+
v2_meta: true,
|
|
65
|
+
v2_routeConvention: true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
);
|
|
58
69
|
return result.routeGroups;
|
|
59
70
|
}
|
|
60
71
|
}
|
package/dist/lib/remix-config.js
CHANGED
|
@@ -6,6 +6,7 @@ import { AbortError } from '@shopify/cli-kit/node/error';
|
|
|
6
6
|
import { outputWarn } from '@shopify/cli-kit/node/output';
|
|
7
7
|
import { fileExists } from '@shopify/cli-kit/node/fs';
|
|
8
8
|
import { muteRemixLogs } from './log.js';
|
|
9
|
+
import { getRequiredRemixVersion } from './remix-version-check.js';
|
|
9
10
|
|
|
10
11
|
const BUILD_DIR = "dist";
|
|
11
12
|
const CLIENT_SUBDIR = "client";
|
|
@@ -25,9 +26,18 @@ function getProjectPaths(appPath, entry) {
|
|
|
25
26
|
publicPath
|
|
26
27
|
};
|
|
27
28
|
}
|
|
29
|
+
function handleRemixImportFail() {
|
|
30
|
+
const remixVersion = getRequiredRemixVersion();
|
|
31
|
+
throw new AbortError(
|
|
32
|
+
"Could not load Remix packages.",
|
|
33
|
+
`Please make sure you have \`@remix-run/dev@${remixVersion}\` installed and all the other Remix packages have the same version.`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
28
36
|
async function getRemixConfig(root, mode = process.env.NODE_ENV) {
|
|
29
37
|
await muteRemixLogs();
|
|
30
|
-
const { readConfig } = await import('@remix-run/dev/dist/config.js')
|
|
38
|
+
const { readConfig } = await import('@remix-run/dev/dist/config.js').catch(
|
|
39
|
+
handleRemixImportFail
|
|
40
|
+
);
|
|
31
41
|
const config = await readConfig(root, mode);
|
|
32
42
|
if (process.env.LOCAL_DEV) {
|
|
33
43
|
const packagesPath = fileURLToPath(new URL("../../..", import.meta.url));
|
|
@@ -134,4 +144,4 @@ async function assertEntryFileExists(root, fileRelative) {
|
|
|
134
144
|
}
|
|
135
145
|
}
|
|
136
146
|
|
|
137
|
-
export { assertOxygenChecks, getProjectPaths, getRemixConfig };
|
|
147
|
+
export { assertOxygenChecks, getProjectPaths, getRemixConfig, handleRemixImportFail };
|
|
@@ -2,12 +2,15 @@ import { createRequire } from 'node:module';
|
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { renderWarning } from '@shopify/cli-kit/node/ui';
|
|
4
4
|
|
|
5
|
-
function
|
|
6
|
-
const require2 = createRequire(import.meta.url);
|
|
5
|
+
function getRequiredRemixVersion(require2 = createRequire(import.meta.url)) {
|
|
7
6
|
const hydrogenPkgJson = require2(fileURLToPath(
|
|
8
7
|
new URL("../../package.json", import.meta.url)
|
|
9
8
|
));
|
|
10
|
-
|
|
9
|
+
return hydrogenPkgJson.peerDependencies["@remix-run/dev"];
|
|
10
|
+
}
|
|
11
|
+
function checkRemixVersions() {
|
|
12
|
+
const require2 = createRequire(import.meta.url);
|
|
13
|
+
const requiredVersionInHydrogen = getRequiredRemixVersion(require2);
|
|
11
14
|
const pkgs = [
|
|
12
15
|
"dev",
|
|
13
16
|
"react",
|
|
@@ -48,4 +51,4 @@ function getRemixPackageVersion(require2, name) {
|
|
|
48
51
|
return result;
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
export { checkRemixVersions };
|
|
54
|
+
export { checkRemixVersions, getRequiredRemixVersion };
|
|
@@ -27,7 +27,7 @@ describe("remix-version-check", () => {
|
|
|
27
27
|
const expectedVersion = "42.0.0-test";
|
|
28
28
|
vi.mocked(requireMock).mockReturnValueOnce({
|
|
29
29
|
// Hydrogen expected version
|
|
30
|
-
|
|
30
|
+
peerDependencies: { "@remix-run/dev": expectedVersion }
|
|
31
31
|
});
|
|
32
32
|
const outputMock = mockAndCaptureOutput();
|
|
33
33
|
checkRemixVersions();
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { ReadableStream } from 'node:stream/web';
|
|
3
|
+
import { Response } from '@shopify/mini-oxygen';
|
|
4
|
+
|
|
5
|
+
const DEV_ROUTES = /* @__PURE__ */ new Set(["/graphiql", "/debug-network"]);
|
|
6
|
+
const EVENT_MAP = {
|
|
7
|
+
request: "Request",
|
|
8
|
+
subrequest: "Sub request"
|
|
9
|
+
};
|
|
10
|
+
function getRequestInfo(request) {
|
|
11
|
+
return {
|
|
12
|
+
id: request.headers.get("request-id"),
|
|
13
|
+
eventType: request.headers.get("hydrogen-event-type") || "unknown",
|
|
14
|
+
startTime: request.headers.get("hydrogen-start-time"),
|
|
15
|
+
endTime: request.headers.get("hydrogen-end-time") || String(Date.now()),
|
|
16
|
+
purpose: request.headers.get("purpose") === "prefetch" ? "(prefetch)" : "",
|
|
17
|
+
cacheStatus: request.headers.get("hydrogen-cache-status")
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const eventEmitter = new EventEmitter();
|
|
21
|
+
const eventHistory = [];
|
|
22
|
+
async function clearHistory() {
|
|
23
|
+
eventHistory.length = 0;
|
|
24
|
+
return new Response("ok");
|
|
25
|
+
}
|
|
26
|
+
async function logRequestEvent(request) {
|
|
27
|
+
if (DEV_ROUTES.has(new URL(request.url).pathname)) {
|
|
28
|
+
return new Response("ok");
|
|
29
|
+
}
|
|
30
|
+
const { eventType, purpose, ...data } = getRequestInfo(request);
|
|
31
|
+
let description = request.url;
|
|
32
|
+
if (eventType === "subrequest") {
|
|
33
|
+
description = decodeURIComponent(request.url).match(/(query|mutation)\s+(\w+)/)?.[0]?.replace(/\s+/, " ") || request.url;
|
|
34
|
+
}
|
|
35
|
+
const event = {
|
|
36
|
+
event: EVENT_MAP[eventType] || eventType,
|
|
37
|
+
data: JSON.stringify({
|
|
38
|
+
...data,
|
|
39
|
+
url: `${purpose} ${description}`.trim()
|
|
40
|
+
})
|
|
41
|
+
};
|
|
42
|
+
if (eventHistory.length > 100)
|
|
43
|
+
eventHistory.shift();
|
|
44
|
+
eventHistory.push(event);
|
|
45
|
+
eventEmitter.emit("request", event);
|
|
46
|
+
return new Response("ok");
|
|
47
|
+
}
|
|
48
|
+
function streamRequestEvents(request) {
|
|
49
|
+
const stream = new ReadableStream({
|
|
50
|
+
start(controller) {
|
|
51
|
+
const encoder = new TextEncoder();
|
|
52
|
+
const enqueueEvent = ({ event = "message", data }) => {
|
|
53
|
+
controller.enqueue(encoder.encode(`event: ${event}
|
|
54
|
+
`));
|
|
55
|
+
controller.enqueue(encoder.encode(`data: ${data}
|
|
56
|
+
|
|
57
|
+
`));
|
|
58
|
+
};
|
|
59
|
+
eventHistory.forEach(enqueueEvent);
|
|
60
|
+
eventEmitter.addListener("request", enqueueEvent);
|
|
61
|
+
let closed = false;
|
|
62
|
+
function close() {
|
|
63
|
+
if (closed)
|
|
64
|
+
return;
|
|
65
|
+
closed = true;
|
|
66
|
+
request.signal.removeEventListener("abort", close);
|
|
67
|
+
eventEmitter.removeListener("request", enqueueEvent);
|
|
68
|
+
controller.close();
|
|
69
|
+
}
|
|
70
|
+
request.signal.addEventListener("abort", close);
|
|
71
|
+
if (request.signal.aborted)
|
|
72
|
+
return close();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return new Response(stream, {
|
|
76
|
+
headers: {
|
|
77
|
+
"Content-Type": "text/event-stream",
|
|
78
|
+
"Cache-Control": "no-store",
|
|
79
|
+
Connection: "keep-alive"
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { DEV_ROUTES, clearHistory, logRequestEvent, streamRequestEvents };
|
|
@@ -51,9 +51,9 @@ async function getResolvedRoutes(routeKeys = Object.keys(ROUTE_MAP)) {
|
|
|
51
51
|
return { routeGroups, resolvedRouteFiles };
|
|
52
52
|
}
|
|
53
53
|
const ALL_ROUTE_CHOICES = [...Object.keys(ROUTE_MAP), "all"];
|
|
54
|
-
async function generateRoutes(options) {
|
|
54
|
+
async function generateRoutes(options, remixConfig) {
|
|
55
55
|
const { routeGroups, resolvedRouteFiles } = options.routeName === "all" ? await getResolvedRoutes() : await getResolvedRoutes([options.routeName]);
|
|
56
|
-
const { rootDirectory, appDirectory, future, tsconfigPath } = await getRemixConfig(options.directory);
|
|
56
|
+
const { rootDirectory, appDirectory, future, tsconfigPath } = remixConfig || await getRemixConfig(options.directory);
|
|
57
57
|
const routesArray = resolvedRouteFiles.flatMap(
|
|
58
58
|
(item) => GENERATOR_ROUTE_DIR + "/" + item
|
|
59
59
|
);
|
package/dist/lib/transpile-ts.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
|
-
import glob from 'fast-glob';
|
|
1
|
+
import { glob, removeFile, readFile, writeFile } from '@shopify/cli-kit/node/fs';
|
|
4
2
|
import { outputDebug } from '@shopify/cli-kit/node/output';
|
|
3
|
+
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
5
4
|
import { getCodeFormatOptions, formatCode } from './format-code.js';
|
|
6
5
|
|
|
7
6
|
const escapeNewLines = (code) => code.replace(/\n\n/g, "\n/* :newline: */");
|
|
@@ -79,35 +78,34 @@ async function transpileProject(projectDir) {
|
|
|
79
78
|
const formatConfig = await getCodeFormatOptions();
|
|
80
79
|
for (const entry of entries) {
|
|
81
80
|
if (entry.endsWith(".d.ts")) {
|
|
82
|
-
await
|
|
81
|
+
await removeFile(entry);
|
|
83
82
|
continue;
|
|
84
83
|
}
|
|
85
|
-
const tsx = await
|
|
84
|
+
const tsx = await readFile(entry);
|
|
86
85
|
const mjs = await formatCode(await transpileFile(tsx), formatConfig);
|
|
87
|
-
await
|
|
88
|
-
await
|
|
86
|
+
await removeFile(entry);
|
|
87
|
+
await writeFile(entry.replace(/\.ts(x?)$/, ".js$1"), mjs);
|
|
89
88
|
}
|
|
90
89
|
try {
|
|
91
|
-
const remixConfigPath =
|
|
92
|
-
let remixConfig = await
|
|
90
|
+
const remixConfigPath = joinPath(projectDir, "remix.config.js");
|
|
91
|
+
let remixConfig = await readFile(remixConfigPath);
|
|
93
92
|
remixConfig = remixConfig.replace(/\/server\.ts/gim, "/server.js");
|
|
94
|
-
await
|
|
93
|
+
await writeFile(remixConfigPath, remixConfig);
|
|
95
94
|
} catch (error) {
|
|
96
95
|
outputDebug(
|
|
97
96
|
"Could not change TS extensions in remix.config.js:\n" + error.stack
|
|
98
97
|
);
|
|
99
98
|
}
|
|
100
99
|
try {
|
|
101
|
-
const tsConfigPath =
|
|
102
|
-
const tsConfigWithComments = await
|
|
100
|
+
const tsConfigPath = joinPath(projectDir, "tsconfig.json");
|
|
101
|
+
const tsConfigWithComments = await readFile(tsConfigPath);
|
|
103
102
|
const jsConfig = convertConfigToJS(
|
|
104
103
|
JSON.parse(tsConfigWithComments.replace(/^\s*\/\/.*$/gm, ""))
|
|
105
104
|
);
|
|
106
|
-
await
|
|
107
|
-
await
|
|
108
|
-
|
|
109
|
-
JSON.stringify(jsConfig, null, 2)
|
|
110
|
-
"utf8"
|
|
105
|
+
await removeFile(tsConfigPath);
|
|
106
|
+
await writeFile(
|
|
107
|
+
joinPath(projectDir, "jsconfig.json"),
|
|
108
|
+
JSON.stringify(jsConfig, null, 2)
|
|
111
109
|
);
|
|
112
110
|
} catch (error) {
|
|
113
111
|
outputDebug(
|
|
@@ -116,7 +114,7 @@ async function transpileProject(projectDir) {
|
|
|
116
114
|
}
|
|
117
115
|
try {
|
|
118
116
|
const pkgJson = JSON.parse(
|
|
119
|
-
await
|
|
117
|
+
await readFile(joinPath(projectDir, "package.json"))
|
|
120
118
|
);
|
|
121
119
|
delete pkgJson.scripts["typecheck"];
|
|
122
120
|
delete pkgJson.devDependencies["typescript"];
|
|
@@ -133,8 +131,8 @@ async function transpileProject(projectDir) {
|
|
|
133
131
|
if (pkgJson.scripts?.build) {
|
|
134
132
|
pkgJson.scripts.build = pkgJson.scripts.build.replace(codegenFlag, "");
|
|
135
133
|
}
|
|
136
|
-
await
|
|
137
|
-
|
|
134
|
+
await writeFile(
|
|
135
|
+
joinPath(projectDir, "package.json"),
|
|
138
136
|
JSON.stringify(pkgJson, null, 2)
|
|
139
137
|
);
|
|
140
138
|
} catch (error) {
|
|
@@ -143,10 +141,10 @@ async function transpileProject(projectDir) {
|
|
|
143
141
|
);
|
|
144
142
|
}
|
|
145
143
|
try {
|
|
146
|
-
const eslintrcPath =
|
|
147
|
-
let eslintrc = await
|
|
144
|
+
const eslintrcPath = joinPath(projectDir, ".eslintrc.js");
|
|
145
|
+
let eslintrc = await readFile(eslintrcPath);
|
|
148
146
|
eslintrc = eslintrc.replace(/\/\*\*[\s*]+@type.+\s+\*\/\s?/gim, "").replace(/\s*,?\s*['"`]plugin:hydrogen\/typescript['"`]/gim, "").replace(/\s+['"`]@typescript-eslint\/.+,/gim, "");
|
|
149
|
-
await
|
|
147
|
+
await writeFile(eslintrcPath, eslintrc);
|
|
150
148
|
} catch (error) {
|
|
151
149
|
outputDebug(
|
|
152
150
|
"Could not remove TS rules from .eslintrc:\n" + error.stack
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { glob } from '@shopify/cli-kit/node/fs';
|
|
3
|
+
import { joinPath, relativePath } from '@shopify/cli-kit/node/path';
|
|
4
4
|
|
|
5
5
|
const VIRTUAL_ROUTES_DIR = "virtual-routes/routes";
|
|
6
6
|
const VIRTUAL_ROOT = "virtual-routes/virtual-root";
|
|
7
7
|
async function addVirtualRoutes(config) {
|
|
8
8
|
const userRouteList = Object.values(config.routes);
|
|
9
9
|
const distPath = fileURLToPath(new URL("..", import.meta.url));
|
|
10
|
-
const virtualRoutesPath =
|
|
11
|
-
for (const absoluteFilePath of await
|
|
12
|
-
|
|
10
|
+
const virtualRoutesPath = joinPath(distPath, VIRTUAL_ROUTES_DIR);
|
|
11
|
+
for (const absoluteFilePath of await glob(
|
|
12
|
+
joinPath(virtualRoutesPath, "**", "*")
|
|
13
|
+
)) {
|
|
14
|
+
const relativeFilePath = relativePath(virtualRoutesPath, absoluteFilePath);
|
|
13
15
|
const routePath = relativeFilePath.replace(/\.[jt]sx?$/, "").replaceAll("\\", "/");
|
|
14
16
|
const isIndex = /(^|\/)index$/.test(routePath);
|
|
15
17
|
const normalizedVirtualRoutePath = isIndex ? routePath.slice(0, -"index".length).replace(/\/$/, "") || void 0 : (
|
|
@@ -27,15 +29,15 @@ async function addVirtualRoutes(config) {
|
|
|
27
29
|
path: normalizedVirtualRoutePath,
|
|
28
30
|
index: isIndex || void 0,
|
|
29
31
|
caseSensitive: void 0,
|
|
30
|
-
file:
|
|
32
|
+
file: relativePath(config.appDirectory, absoluteFilePath)
|
|
31
33
|
};
|
|
32
34
|
if (!config.routes[VIRTUAL_ROOT]) {
|
|
33
35
|
config.routes[VIRTUAL_ROOT] = {
|
|
34
36
|
id: VIRTUAL_ROOT,
|
|
35
37
|
path: "",
|
|
36
|
-
file:
|
|
38
|
+
file: relativePath(
|
|
37
39
|
config.appDirectory,
|
|
38
|
-
|
|
40
|
+
joinPath(distPath, VIRTUAL_ROOT + ".jsx")
|
|
39
41
|
)
|
|
40
42
|
};
|
|
41
43
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import _useResizeObserver from "use-resize-observer";
|
|
4
|
+
const useResizeObserver = _useResizeObserver;
|
|
5
|
+
const FlameChartWrapper = (props) => {
|
|
6
|
+
const boxRef = useRef(null);
|
|
7
|
+
const canvasRef = useRef(null);
|
|
8
|
+
const flameChart = useRef(null);
|
|
9
|
+
useResizeObserver({
|
|
10
|
+
ref: boxRef,
|
|
11
|
+
onResize: ({ width = 0, height = 0 }) => flameChart.current?.resize(width, height - 3)
|
|
12
|
+
});
|
|
13
|
+
const initialize = useCallback(() => {
|
|
14
|
+
const {
|
|
15
|
+
data,
|
|
16
|
+
marks,
|
|
17
|
+
waterfall,
|
|
18
|
+
timeseries,
|
|
19
|
+
settings,
|
|
20
|
+
colors,
|
|
21
|
+
plugins,
|
|
22
|
+
timeframeTimeseries
|
|
23
|
+
} = props;
|
|
24
|
+
if (canvasRef.current && boxRef.current) {
|
|
25
|
+
const { width = 0, height = 0 } = boxRef.current.getBoundingClientRect();
|
|
26
|
+
canvasRef.current.width = width;
|
|
27
|
+
canvasRef.current.height = height - 3;
|
|
28
|
+
flameChart.current = new flameChartJs.FlameChart({
|
|
29
|
+
canvas: canvasRef.current,
|
|
30
|
+
data,
|
|
31
|
+
marks,
|
|
32
|
+
waterfall,
|
|
33
|
+
timeseries,
|
|
34
|
+
timeframeTimeseries,
|
|
35
|
+
settings,
|
|
36
|
+
colors,
|
|
37
|
+
plugins
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}, [props]);
|
|
41
|
+
const setBoxRef = useCallback(
|
|
42
|
+
(ref) => {
|
|
43
|
+
const isNewRef = ref !== boxRef.current;
|
|
44
|
+
boxRef.current = ref;
|
|
45
|
+
if (isNewRef) {
|
|
46
|
+
initialize();
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
[initialize]
|
|
50
|
+
);
|
|
51
|
+
const setCanvasRef = useCallback(
|
|
52
|
+
(ref) => {
|
|
53
|
+
const isNewRef = ref !== canvasRef.current;
|
|
54
|
+
canvasRef.current = ref;
|
|
55
|
+
if (isNewRef) {
|
|
56
|
+
initialize();
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
[initialize]
|
|
60
|
+
);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (props.data) {
|
|
63
|
+
flameChart.current?.setNodes(props.data);
|
|
64
|
+
}
|
|
65
|
+
}, [props.data]);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (props.marks) {
|
|
68
|
+
flameChart.current?.setMarks(props.marks);
|
|
69
|
+
}
|
|
70
|
+
}, [props.marks]);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (props.waterfall) {
|
|
73
|
+
flameChart.current?.setWaterfall(props.waterfall);
|
|
74
|
+
}
|
|
75
|
+
}, [props.waterfall]);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (props.timeseries) {
|
|
78
|
+
flameChart.current?.setTimeseries(props.timeseries);
|
|
79
|
+
}
|
|
80
|
+
}, [props.timeseries]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (props.timeframeTimeseries) {
|
|
83
|
+
flameChart.current?.setTimeframeTimeseries(props.timeframeTimeseries);
|
|
84
|
+
}
|
|
85
|
+
}, [props.timeframeTimeseries]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (props.settings && flameChart.current) {
|
|
88
|
+
flameChart.current.setSettings(props.settings);
|
|
89
|
+
flameChart.current.renderEngine.recalcChildrenSizes();
|
|
90
|
+
flameChart.current.render();
|
|
91
|
+
}
|
|
92
|
+
}, [props.settings]);
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (props.position) {
|
|
95
|
+
flameChart.current?.setFlameChartPosition(props.position);
|
|
96
|
+
}
|
|
97
|
+
}, [props.position]);
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (props.zoom) {
|
|
100
|
+
flameChart.current?.setZoom(props.zoom.start, props.zoom.end);
|
|
101
|
+
}
|
|
102
|
+
}, [props.zoom]);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (props.onSelect) {
|
|
105
|
+
flameChart.current?.on("select", props.onSelect);
|
|
106
|
+
}
|
|
107
|
+
return () => {
|
|
108
|
+
if (props.onSelect) {
|
|
109
|
+
flameChart.current?.removeListener("select", props.onSelect);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}, [props.onSelect]);
|
|
113
|
+
return /* @__PURE__ */ jsx(
|
|
114
|
+
"div",
|
|
115
|
+
{
|
|
116
|
+
style: { height: `${props.height ? props.height : 300}px` },
|
|
117
|
+
className: props.className,
|
|
118
|
+
ref: setBoxRef,
|
|
119
|
+
children: /* @__PURE__ */ jsx("canvas", { ref: setCanvasRef })
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
export {
|
|
124
|
+
FlameChartWrapper
|
|
125
|
+
};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { FlameChartWrapper } from "../components/FlameChartWrapper.jsx";
|
|
4
|
+
import { Link } from "@remix-run/react";
|
|
5
|
+
import { Script } from "@shopify/hydrogen";
|
|
6
|
+
function DebugNetwork() {
|
|
7
|
+
const serverEvents = useRef({
|
|
8
|
+
smallestStartTime: 0,
|
|
9
|
+
mainRequests: [],
|
|
10
|
+
subRequests: {},
|
|
11
|
+
showPutRequests: false
|
|
12
|
+
});
|
|
13
|
+
const [timestamp, setTimestamp] = useState();
|
|
14
|
+
function serverEventHandler(onEvent) {
|
|
15
|
+
return (event) => {
|
|
16
|
+
const data = JSON.parse(event.data);
|
|
17
|
+
if (serverEvents.current.smallestStartTime === 0) {
|
|
18
|
+
serverEvents.current.smallestStartTime = data.startTime;
|
|
19
|
+
} else {
|
|
20
|
+
serverEvents.current.smallestStartTime = Math.min(
|
|
21
|
+
data.startTime,
|
|
22
|
+
serverEvents.current.smallestStartTime
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
onEvent(data);
|
|
26
|
+
setTimeout(() => {
|
|
27
|
+
setTimestamp((/* @__PURE__ */ new Date()).getTime());
|
|
28
|
+
}, 0);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const evtSource = new EventSource("/debug-network-server", {
|
|
33
|
+
withCredentials: true
|
|
34
|
+
});
|
|
35
|
+
const mainRequestHandler = serverEventHandler((data) => {
|
|
36
|
+
serverEvents.current.mainRequests = [
|
|
37
|
+
...serverEvents.current.mainRequests,
|
|
38
|
+
{
|
|
39
|
+
...data,
|
|
40
|
+
url: data.url.replace(location.origin, "")
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
});
|
|
44
|
+
evtSource.addEventListener("Request", mainRequestHandler);
|
|
45
|
+
const subRequestHandler = serverEventHandler((data) => {
|
|
46
|
+
let groupEvents = serverEvents.current.subRequests[data.id] || [];
|
|
47
|
+
groupEvents = [...groupEvents, data];
|
|
48
|
+
serverEvents.current.subRequests = {
|
|
49
|
+
...serverEvents.current.subRequests,
|
|
50
|
+
[data.id]: groupEvents
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
evtSource.addEventListener("Sub request", subRequestHandler);
|
|
54
|
+
return () => {
|
|
55
|
+
evtSource.removeEventListener("Request", mainRequestHandler);
|
|
56
|
+
evtSource.removeEventListener("Sub request", subRequestHandler);
|
|
57
|
+
evtSource.close();
|
|
58
|
+
};
|
|
59
|
+
}, []);
|
|
60
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
61
|
+
/* @__PURE__ */ jsx(
|
|
62
|
+
Script,
|
|
63
|
+
{
|
|
64
|
+
src: "https://unpkg.com/flame-chart-js@2.3.1/dist/index.min.js",
|
|
65
|
+
suppressHydrationWarning: true
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ jsxs(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
style: {
|
|
72
|
+
width: "100vw",
|
|
73
|
+
backgroundColor: "#F5F5F5",
|
|
74
|
+
fontSize: "0.8rem"
|
|
75
|
+
},
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ jsxs(
|
|
78
|
+
"div",
|
|
79
|
+
{
|
|
80
|
+
style: {
|
|
81
|
+
display: "flex",
|
|
82
|
+
justifyContent: "space-between"
|
|
83
|
+
},
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ jsxs(
|
|
86
|
+
"div",
|
|
87
|
+
{
|
|
88
|
+
style: {
|
|
89
|
+
display: "flex",
|
|
90
|
+
alignItems: "center"
|
|
91
|
+
},
|
|
92
|
+
children: [
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
"button",
|
|
95
|
+
{
|
|
96
|
+
onClick: () => {
|
|
97
|
+
fetch("/debug-network-server", { method: "DELETE" }).catch(
|
|
98
|
+
(error) => console.error("Could not clear history:", error)
|
|
99
|
+
);
|
|
100
|
+
serverEvents.current = {
|
|
101
|
+
smallestStartTime: 0,
|
|
102
|
+
mainRequests: [],
|
|
103
|
+
subRequests: {},
|
|
104
|
+
showPutRequests: serverEvents.current.showPutRequests
|
|
105
|
+
};
|
|
106
|
+
setTimestamp((/* @__PURE__ */ new Date()).getTime());
|
|
107
|
+
},
|
|
108
|
+
children: "Clear"
|
|
109
|
+
}
|
|
110
|
+
),
|
|
111
|
+
/* @__PURE__ */ jsx(
|
|
112
|
+
"input",
|
|
113
|
+
{
|
|
114
|
+
id: "showPutRequests",
|
|
115
|
+
type: "checkbox",
|
|
116
|
+
checked: serverEvents.current.showPutRequests,
|
|
117
|
+
onChange: (event) => {
|
|
118
|
+
serverEvents.current.showPutRequests = event.target.checked;
|
|
119
|
+
setTimestamp((/* @__PURE__ */ new Date()).getTime());
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
),
|
|
123
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "showPutRequests", children: "Show cache update requests (PUT)" })
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
),
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
"p",
|
|
129
|
+
{
|
|
130
|
+
style: {
|
|
131
|
+
paddingRight: "5px"
|
|
132
|
+
},
|
|
133
|
+
children: "Unstable"
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
/* @__PURE__ */ jsx(FlameChart, { serverEvents: serverEvents.current }, timestamp),
|
|
140
|
+
/* @__PURE__ */ jsxs("p", { style: { color: "#777", fontSize: "0.7rem", paddingLeft: "5px" }, children: [
|
|
141
|
+
"Note: You may need to turn on '",
|
|
142
|
+
/* @__PURE__ */ jsx("b", { children: "Disable Cache" }),
|
|
143
|
+
"' for your navigating window. If you are not seeing any requests, try re-running '",
|
|
144
|
+
/* @__PURE__ */ jsx("b", { children: "npm run dev" }),
|
|
145
|
+
"' in your terminal while leaving this window open."
|
|
146
|
+
] })
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
] });
|
|
151
|
+
}
|
|
152
|
+
const PANEL_HEIGHT = 300;
|
|
153
|
+
function FlameChart({ serverEvents }) {
|
|
154
|
+
if (serverEvents.mainRequests.length === 0)
|
|
155
|
+
return /* @__PURE__ */ jsx(
|
|
156
|
+
"div",
|
|
157
|
+
{
|
|
158
|
+
style: {
|
|
159
|
+
height: `${PANEL_HEIGHT}px`,
|
|
160
|
+
display: "flex",
|
|
161
|
+
justifyContent: "center",
|
|
162
|
+
alignItems: "center",
|
|
163
|
+
backgroundColor: "#FAFAFA"
|
|
164
|
+
},
|
|
165
|
+
children: /* @__PURE__ */ jsxs("p", { style: { fontWeight: "bold", color: "#777" }, children: [
|
|
166
|
+
"Navigate your",
|
|
167
|
+
" ",
|
|
168
|
+
/* @__PURE__ */ jsx(Link, { to: "/", target: "_blank", children: "app" })
|
|
169
|
+
] })
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
let totalRequests = 0;
|
|
173
|
+
let totalSubRequests = 0;
|
|
174
|
+
const calcDuration = (time) => time - serverEvents.smallestStartTime;
|
|
175
|
+
let items = [];
|
|
176
|
+
serverEvents.mainRequests.forEach((mainRequest) => {
|
|
177
|
+
const mainResponseStart = calcDuration(mainRequest.endTime);
|
|
178
|
+
let mainResponseEnd = mainResponseStart;
|
|
179
|
+
const subRequestItems = [];
|
|
180
|
+
const subRequests = serverEvents.subRequests[mainRequest.id] || [];
|
|
181
|
+
subRequests.forEach((subRequest) => {
|
|
182
|
+
const subRequestEnd = calcDuration(subRequest.endTime);
|
|
183
|
+
if (subRequest.cacheStatus !== "PUT") {
|
|
184
|
+
mainResponseEnd = Math.max(mainResponseEnd, subRequestEnd);
|
|
185
|
+
}
|
|
186
|
+
const subRequestItem = {
|
|
187
|
+
name: `${subRequest.cacheStatus} ${subRequest.url}`.trim(),
|
|
188
|
+
intervals: "request",
|
|
189
|
+
timing: {
|
|
190
|
+
requestStart: calcDuration(subRequest.startTime),
|
|
191
|
+
requestEnd: subRequestEnd
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
if (serverEvents.showPutRequests) {
|
|
195
|
+
subRequestItems.push(subRequestItem);
|
|
196
|
+
} else {
|
|
197
|
+
subRequest.cacheStatus !== "PUT" && subRequestItems.push(subRequestItem);
|
|
198
|
+
}
|
|
199
|
+
totalSubRequests++;
|
|
200
|
+
});
|
|
201
|
+
totalRequests++;
|
|
202
|
+
items.push({
|
|
203
|
+
name: mainRequest.url,
|
|
204
|
+
intervals: "mainRequest",
|
|
205
|
+
timing: {
|
|
206
|
+
requestStart: calcDuration(mainRequest.startTime),
|
|
207
|
+
responseStart: mainResponseStart,
|
|
208
|
+
responseEnd: mainResponseEnd
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
items = items.concat(subRequestItems);
|
|
212
|
+
});
|
|
213
|
+
const data = {
|
|
214
|
+
items,
|
|
215
|
+
intervals: {
|
|
216
|
+
mainRequest: [
|
|
217
|
+
{
|
|
218
|
+
name: "server",
|
|
219
|
+
color: "#99CC00",
|
|
220
|
+
type: "block",
|
|
221
|
+
start: "requestStart",
|
|
222
|
+
end: "responseStart"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "streaming",
|
|
226
|
+
color: "#33CCFF",
|
|
227
|
+
type: "block",
|
|
228
|
+
start: "responseStart",
|
|
229
|
+
end: "responseEnd"
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
request: [
|
|
233
|
+
{
|
|
234
|
+
name: "request",
|
|
235
|
+
color: "#FFCC00",
|
|
236
|
+
type: "block",
|
|
237
|
+
start: "requestStart",
|
|
238
|
+
end: "requestEnd"
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
244
|
+
/* @__PURE__ */ jsx(
|
|
245
|
+
FlameChartWrapper,
|
|
246
|
+
{
|
|
247
|
+
height: PANEL_HEIGHT,
|
|
248
|
+
waterfall: data,
|
|
249
|
+
settings: {
|
|
250
|
+
styles: {
|
|
251
|
+
waterfallPlugin: {
|
|
252
|
+
defaultHeight: PANEL_HEIGHT
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
),
|
|
258
|
+
/* @__PURE__ */ jsxs(
|
|
259
|
+
"div",
|
|
260
|
+
{
|
|
261
|
+
style: {
|
|
262
|
+
display: "flex",
|
|
263
|
+
padding: "5px",
|
|
264
|
+
borderTop: "1px solid #CCC",
|
|
265
|
+
borderBottom: "1px solid #CCC"
|
|
266
|
+
},
|
|
267
|
+
children: [
|
|
268
|
+
totalRequests,
|
|
269
|
+
" requests",
|
|
270
|
+
/* @__PURE__ */ jsx(
|
|
271
|
+
"span",
|
|
272
|
+
{
|
|
273
|
+
style: {
|
|
274
|
+
paddingLeft: "2px",
|
|
275
|
+
paddingRight: "2px"
|
|
276
|
+
},
|
|
277
|
+
children: "|"
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
totalSubRequests,
|
|
281
|
+
" sub requests"
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
] });
|
|
286
|
+
}
|
|
287
|
+
export {
|
|
288
|
+
DebugNetwork as default
|
|
289
|
+
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import styles from "./assets/styles.css";
|
|
12
12
|
import favicon from "./assets/favicon.svg";
|
|
13
13
|
import { Layout } from "./components/Layout.jsx";
|
|
14
|
+
import { useNonce } from "@shopify/hydrogen";
|
|
14
15
|
const links = () => {
|
|
15
16
|
return [
|
|
16
17
|
{ rel: "stylesheet", href: styles },
|
|
@@ -18,6 +19,7 @@ const links = () => {
|
|
|
18
19
|
];
|
|
19
20
|
};
|
|
20
21
|
function App() {
|
|
22
|
+
const nonce = useNonce();
|
|
21
23
|
return /* @__PURE__ */ jsxs("html", { lang: "en", children: [
|
|
22
24
|
/* @__PURE__ */ jsxs("head", { children: [
|
|
23
25
|
/* @__PURE__ */ jsx("meta", { charSet: "utf-8" }),
|
|
@@ -35,12 +37,13 @@ function App() {
|
|
|
35
37
|
] }),
|
|
36
38
|
/* @__PURE__ */ jsxs("body", { children: [
|
|
37
39
|
/* @__PURE__ */ jsx(Layout, { children: /* @__PURE__ */ jsx(Outlet, {}) }),
|
|
38
|
-
/* @__PURE__ */ jsx(ScrollRestoration, {}),
|
|
39
|
-
/* @__PURE__ */ jsx(Scripts, {})
|
|
40
|
+
/* @__PURE__ */ jsx(ScrollRestoration, { nonce }),
|
|
41
|
+
/* @__PURE__ */ jsx(Scripts, { nonce })
|
|
40
42
|
] })
|
|
41
43
|
] });
|
|
42
44
|
}
|
|
43
45
|
function ErrorBoundary() {
|
|
46
|
+
const nonce = useNonce();
|
|
44
47
|
const error = useRouteError();
|
|
45
48
|
let errorMessage = "Unknown error";
|
|
46
49
|
let errorStatus = 500;
|
|
@@ -71,8 +74,8 @@ function ErrorBoundary() {
|
|
|
71
74
|
/* @__PURE__ */ jsx("h2", { children: errorStatus }),
|
|
72
75
|
errorMessage && /* @__PURE__ */ jsx("fieldset", { children: /* @__PURE__ */ jsx("pre", { children: errorMessage }) })
|
|
73
76
|
] }) }),
|
|
74
|
-
/* @__PURE__ */ jsx(ScrollRestoration, {}),
|
|
75
|
-
/* @__PURE__ */ jsx(Scripts, {})
|
|
77
|
+
/* @__PURE__ */ jsx(ScrollRestoration, { nonce }),
|
|
78
|
+
/* @__PURE__ */ jsx(Scripts, { nonce })
|
|
76
79
|
] })
|
|
77
80
|
] });
|
|
78
81
|
}
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
|
-
"version": "5.2.
|
|
7
|
+
"version": "5.2.3",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
@@ -23,32 +23,47 @@
|
|
|
23
23
|
"@types/recursive-readdir": "^2.2.1",
|
|
24
24
|
"@types/tar-fs": "^2.0.1",
|
|
25
25
|
"@vitest/coverage-v8": "^0.33.0",
|
|
26
|
+
"fast-glob": "^3.2.12",
|
|
27
|
+
"flame-chart-js": "2.3.1",
|
|
26
28
|
"type-fest": "^3.6.0",
|
|
27
29
|
"vitest": "^0.33.0"
|
|
28
30
|
},
|
|
29
|
-
"peerDependencies": {
|
|
30
|
-
"@remix-run/react": "1.19.1",
|
|
31
|
-
"@shopify/hydrogen-react": "^2023.7.3",
|
|
32
|
-
"@shopify/remix-oxygen": "^1.1.3"
|
|
33
|
-
},
|
|
34
31
|
"dependencies": {
|
|
35
32
|
"@ast-grep/napi": "0.11.0",
|
|
36
33
|
"@graphql-codegen/cli": "3.3.1",
|
|
37
34
|
"@oclif/core": "2.8.11",
|
|
38
|
-
"@remix-run/dev": "1.19.1",
|
|
39
35
|
"@shopify/cli-kit": "3.48.0",
|
|
40
36
|
"@shopify/hydrogen-codegen": "^0.0.2",
|
|
41
|
-
"@shopify/mini-oxygen": "^2.1
|
|
37
|
+
"@shopify/mini-oxygen": "^2.2.1",
|
|
42
38
|
"ansi-escapes": "^6.2.0",
|
|
43
39
|
"diff": "^5.1.0",
|
|
44
|
-
"fast-glob": "^3.2.12",
|
|
45
40
|
"fs-extra": "^11.1.0",
|
|
46
41
|
"get-port": "^7.0.0",
|
|
47
42
|
"gunzip-maybe": "^1.4.2",
|
|
48
43
|
"prettier": "^2.8.4",
|
|
49
|
-
"recursive-readdir": "^2.2.3",
|
|
50
44
|
"tar-fs": "^2.1.1",
|
|
51
|
-
"typescript": "^5.2.2"
|
|
45
|
+
"typescript": "^5.2.2",
|
|
46
|
+
"use-resize-observer": "^9.1.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@remix-run/dev": "1.19.1",
|
|
50
|
+
"@remix-run/react": "1.19.1",
|
|
51
|
+
"@shopify/hydrogen-react": "^2023.7.4",
|
|
52
|
+
"@shopify/remix-oxygen": "^1.1.4"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"@remix-run/dev": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"@remix-run/react": {
|
|
59
|
+
"optional": true
|
|
60
|
+
},
|
|
61
|
+
"@shopify/hydrogen-react": {
|
|
62
|
+
"optional": true
|
|
63
|
+
},
|
|
64
|
+
"@shopify/remix-oxygen": {
|
|
65
|
+
"optional": true
|
|
66
|
+
}
|
|
52
67
|
},
|
|
53
68
|
"bin": "dist/create-app.js",
|
|
54
69
|
"exports": {
|