catalyst-core 1.0.1 → 1.0.2-canary.2
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/bin/catalyst.js +105 -1
- package/dist/cli/README.md +119 -0
- package/dist/cli/appConfig.js +50 -0
- package/dist/cli/buildErrors.js +170 -0
- package/dist/cli/buildWarnings.js +28 -0
- package/dist/cli/devOutput.js +524 -0
- package/dist/cli/diagnostic.js +388 -0
- package/dist/cli/hints.js +69 -0
- package/dist/cli/package.json +3 -0
- package/dist/cli/spinner.js +141 -0
- package/dist/cli/theme.js +155 -0
- package/dist/errors/index.js +7 -3
- package/dist/errors/registry.js +8 -0
- package/dist/errors-index.json +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.jsx +1 -1
- package/dist/logger.js +61 -6
- package/dist/native/androidProject/app/build.gradle.kts +2 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +2 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +9 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/SplashActivity.kt +65 -34
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/SplashOverlay.kt +109 -0
- package/dist/native/androidSetup.js +46 -57
- package/dist/native/buildAndroid/assets.js +22 -13
- package/dist/native/buildAndroid/build.js +98 -26
- package/dist/native/buildAndroid/config.js +43 -12
- package/dist/native/buildAndroid/index.js +49 -191
- package/dist/native/buildAppAndroid.js +19 -8
- package/dist/native/buildAppIos.js +35 -16
- package/dist/native/buildIos/assets.js +1 -6
- package/dist/native/buildIos/build.js +61 -94
- package/dist/native/buildIos/config.js +26 -8
- package/dist/native/buildIos/index.js +143 -64
- package/dist/native/buildIos/plugins.js +12 -15
- package/dist/native/internalPluginUtils.js +0 -1
- package/dist/native/iosnativeWebView/CoreLogic/Sources/CatalystCoreLogic/Constants/ConfigConstants.swift +2 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashHTMLView.swift +31 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +17 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +41 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +2 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +3 -0
- package/dist/native/pluginComposerAndroid.js +2 -3
- package/dist/native/renameAndroidProject.js +42 -20
- package/dist/native/setupEmulatorIos.js +28 -49
- package/dist/native/setupServer.js +12 -15
- package/dist/native/terminalProgress.js +186 -147
- package/dist/native/utils.js +335 -54
- package/dist/otel.js +15 -9
- package/dist/scripts/build.js +314 -42
- package/dist/scripts/generateOfflineManifest.js +11 -1
- package/dist/scripts/loadEnvironmentVariables.js +24 -2
- package/dist/scripts/preServerInit.js +32 -10
- package/dist/scripts/preflight.js +115 -11
- package/dist/scripts/scriptUtils.js +34 -0
- package/dist/scripts/serve.js +65 -9
- package/dist/scripts/start.js +58 -21
- package/dist/server/expressServer.js +167 -32
- package/dist/server/renderer/document/Body.d.ts +0 -11
- package/dist/server/renderer/document/Body.jsx +0 -10
- package/dist/server/renderer/document/Head.d.ts +0 -13
- package/dist/server/renderer/document/Head.jsx +0 -12
- package/dist/server/renderer/handler.jsx +87 -11
- package/dist/server/utils/validator.js +14 -11
- package/dist/vite/manifest-categorization-plugin.js +7 -5
- package/dist/vite/resolveDevServerConfig.js +9 -2
- package/dist/vite/vite.config.client.js +19 -1
- package/dist/vite/vite.config.js +96 -19
- package/dist/vite/vite.config.server.js +21 -0
- package/dist/web-router/components/MetaTag.jsx +20 -14
- package/dist/web-router/components/MetaTag.test.jsx +108 -32
- package/dist/web-router/components/RouterDataProvider.d.ts +25 -1
- package/dist/web-router/components/RouterDataProvider.jsx +35 -1
- package/dist/web-router/components/RouterDataProvider.test.jsx +44 -0
- package/dist/web-router/context.d.ts +15 -1
- package/dist/web-router/context.jsx +11 -0
- package/dist/webmcp/WebMcpProvider.jsx +234 -0
- package/dist/webmcp/declarative.js +207 -0
- package/dist/webmcp/declarative.test.js +122 -0
- package/dist/webmcp/errors.js +59 -0
- package/dist/webmcp/errors.test.js +47 -0
- package/dist/webmcp/index.js +40 -0
- package/dist/webmcp/native.js +37 -0
- package/dist/webmcp/native.test.js +41 -0
- package/dist/webmcp/pageInfo.js +119 -0
- package/dist/webmcp/pageInfo.test.js +85 -0
- package/dist/webmcp/registry.dom.test.js +175 -0
- package/dist/webmcp/registry.js +540 -0
- package/dist/webmcp/registry.test.js +228 -0
- package/dist/webmcp/routeContext.js +11 -0
- package/dist/webmcp/shim-entry.js +8 -0
- package/dist/webmcp/shim.js +149 -0
- package/dist/webmcp/useTool.js +124 -0
- package/package.json +4 -7
- package/test/cli/buildErrors.test.ts +173 -0
- package/test/cli/buildWarnings.test.cjs +62 -0
- package/test/cli/devOutput.test.ts +549 -0
- package/test/cli/diagnostic.test.ts +434 -0
- package/test/cli/output.test.cjs +90 -0
- package/test/cli/spinner.test.ts +177 -0
- package/test/cli/theme.test.ts +163 -0
- package/test/native/redactSecrets.test.ts +72 -0
- package/test/native/utils.test.ts +532 -0
- package/test/scripts/build.test.ts +275 -0
- package/test/scripts/preflight.test.ts +100 -2
- package/test/scripts/serve.test.ts +102 -0
- package/test/server/fixtures/template/src/js/routes/index.jsx +8 -0
- package/test/server/handler.test.ts +17 -0
- package/test/server/logger.test.ts +185 -0
- package/test/server/manifestCache.test.ts +13 -0
- package/test/server/otel.test.ts +556 -0
- package/test/server/validator.test.ts +19 -8
- package/vitest.config.ts +4 -0
- package/dist/scripts/checkVersion.js +0 -3
- package/dist/scripts/cleanCache.js +0 -30
|
@@ -8,14 +8,3 @@ import React from "react";
|
|
|
8
8
|
* @param {object} children - contains any child elements defined within the component
|
|
9
9
|
*/
|
|
10
10
|
export declare function Body(props: any): React.JSX.Element;
|
|
11
|
-
export declare namespace Body {
|
|
12
|
-
var propTypes: {
|
|
13
|
-
initialState: any;
|
|
14
|
-
jsx: any;
|
|
15
|
-
statusCode: any;
|
|
16
|
-
fetcherData: any;
|
|
17
|
-
children: any;
|
|
18
|
-
safeArea: any;
|
|
19
|
-
nativeWebView: any;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
2
|
const DEFAULT_SAFE_AREA = {
|
|
4
3
|
top: 0,
|
|
5
4
|
right: 0,
|
|
@@ -51,12 +50,3 @@ export function Body(props) {
|
|
|
51
50
|
{children}
|
|
52
51
|
</body>;
|
|
53
52
|
}
|
|
54
|
-
Body.propTypes = {
|
|
55
|
-
initialState: PropTypes.object,
|
|
56
|
-
jsx: PropTypes.any,
|
|
57
|
-
statusCode: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
58
|
-
fetcherData: PropTypes.object,
|
|
59
|
-
children: PropTypes.node,
|
|
60
|
-
safeArea: PropTypes.object,
|
|
61
|
-
nativeWebView: PropTypes.bool
|
|
62
|
-
};
|
|
@@ -5,16 +5,3 @@ import React from "react";
|
|
|
5
5
|
* Route-cached deferred CSS is inlined here on repeat visits; new deferred CSS still appends after body.
|
|
6
6
|
*/
|
|
7
7
|
export declare function Head(props: any): React.JSX.Element;
|
|
8
|
-
export declare namespace Head {
|
|
9
|
-
var propTypes: {
|
|
10
|
-
isBot: any;
|
|
11
|
-
inlineCss: any;
|
|
12
|
-
deferredRouteInlineCss: any;
|
|
13
|
-
jsScripts: any;
|
|
14
|
-
criticalPreloadLinks: any;
|
|
15
|
-
deferredPreloadLinks: any;
|
|
16
|
-
metaTags: any;
|
|
17
|
-
publicAssetPath: any;
|
|
18
|
-
children: any;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
2
|
import FastRefresh from "../../../vite/FastRefresh.jsx";
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -49,14 +48,3 @@ export function Head(props) {
|
|
|
49
48
|
{children}
|
|
50
49
|
</head>;
|
|
51
50
|
}
|
|
52
|
-
Head.propTypes = {
|
|
53
|
-
isBot: PropTypes.bool,
|
|
54
|
-
inlineCss: PropTypes.string,
|
|
55
|
-
deferredRouteInlineCss: PropTypes.string,
|
|
56
|
-
jsScripts: PropTypes.array,
|
|
57
|
-
criticalPreloadLinks: PropTypes.array,
|
|
58
|
-
deferredPreloadLinks: PropTypes.array,
|
|
59
|
-
metaTags: PropTypes.array,
|
|
60
|
-
publicAssetPath: PropTypes.string,
|
|
61
|
-
children: PropTypes.node
|
|
62
|
-
};
|
|
@@ -29,15 +29,84 @@ import { resolveOutputMode, getDebugEnvInfo } from "../../scripts/scriptUtils.js
|
|
|
29
29
|
// this process never sees the parent's CLI flags, so process.argv here would
|
|
30
30
|
// never contain --debug/--verbose — only CATALYST_OUTPUT_MODE is real input.
|
|
31
31
|
const outputMode = resolveOutputMode([], process.env);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const STAGE_LABEL = {
|
|
33
|
+
RENDER: "render",
|
|
34
|
+
FETCHER: "serverFetcher",
|
|
35
|
+
SERVER_SIDE_FUNCTION: "serverSideFunction",
|
|
36
|
+
REQUEST_HANDLING: "request"
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// One failure is reported once. A render error used to surface at every catch
|
|
40
|
+
// site it bubbled through -- render, fetcher, serverSideFunction, request --
|
|
41
|
+
// each re-rendering the same broken tree, so one throw printed four coded
|
|
42
|
+
// reports and four stacks per request.
|
|
43
|
+
const reported = new WeakSet();
|
|
44
|
+
|
|
45
|
+
// Marks an error that came out of rendering. The fetcher and
|
|
46
|
+
// serverSideFunction fallbacks re-render the page; re-rendering after the
|
|
47
|
+
// render itself failed only throws the same error again.
|
|
48
|
+
const RENDER_FAILED = Symbol.for("catalyst.renderFailed");
|
|
49
|
+
const asError = error => error && typeof error === "object" ? error : new Error(String(error));
|
|
50
|
+
const markRenderFailure = error => {
|
|
51
|
+
const err = asError(error);
|
|
52
|
+
err[RENDER_FAILED] = true;
|
|
53
|
+
// Already reported by the render path; a thrown string arrives here as a
|
|
54
|
+
// fresh Error the outer catch would otherwise report a second time.
|
|
55
|
+
reported.add(err);
|
|
56
|
+
return err;
|
|
57
|
+
};
|
|
58
|
+
const isRenderFailure = error => Boolean(error && typeof error === "object" && error[RENDER_FAILED]);
|
|
59
|
+
const logSSRError = (stage, error, req) => {
|
|
60
|
+
if (error && typeof error === "object") {
|
|
61
|
+
if (reported.has(error)) return;
|
|
62
|
+
reported.add(error);
|
|
63
|
+
}
|
|
36
64
|
const wrapped = wrapSSRError(stage, error);
|
|
65
|
+
|
|
66
|
+
// This module is bundled into the app's build, so it cannot reach
|
|
67
|
+
// catalyst-core's terminal renderer by path. The server that loads it
|
|
68
|
+
// provides one (see expressServer.ts); a custom server without it gets the
|
|
69
|
+
// registry's plain formatting.
|
|
70
|
+
const report = globalThis.__catalystReportRuntimeError;
|
|
71
|
+
if (typeof report === "function") {
|
|
72
|
+
const where = req ? `${req.method} ${req.originalUrl}` : null;
|
|
73
|
+
report({
|
|
74
|
+
error,
|
|
75
|
+
code: wrapped.code,
|
|
76
|
+
docs: wrapped.docUrl,
|
|
77
|
+
scope: [where, STAGE_LABEL[stage]].filter(Boolean).join(" · ")
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Debug mode's boxed output already embeds the full stack trace (see
|
|
82
|
+
// errors/index.js#formatDebug), so only default/verbose need the original
|
|
83
|
+
// error printed separately to avoid losing the stack.
|
|
37
84
|
const debugEnv = outputMode === "debug" ? getDebugEnvInfo() : undefined;
|
|
38
85
|
console.error(formatError(wrapped, outputMode, debugEnv));
|
|
39
86
|
if (outputMode !== "debug") console.error(error);
|
|
40
87
|
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Finish a request whose handling failed. Without this a render error left the
|
|
91
|
+
* response open: the browser spun until it timed out and the terminal was the
|
|
92
|
+
* only place the failure showed.
|
|
93
|
+
*/
|
|
94
|
+
const sendFailure = (res, error) => {
|
|
95
|
+
// Plain Node response calls, and never a throw: failing while reporting a
|
|
96
|
+
// failure would turn one error into an unhandled rejection.
|
|
97
|
+
try {
|
|
98
|
+
if (res.headersSent) {
|
|
99
|
+
if (!res.writableEnded) res.end();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const detail = process.env.NODE_ENV !== "production" && error ? `\n\n${error?.message || String(error)}\n\nThe full error is in the terminal running catalyst start.` : "";
|
|
103
|
+
res.statusCode = 500;
|
|
104
|
+
res.setHeader?.("content-type", "text/plain; charset=utf-8");
|
|
105
|
+
res.end(`Internal Server Error${detail}`);
|
|
106
|
+
} catch {
|
|
107
|
+
// The connection is already gone; nothing left to tell it.
|
|
108
|
+
}
|
|
109
|
+
};
|
|
41
110
|
const DEFAULT_SAFE_AREA_INSETS = {
|
|
42
111
|
top: 0,
|
|
43
112
|
right: 0,
|
|
@@ -286,6 +355,10 @@ const _renderMarkUp = async (errorCode, req, res, metaTags, fetcherData, store,
|
|
|
286
355
|
cb();
|
|
287
356
|
}
|
|
288
357
|
});
|
|
358
|
+
// destroy(error) below emits "error" on this stream. Unhandled, that
|
|
359
|
+
// became an uncaughtException per render on top of the report
|
|
360
|
+
// already printed; the failure is handled through reject().
|
|
361
|
+
tail.on("error", () => {});
|
|
289
362
|
tail.pipe(res);
|
|
290
363
|
const {
|
|
291
364
|
pipe
|
|
@@ -299,7 +372,7 @@ const _renderMarkUp = async (errorCode, req, res, metaTags, fetcherData, store,
|
|
|
299
372
|
resolve();
|
|
300
373
|
},
|
|
301
374
|
onError(error) {
|
|
302
|
-
logSSRError("RENDER", error);
|
|
375
|
+
logSSRError("RENDER", error, req);
|
|
303
376
|
safeCall(onRenderError, {
|
|
304
377
|
req,
|
|
305
378
|
res,
|
|
@@ -308,20 +381,20 @@ const _renderMarkUp = async (errorCode, req, res, metaTags, fetcherData, store,
|
|
|
308
381
|
});
|
|
309
382
|
cleanupSafeArea();
|
|
310
383
|
tail.destroy(error);
|
|
311
|
-
reject(error);
|
|
384
|
+
reject(markRenderFailure(error));
|
|
312
385
|
}
|
|
313
386
|
});
|
|
314
387
|
});
|
|
315
388
|
} catch (error) {
|
|
316
389
|
cleanupSafeArea();
|
|
317
|
-
logSSRError("RENDER", error);
|
|
390
|
+
logSSRError("RENDER", error, req);
|
|
318
391
|
safeCall(onRenderError, {
|
|
319
392
|
req,
|
|
320
393
|
res,
|
|
321
394
|
store,
|
|
322
395
|
error
|
|
323
396
|
});
|
|
324
|
-
return Promise.reject(error);
|
|
397
|
+
return Promise.reject(markRenderFailure(error));
|
|
325
398
|
}
|
|
326
399
|
};
|
|
327
400
|
const tracedRenderMarkUp = withObservability(SSR_SERVICE, _renderMarkUp, "renderMarkUp");
|
|
@@ -412,7 +485,8 @@ async function _handler(req, res) {
|
|
|
412
485
|
await tracedRenderMarkUp(null, req, res, allTags, fetcherData, store, allMatches, context, chunkExtractor);
|
|
413
486
|
}
|
|
414
487
|
} catch (error) {
|
|
415
|
-
|
|
488
|
+
if (isRenderFailure(error)) throw error;
|
|
489
|
+
logSSRError("FETCHER", error, req);
|
|
416
490
|
safeCall(onFetcherError, {
|
|
417
491
|
req,
|
|
418
492
|
res,
|
|
@@ -424,7 +498,8 @@ async function _handler(req, res) {
|
|
|
424
498
|
await tracedRenderMarkUp(404, req, res, allTags, fetcherData, store, allMatches, context, chunkExtractor);
|
|
425
499
|
}
|
|
426
500
|
} catch (error) {
|
|
427
|
-
|
|
501
|
+
if (isRenderFailure(error)) throw error;
|
|
502
|
+
logSSRError("SERVER_SIDE_FUNCTION", error, req);
|
|
428
503
|
safeCall(onAppServerSideError, {
|
|
429
504
|
req,
|
|
430
505
|
res,
|
|
@@ -436,12 +511,13 @@ async function _handler(req, res) {
|
|
|
436
511
|
await tracedRenderMarkUp(error.status_code, req, res, allTags, fetcherData, store, allMatches, context, chunkExtractor);
|
|
437
512
|
}
|
|
438
513
|
} catch (error) {
|
|
439
|
-
logSSRError("REQUEST_HANDLING", error);
|
|
514
|
+
logSSRError("REQUEST_HANDLING", error, req);
|
|
440
515
|
safeCall(onRequestError, {
|
|
441
516
|
req,
|
|
442
517
|
res,
|
|
443
518
|
error
|
|
444
519
|
});
|
|
520
|
+
sendFailure(res, error);
|
|
445
521
|
}
|
|
446
522
|
}
|
|
447
523
|
const handler = withObservability(SSR_SERVICE, _handler, "handler");
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import pc from "ansis";
|
|
2
1
|
import { createError, formatError, ERROR_CODES } from "../../errors/index.js";
|
|
3
2
|
import { resolveOutputMode, getDebugEnvInfo } from "../../scripts/scriptUtils.js";
|
|
4
3
|
|
|
@@ -17,13 +16,19 @@ const outputMode = resolveOutputMode([], process.env);
|
|
|
17
16
|
// single error and fall back, keeping the request alive.
|
|
18
17
|
// `handleError` below is the shared "log it, in whatever output mode" helper
|
|
19
18
|
// those in-server sites use.
|
|
20
|
-
|
|
19
|
+
// The default view is the CLI's diagnostic block. This module is also bundled
|
|
20
|
+
// into the app's SSR build, where the CLI is not reachable by a relative path,
|
|
21
|
+
// so the server process installs the renderer as a global (preServerInit.js,
|
|
22
|
+
// the first module it loads). Without it, or under --verbose/--debug, the
|
|
23
|
+
// registry's own format is used.
|
|
24
|
+
const renderError = e => {
|
|
25
|
+
const render = globalThis.__catalystRenderRegistryError;
|
|
26
|
+
if (outputMode === "default" && typeof render === "function") return render(e, "server");
|
|
21
27
|
const debugEnv = outputMode === "debug" ? getDebugEnvInfo() : undefined;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
28
|
+
return formatError(e, outputMode, debugEnv);
|
|
29
|
+
};
|
|
30
|
+
const handleError = e => {
|
|
31
|
+
console.log(renderError(e));
|
|
27
32
|
};
|
|
28
33
|
const validatePreInitServer = fn => {
|
|
29
34
|
if (!fn) return createError(ERROR_CODES.PREFLIGHT_PRE_SERVER_INIT_MISSING);
|
|
@@ -112,8 +117,7 @@ const safeCall = async (fn, ...args) => {
|
|
|
112
117
|
const wrapped = createError(ERROR_CODES.PROCESS_USER_HOOK_FAILED, {
|
|
113
118
|
cause: e
|
|
114
119
|
});
|
|
115
|
-
|
|
116
|
-
console.error(formatError(wrapped, outputMode, debugEnv));
|
|
120
|
+
console.error(renderError(wrapped));
|
|
117
121
|
}
|
|
118
122
|
};
|
|
119
123
|
|
|
@@ -132,8 +136,7 @@ const safeCallNamed = async (hookName, fn, ...args) => {
|
|
|
132
136
|
details: `The "${hookName}" hook threw. See the cause below.`,
|
|
133
137
|
cause: e
|
|
134
138
|
});
|
|
135
|
-
|
|
136
|
-
console.error(formatError(wrapped, outputMode, debugEnv));
|
|
139
|
+
console.error(renderError(wrapped));
|
|
137
140
|
}
|
|
138
141
|
};
|
|
139
142
|
export { handleError, validateConfigFile, validateConfigureStore, validateCustomDocument, validateGetRoutes, validatePackageJson, validateReducerFunction, validateModuleAlias, validatePreInitServer, validateMiddleware, safeCall, safeCallNamed };
|
|
@@ -199,7 +199,7 @@ export function manifestCategorizationPlugin(options = {}) {
|
|
|
199
199
|
if (matchedViteEntry && matchedViteKey) {
|
|
200
200
|
newCategorizedChunks[category][matchedViteKey] = { ...matchedViteEntry }
|
|
201
201
|
} else {
|
|
202
|
-
console.
|
|
202
|
+
console.debug(`No manifest match for ${chunkName}; keeping original structure`)
|
|
203
203
|
newCategorizedChunks[category][chunkName] = chunkData
|
|
204
204
|
}
|
|
205
205
|
}
|
|
@@ -405,7 +405,7 @@ export function manifestCategorizationPlugin(options = {}) {
|
|
|
405
405
|
}
|
|
406
406
|
} catch (err) {
|
|
407
407
|
// nosemgrep: javascript.lang.security.audit.unsafe-formatstring.unsafe-formatstring - importPath comes from the app's own module import graph resolved by Vite at build time, not attacker-controlled; console.log here does no printf-style substitution.
|
|
408
|
-
console.
|
|
408
|
+
console.debug(`Could not resolve ${importPath}: ${err.message}`)
|
|
409
409
|
}
|
|
410
410
|
})
|
|
411
411
|
)
|
|
@@ -430,16 +430,18 @@ export function manifestCategorizationPlugin(options = {}) {
|
|
|
430
430
|
const manifestContent = fs.readFileSync(manifestPath, "utf8")
|
|
431
431
|
viteManifest = JSON.parse(manifestContent)
|
|
432
432
|
} else {
|
|
433
|
-
|
|
433
|
+
// Silent: this fires as a *consequence* of the bundle
|
|
434
|
+
// failing, so shouting about a missing manifest adds a
|
|
435
|
+
// second, misleading error on top of the real one.
|
|
434
436
|
return
|
|
435
437
|
}
|
|
436
438
|
} catch (e) {
|
|
437
|
-
|
|
439
|
+
// Same reasoning as above: a consequence, not the cause.
|
|
438
440
|
return
|
|
439
441
|
}
|
|
440
442
|
|
|
441
443
|
if (!processedManifest) {
|
|
442
|
-
console.
|
|
444
|
+
console.debug("No processed manifest available")
|
|
443
445
|
return
|
|
444
446
|
}
|
|
445
447
|
|
|
@@ -38,11 +38,18 @@ export function resolveDevFsAllow(devFs, frameworkPaths) {
|
|
|
38
38
|
* first so the framework-owned `hmr` and `fs` always win.
|
|
39
39
|
*/
|
|
40
40
|
export function buildDevServer(devServer = {}, { frameworkPaths, isProduction = false } = {}) {
|
|
41
|
-
//
|
|
41
|
+
// `base` is pulled out to keep it out of serverOverrides -- it is applied
|
|
42
|
+
// at the config root, not under `server` -- so it is unused by design.
|
|
43
|
+
// eslint-disable-next-line no-unused-vars
|
|
42
44
|
const { base, hmr, fs, ...serverOverrides } = devServer
|
|
45
|
+
// CATALYST_NO_HMR lets a one-shot consumer of this config (the offline
|
|
46
|
+
// manifest step, which spins up a throwaway server purely to load a
|
|
47
|
+
// module) opt out. isProduction is captured at module load, so it cannot
|
|
48
|
+
// be flipped by setting NODE_ENV just before createServer().
|
|
49
|
+
const hmrDisabled = isProduction || process.env.CATALYST_NO_HMR === "true"
|
|
43
50
|
return {
|
|
44
51
|
...serverOverrides,
|
|
45
|
-
hmr:
|
|
52
|
+
hmr: hmrDisabled ? false : (hmr ?? true),
|
|
46
53
|
fs: { allow: resolveDevFsAllow(fs, frameworkPaths) },
|
|
47
54
|
}
|
|
48
55
|
}
|
|
@@ -31,6 +31,10 @@ const createClientConfig = async () => {
|
|
|
31
31
|
sourcemap: false,
|
|
32
32
|
manifest: true,
|
|
33
33
|
ssrManifest: true,
|
|
34
|
+
// Parallel client and server builds share this directory -- emptying it
|
|
35
|
+
// would delete the other bundle. Declared explicitly so Vite does not
|
|
36
|
+
// warn about an outDir outside the project root.
|
|
37
|
+
emptyOutDir: false,
|
|
34
38
|
outDir: path.join(process.env.src_path, process.env.BUILD_OUTPUT_PATH || "build"),
|
|
35
39
|
|
|
36
40
|
// cssCodeSplit: true (default) — each chunk gets its own CSS file.
|
|
@@ -38,6 +42,20 @@ const createClientConfig = async () => {
|
|
|
38
42
|
// per-chunk CSS is fine — more files but each is small and cacheable.
|
|
39
43
|
|
|
40
44
|
rollupOptions: {
|
|
45
|
+
// Advisory warnings about third-party code the app author
|
|
46
|
+
// cannot act on: a dependency's misplaced /*#__PURE__*/ comment
|
|
47
|
+
// and unused re-exports from our own entry. Genuine problems
|
|
48
|
+
// (unresolved imports, circular deps, missing exports) still
|
|
49
|
+
// surface.
|
|
50
|
+
onwarn(warning, defaultHandler) {
|
|
51
|
+
const ignored = new Set([
|
|
52
|
+
"INVALID_ANNOTATION",
|
|
53
|
+
"UNUSED_EXTERNAL_IMPORT",
|
|
54
|
+
"MODULE_LEVEL_DIRECTIVE",
|
|
55
|
+
])
|
|
56
|
+
if (ignored.has(warning.code)) return
|
|
57
|
+
defaultHandler(warning)
|
|
58
|
+
},
|
|
41
59
|
input: {
|
|
42
60
|
main: path.join(process.env.src_path, "client/index.js"),
|
|
43
61
|
},
|
|
@@ -74,7 +92,7 @@ const createClientConfig = async () => {
|
|
|
74
92
|
return undefined
|
|
75
93
|
}
|
|
76
94
|
if (
|
|
77
|
-
/[\\/]node_modules[\\/](react|react-dom|scheduler
|
|
95
|
+
/[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/.test(
|
|
78
96
|
id
|
|
79
97
|
)
|
|
80
98
|
) {
|
package/dist/vite/vite.config.js
CHANGED
|
@@ -51,15 +51,27 @@ function scssModulesPlugin() {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// Automatic runtime so the output imports react/jsx-(dev-)runtime: plugin-react
|
|
55
|
+
// only adds Fast Refresh to .js files that import it. Classic createElement
|
|
56
|
+
// output leaves the module without an HMR boundary, forcing full page reloads.
|
|
54
57
|
function jsxInJsPlugin() {
|
|
58
|
+
let isProduction = process.env.NODE_ENV === "production"
|
|
59
|
+
|
|
55
60
|
return {
|
|
56
61
|
name: "vite-plugin-jsx-in-js",
|
|
57
62
|
enforce: "pre",
|
|
63
|
+
configResolved(config) {
|
|
64
|
+
isProduction = config.isProduction
|
|
65
|
+
},
|
|
58
66
|
async transform(code, id) {
|
|
59
67
|
if (!id.endsWith(".js")) return null
|
|
60
68
|
if (id.includes("node_modules")) return null
|
|
61
69
|
if (!/</.test(code)) return null
|
|
62
|
-
return transformWithEsbuild(code, id, {
|
|
70
|
+
return transformWithEsbuild(code, id, {
|
|
71
|
+
loader: "jsx",
|
|
72
|
+
jsx: "automatic",
|
|
73
|
+
jsxDev: !isProduction,
|
|
74
|
+
})
|
|
63
75
|
},
|
|
64
76
|
}
|
|
65
77
|
}
|
|
@@ -123,14 +135,20 @@ import { imageUrl, fontUrl } from "./scssParams.js"
|
|
|
123
135
|
// options — so anything else is left alone and Vite falls back to its normal
|
|
124
136
|
// auto-loaded postcss.config.js resolution.
|
|
125
137
|
const resolvePostcssPlugins = () => {
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
// .cjs first: an app with "type": "module" (the scaffold default) must
|
|
139
|
+
// name a CommonJS config .cjs, and a .js one is ESM there.
|
|
140
|
+
const configPath = ["postcss.config.cjs", "postcss.config.js"]
|
|
141
|
+
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal - process.env.src_path is the app root set by the framework's own build tooling, not request/user input.
|
|
142
|
+
.map((name) => path.join(process.env.src_path, name))
|
|
143
|
+
.find((candidate) => fs.existsSync(candidate))
|
|
144
|
+
if (!configPath) return null
|
|
129
145
|
|
|
130
146
|
const appRequire = createRequire(configPath)
|
|
131
147
|
|
|
132
148
|
try {
|
|
133
|
-
const
|
|
149
|
+
const loaded = appRequire(configPath)
|
|
150
|
+
// require() of an ESM config returns its namespace.
|
|
151
|
+
const userConfig = loaded?.__esModule || loaded?.default ? loaded.default : loaded
|
|
134
152
|
const pluginsConfig = userConfig?.plugins
|
|
135
153
|
|
|
136
154
|
if (!pluginsConfig || Array.isArray(pluginsConfig) || typeof pluginsConfig !== "object") {
|
|
@@ -256,7 +274,27 @@ const getScssResourcesImport = () => {
|
|
|
256
274
|
|
|
257
275
|
// Pre-bundling upfront avoids Vite discovering deps at runtime which causes
|
|
258
276
|
// cascading full-page reloads and "stuck in loading" on dev server.
|
|
259
|
-
|
|
277
|
+
/**
|
|
278
|
+
* Keep only the packages this app can actually resolve.
|
|
279
|
+
*
|
|
280
|
+
* The list below is a superset covering every dependency a Catalyst app might
|
|
281
|
+
* use. Vite logs "Failed to resolve dependency: X, present in
|
|
282
|
+
* 'optimizeDeps.include'" for each entry it cannot find, so an app that uses
|
|
283
|
+
* half of them opened with 23 warning lines it could do nothing about.
|
|
284
|
+
*/
|
|
285
|
+
function resolvableFrom(candidates, appRoot) {
|
|
286
|
+
const resolver = createRequire(path.join(appRoot, "package.json"))
|
|
287
|
+
return candidates.filter((name) => {
|
|
288
|
+
try {
|
|
289
|
+
resolver.resolve(name)
|
|
290
|
+
return true
|
|
291
|
+
} catch (error) {
|
|
292
|
+
return false
|
|
293
|
+
}
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const candidateBrowserDeps = [
|
|
260
298
|
"react",
|
|
261
299
|
"react-dom",
|
|
262
300
|
"react-dom/client",
|
|
@@ -266,9 +304,7 @@ const browserOptimizeDeps = [
|
|
|
266
304
|
"redux",
|
|
267
305
|
"redux-thunk",
|
|
268
306
|
"axios",
|
|
269
|
-
"react-helmet-async",
|
|
270
307
|
"react-google-recaptcha",
|
|
271
|
-
"react-fast-compare",
|
|
272
308
|
"history",
|
|
273
309
|
"lottie-web",
|
|
274
310
|
// Pre-bundle alongside React so esbuild marks react/react-router as external
|
|
@@ -295,7 +331,6 @@ const browserOptimizeDeps = [
|
|
|
295
331
|
// first navigation that pulls them in doesn't trigger a full-page reload.
|
|
296
332
|
"react-loadable-visibility",
|
|
297
333
|
"react-detect-offline",
|
|
298
|
-
"react-side-effect",
|
|
299
334
|
"react-async-script",
|
|
300
335
|
"normalize.css",
|
|
301
336
|
]
|
|
@@ -355,14 +390,12 @@ export const sharedViteConfig = {
|
|
|
355
390
|
// the bundler never tries to resolve the optional (opt-in) OTEL packages.
|
|
356
391
|
external: nodeOnlyExternalDeps,
|
|
357
392
|
optimizeDeps: {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
"shallowequal",
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
"redux-logger",
|
|
365
|
-
],
|
|
393
|
+
// Same treatment as the browser list: only include what this app
|
|
394
|
+
// can resolve, or Vite warns about every entry it cannot find.
|
|
395
|
+
include: resolvableFrom(
|
|
396
|
+
["invariant", "shallowequal", "prop-types", "redux-thunk", "redux-logger"],
|
|
397
|
+
process.env.src_path
|
|
398
|
+
),
|
|
366
399
|
// Prevent pre-bundling React ecosystem packages for SSR to avoid duplicate
|
|
367
400
|
// React instances. Pre-bundled copies in .vite/deps_ssr/ create a separate
|
|
368
401
|
// React instance from node_modules/react used by react-dom/server, causing
|
|
@@ -429,8 +462,18 @@ export const sharedViteConfig = {
|
|
|
429
462
|
path.join(process.env.src_path, "client/index.js"),
|
|
430
463
|
path.join(process.env.src_path, "src/**/*.{js,jsx,ts,tsx}"),
|
|
431
464
|
],
|
|
432
|
-
include:
|
|
433
|
-
|
|
465
|
+
include: resolvableFrom(candidateBrowserDeps, process.env.src_path),
|
|
466
|
+
// catalyst-core/webmcp (and its shim subpath) must share the SAME
|
|
467
|
+
// pre-bundled module instance as catalyst-core's main entry:
|
|
468
|
+
// WebMcpProvider reads router state through a React context
|
|
469
|
+
// (OneMgRouterContext, via useRouterState()) that RouterDataProvider
|
|
470
|
+
// populates. If esbuild's dep scanner pre-bundles the subpath as its
|
|
471
|
+
// own separate optimized chunk, that chunk gets its own
|
|
472
|
+
// `createContext()` call — a second, unlinked context instance that
|
|
473
|
+
// WebMcpProvider reads from empty, throwing "must be used within a
|
|
474
|
+
// RouterDataProvider" even though one genuinely is an ancestor.
|
|
475
|
+
// Same rationale as the existing ClientRouter exclusion below.
|
|
476
|
+
exclude: ["catalyst-core/router/ClientRouter", "catalyst-core/webmcp", "catalyst-core/webmcp/shim"],
|
|
434
477
|
esbuildOptions: {
|
|
435
478
|
format: "esm",
|
|
436
479
|
target: "node2022",
|
|
@@ -482,6 +525,16 @@ export const sharedViteConfig = {
|
|
|
482
525
|
},
|
|
483
526
|
}
|
|
484
527
|
|
|
528
|
+
// Every catalyst-core subpath export except the main entry and webmcp, as bare
|
|
529
|
+
// specifiers ("catalyst-core/hooks"). Read from package.json so a new subpath
|
|
530
|
+
// stays externalized in dev SSR without touching this list.
|
|
531
|
+
function catalystCoreExternalSubpaths() {
|
|
532
|
+
const { exports = {} } = JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), "utf8"))
|
|
533
|
+
return Object.keys(exports)
|
|
534
|
+
.filter((key) => key.startsWith("./") && key !== "./package.json" && !key.startsWith("./webmcp"))
|
|
535
|
+
.map((key) => `catalyst-core/${key.slice(2)}`)
|
|
536
|
+
}
|
|
537
|
+
|
|
485
538
|
// ─── Dev server config ────────────────────────────────────────────────────────
|
|
486
539
|
// Async factory so it can read the app's buildConfig.devServer for `base`, HMR
|
|
487
540
|
// and other Vite server options. `base` comes solely from devServer.base; the
|
|
@@ -494,6 +547,30 @@ export default defineConfig(async () => {
|
|
|
494
547
|
return {
|
|
495
548
|
...sharedViteConfig,
|
|
496
549
|
base: resolveDevBase(devServer),
|
|
550
|
+
// Dev SSR only: transform catalyst-core/webmcp through Vite instead of
|
|
551
|
+
// externalizing it. Node cannot load its .jsx files, and its provider
|
|
552
|
+
// must share one module record (one createContext()) with the main
|
|
553
|
+
// entry, which Vite always transforms (.jsx is never externalized).
|
|
554
|
+
// ssr.noExternal only matches package names, so the package goes in
|
|
555
|
+
// noExternal and every other subpath is pinned external: CJS subpaths
|
|
556
|
+
// (hooks, WebBridge, sentry) would fail to inline ("exports is not
|
|
557
|
+
// defined"), and apps that do not import webmcp resolve exactly as
|
|
558
|
+
// before. Keyed on NODE_ENV, not Vite's `command`: `catalyst build`
|
|
559
|
+
// also spins up this config for the offline manifest, and built
|
|
560
|
+
// output runs under plain Node ESM, one module record per file.
|
|
561
|
+
ssr: {
|
|
562
|
+
...sharedViteConfig.ssr,
|
|
563
|
+
noExternal: [
|
|
564
|
+
...new Set([
|
|
565
|
+
...(sharedViteConfig.ssr?.noExternal || []),
|
|
566
|
+
...(isProduction ? [] : ["catalyst-core"]),
|
|
567
|
+
]),
|
|
568
|
+
],
|
|
569
|
+
external: [
|
|
570
|
+
...(sharedViteConfig.ssr?.external || []),
|
|
571
|
+
...(isProduction ? [] : catalystCoreExternalSubpaths()),
|
|
572
|
+
],
|
|
573
|
+
},
|
|
497
574
|
server: buildDevServer(devServer, {
|
|
498
575
|
frameworkPaths: [process.env.src_path, __dirname],
|
|
499
576
|
isProduction,
|
|
@@ -28,8 +28,15 @@ export default defineConfig(async () => {
|
|
|
28
28
|
...(customViteConfig?.ssrPlugins || []),
|
|
29
29
|
],
|
|
30
30
|
|
|
31
|
+
// The CLI prints its own build summary and asset table, so Vite's
|
|
32
|
+
// duplicate table and rollup's advisory warnings are pure noise.
|
|
33
|
+
|
|
31
34
|
build: {
|
|
32
35
|
...baseConfig.build,
|
|
36
|
+
// Parallel client and server builds share this directory -- emptying it
|
|
37
|
+
// would delete the other bundle. Declared explicitly so Vite does not
|
|
38
|
+
// warn about an outDir outside the project root.
|
|
39
|
+
emptyOutDir: false,
|
|
33
40
|
outDir: path.join(process.env.src_path, process.env.BUILD_OUTPUT_PATH || "build"),
|
|
34
41
|
target: "es2022",
|
|
35
42
|
minify: "esbuild",
|
|
@@ -38,6 +45,20 @@ export default defineConfig(async () => {
|
|
|
38
45
|
ssrManifest: false,
|
|
39
46
|
|
|
40
47
|
rollupOptions: {
|
|
48
|
+
// Advisory warnings about third-party code the app author
|
|
49
|
+
// cannot act on: a dependency's misplaced /*#__PURE__*/ comment
|
|
50
|
+
// and unused re-exports from our own entry. Genuine problems
|
|
51
|
+
// (unresolved imports, circular deps, missing exports) still
|
|
52
|
+
// surface.
|
|
53
|
+
onwarn(warning, defaultHandler) {
|
|
54
|
+
const ignored = new Set([
|
|
55
|
+
"INVALID_ANNOTATION",
|
|
56
|
+
"UNUSED_EXTERNAL_IMPORT",
|
|
57
|
+
"MODULE_LEVEL_DIRECTIVE",
|
|
58
|
+
])
|
|
59
|
+
if (ignored.has(warning.code)) return
|
|
60
|
+
defaultHandler(warning)
|
|
61
|
+
},
|
|
41
62
|
// Belt-and-suspenders with ssr.external: ensures the opt-in OTEL /
|
|
42
63
|
// node-only packages (and their transitive @opentelemetry/* deps) are
|
|
43
64
|
// never resolved/bundled, even though they may not be installed.
|