@inertiajs/core 2.3.17 → 3.0.0-beta.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/index.js +1016 -298
- package/dist/index.js.map +4 -4
- package/dist/server.js +277 -56
- package/dist/server.js.map +4 -4
- package/dist/ssrErrors.js +197 -0
- package/dist/ssrErrors.js.map +7 -0
- package/package.json +28 -14
- package/types/axiosHttpClient.d.ts +16 -0
- package/types/dialog.d.ts +4 -0
- package/types/domUtils.d.ts +1 -1
- package/types/events.d.ts +2 -2
- package/types/http.d.ts +38 -0
- package/types/httpErrors.d.ts +16 -0
- package/types/httpHandlers.d.ts +15 -0
- package/types/index.d.ts +9 -2
- package/types/layout.d.ts +38 -0
- package/types/page.d.ts +4 -1
- package/types/progress.d.ts +2 -8
- package/types/queryString.d.ts +13 -0
- package/types/request.d.ts +12 -5
- package/types/requestParams.d.ts +2 -3
- package/types/requestStream.d.ts +3 -1
- package/types/response.d.ts +17 -5
- package/types/router.d.ts +8 -3
- package/types/server.d.ts +2 -1
- package/types/ssrErrors.d.ts +29 -0
- package/types/ssrUtils.d.ts +2 -0
- package/types/types.d.ts +131 -54
- package/types/url.d.ts +1 -0
- package/types/xhrHttpClient.d.ts +12 -0
- package/dist/index.esm.js +0 -4119
- package/dist/index.esm.js.map +0 -7
- package/dist/server.esm.js +0 -61
- package/dist/server.esm.js.map +0 -7
- package/types/modal.d.ts +0 -12
package/dist/server.js
CHANGED
|
@@ -1,42 +1,236 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
// src/server.ts
|
|
2
|
+
import { originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
|
|
3
|
+
import { createServer } from "http";
|
|
4
|
+
import cluster from "node:cluster";
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { availableParallelism } from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import * as process2 from "process";
|
|
9
|
+
|
|
10
|
+
// src/ssrErrors.ts
|
|
11
|
+
var sourceMapResolver = null;
|
|
12
|
+
function setSourceMapResolver(resolver) {
|
|
13
|
+
sourceMapResolver = resolver;
|
|
14
|
+
}
|
|
15
|
+
var BROWSER_APIS = {
|
|
16
|
+
// Global objects
|
|
17
|
+
window: "The global window object",
|
|
18
|
+
document: "The DOM document object",
|
|
19
|
+
navigator: "The navigator object",
|
|
20
|
+
location: "The location object",
|
|
21
|
+
history: "The browser history API",
|
|
22
|
+
screen: "The screen object",
|
|
23
|
+
localStorage: "Browser local storage",
|
|
24
|
+
sessionStorage: "Browser session storage",
|
|
25
|
+
// Viewport properties (accessed via window.X)
|
|
26
|
+
innerWidth: "Browser viewport width",
|
|
27
|
+
innerHeight: "Browser viewport height",
|
|
28
|
+
outerWidth: "Browser window width",
|
|
29
|
+
outerHeight: "Browser window height",
|
|
30
|
+
scrollX: "Horizontal scroll position",
|
|
31
|
+
scrollY: "Vertical scroll position",
|
|
32
|
+
devicePixelRatio: "The device pixel ratio",
|
|
33
|
+
matchMedia: "The matchMedia function",
|
|
34
|
+
// Observers (commonly instantiated at module level)
|
|
35
|
+
IntersectionObserver: "The IntersectionObserver API",
|
|
36
|
+
ResizeObserver: "The ResizeObserver API",
|
|
37
|
+
MutationObserver: "The MutationObserver API",
|
|
38
|
+
// Timing functions (commonly called at module level)
|
|
39
|
+
requestAnimationFrame: "The requestAnimationFrame function",
|
|
40
|
+
requestIdleCallback: "The requestIdleCallback function",
|
|
41
|
+
// Constructors that might be used at module level
|
|
42
|
+
Image: "The Image constructor",
|
|
43
|
+
Audio: "The Audio constructor",
|
|
44
|
+
Worker: "The Worker constructor",
|
|
45
|
+
BroadcastChannel: "The BroadcastChannel constructor",
|
|
46
|
+
// Network (older Node.js versions)
|
|
47
|
+
fetch: "The fetch API",
|
|
48
|
+
XMLHttpRequest: "The XMLHttpRequest API"
|
|
11
49
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
50
|
+
function detectBrowserApi(error) {
|
|
51
|
+
const message = error.message.toLowerCase();
|
|
52
|
+
for (const api of Object.keys(BROWSER_APIS)) {
|
|
53
|
+
const patterns = [
|
|
54
|
+
`${api.toLowerCase()} is not defined`,
|
|
55
|
+
`'${api.toLowerCase()}' is not defined`,
|
|
56
|
+
`"${api.toLowerCase()}" is not defined`,
|
|
57
|
+
`cannot read properties of undefined (reading '${api.toLowerCase()}')`,
|
|
58
|
+
`cannot read property '${api.toLowerCase()}'`
|
|
59
|
+
];
|
|
60
|
+
if (patterns.some((pattern) => message.includes(pattern))) {
|
|
61
|
+
return api;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function isComponentResolutionError(error) {
|
|
67
|
+
const message = error.message.toLowerCase();
|
|
68
|
+
return message.includes("cannot find module") || message.includes("failed to resolve") || message.includes("module not found") || message.includes("could not resolve");
|
|
69
|
+
}
|
|
70
|
+
var LIFECYCLE_HOOKS = "onMounted/useEffect/onMount";
|
|
71
|
+
function getBrowserApiHint(api) {
|
|
72
|
+
const apiDescription = BROWSER_APIS[api] || `The "${api}" object`;
|
|
73
|
+
if (["localStorage", "sessionStorage"].includes(api)) {
|
|
74
|
+
return `${apiDescription} doesn't exist in Node.js. Check "typeof ${api} !== 'undefined'" before using it, or move the code to a ${LIFECYCLE_HOOKS} lifecycle hook.`;
|
|
75
|
+
}
|
|
76
|
+
if (["window", "document"].includes(api)) {
|
|
77
|
+
return `${apiDescription} doesn't exist in Node.js. Wrap browser-specific code in a ${LIFECYCLE_HOOKS} lifecycle hook, or check "typeof ${api} !== 'undefined'" before using it.`;
|
|
78
|
+
}
|
|
79
|
+
if (["IntersectionObserver", "ResizeObserver", "MutationObserver"].includes(api)) {
|
|
80
|
+
return `${apiDescription} doesn't exist in Node.js. Create observers inside a ${LIFECYCLE_HOOKS} lifecycle hook, not at the module level.`;
|
|
17
81
|
}
|
|
18
|
-
|
|
82
|
+
if (["fetch", "XMLHttpRequest"].includes(api)) {
|
|
83
|
+
return `${apiDescription} may not be available in all Node.js versions. For SSR, ensure data fetching happens on the server (in your controller) and is passed as props, or use a ${LIFECYCLE_HOOKS} hook for client-side fetching.`;
|
|
84
|
+
}
|
|
85
|
+
return `${apiDescription} doesn't exist in Node.js. Move this code to a ${LIFECYCLE_HOOKS} lifecycle hook, or guard it with "typeof ${api} !== 'undefined'".`;
|
|
86
|
+
}
|
|
87
|
+
function getComponentResolutionHint(component) {
|
|
88
|
+
const componentPart = component ? ` "${component}"` : "";
|
|
89
|
+
return `Could not resolve component${componentPart}. Check that the file exists and the path is correct. Ensure the component name matches the file name exactly (case-sensitive).`;
|
|
90
|
+
}
|
|
91
|
+
function getRenderErrorHint() {
|
|
92
|
+
return "An error occurred while rendering the component. Check the component for browser-specific code that runs during initialization. Move any code that accesses browser APIs to a lifecycle hook.";
|
|
93
|
+
}
|
|
94
|
+
function extractSourceLocation(stack) {
|
|
95
|
+
if (!stack) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
for (const line of stack.split("\n")) {
|
|
99
|
+
if (!line.includes("at ")) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (line.includes("node_modules") || line.includes("node:")) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
let match = line.match(/\(([^)]+):(\d+):(\d+)\)/);
|
|
106
|
+
if (!match) {
|
|
107
|
+
match = line.match(/at\s+(?:file:\/\/)?(.+):(\d+):(\d+)\s*$/);
|
|
108
|
+
}
|
|
109
|
+
if (match) {
|
|
110
|
+
const file = match[1].replace(/^file:\/\//, "");
|
|
111
|
+
const lineNum = parseInt(match[2], 10);
|
|
112
|
+
const colNum = parseInt(match[3], 10);
|
|
113
|
+
if (sourceMapResolver) {
|
|
114
|
+
const resolved = sourceMapResolver(file, lineNum, colNum);
|
|
115
|
+
if (resolved) {
|
|
116
|
+
return `${resolved.file}:${resolved.line}:${resolved.column}`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return `${file}:${lineNum}:${colNum}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
function classifySSRError(error, component, url) {
|
|
125
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
126
|
+
const base = {
|
|
127
|
+
error: error.message,
|
|
128
|
+
component,
|
|
129
|
+
url,
|
|
130
|
+
stack: error.stack,
|
|
131
|
+
sourceLocation: extractSourceLocation(error.stack),
|
|
132
|
+
timestamp
|
|
133
|
+
};
|
|
134
|
+
const browserApi = detectBrowserApi(error);
|
|
135
|
+
if (browserApi) {
|
|
136
|
+
return {
|
|
137
|
+
...base,
|
|
138
|
+
type: "browser-api",
|
|
139
|
+
browserApi,
|
|
140
|
+
hint: getBrowserApiHint(browserApi)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (isComponentResolutionError(error)) {
|
|
144
|
+
return {
|
|
145
|
+
...base,
|
|
146
|
+
type: "component-resolution",
|
|
147
|
+
hint: getComponentResolutionHint(component)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
...base,
|
|
152
|
+
type: "render",
|
|
153
|
+
hint: getRenderErrorHint()
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
var colors = {
|
|
157
|
+
reset: "\x1B[0m",
|
|
158
|
+
red: "\x1B[31m",
|
|
159
|
+
yellow: "\x1B[33m",
|
|
160
|
+
cyan: "\x1B[36m",
|
|
161
|
+
dim: "\x1B[2m",
|
|
162
|
+
bold: "\x1B[1m",
|
|
163
|
+
bgRed: "\x1B[41m",
|
|
164
|
+
white: "\x1B[37m"
|
|
19
165
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
|
|
166
|
+
function makeRelative(path2, root) {
|
|
167
|
+
const base = root ?? process.cwd();
|
|
168
|
+
if (path2.startsWith(base + "/")) {
|
|
169
|
+
return path2.slice(base.length + 1);
|
|
170
|
+
}
|
|
171
|
+
return path2;
|
|
172
|
+
}
|
|
173
|
+
function formatConsoleError(classified, root, handleErrors = true, suppressedWarnings = []) {
|
|
174
|
+
if (!handleErrors) {
|
|
175
|
+
const component = classified.component ? `[${classified.component}]` : "";
|
|
176
|
+
return `SSR Error ${component}: ${classified.error}`;
|
|
177
|
+
}
|
|
178
|
+
const componentPart = classified.component ? ` ${colors.cyan}${classified.component}${colors.reset}` : "";
|
|
179
|
+
const lines = [
|
|
180
|
+
"",
|
|
181
|
+
` ${colors.bgRed}${colors.white}${colors.bold} SSR ERROR ${colors.reset}${componentPart}`,
|
|
182
|
+
"",
|
|
183
|
+
` ${classified.error}`
|
|
184
|
+
];
|
|
185
|
+
if (classified.sourceLocation) {
|
|
186
|
+
const relativePath = makeRelative(classified.sourceLocation, root);
|
|
187
|
+
lines.push(` ${colors.dim}Source: ${relativePath}${colors.reset}`);
|
|
188
|
+
}
|
|
189
|
+
if (classified.url) {
|
|
190
|
+
lines.push(` ${colors.dim}URL: ${classified.url}${colors.reset}`);
|
|
191
|
+
}
|
|
192
|
+
lines.push("", ` ${colors.yellow}Hint${colors.reset} ${classified.hint}`, "");
|
|
193
|
+
if (classified.stack) {
|
|
194
|
+
lines.push(` ${colors.dim}${classified.stack.split("\n").join("\n ")}${colors.reset}`, "");
|
|
195
|
+
}
|
|
196
|
+
if (suppressedWarnings.length > 0) {
|
|
197
|
+
lines.push(` ${colors.dim}Suppressed ${suppressedWarnings.length} framework warning(s).${colors.reset}`, "");
|
|
198
|
+
}
|
|
199
|
+
return lines.join("\n");
|
|
200
|
+
}
|
|
29
201
|
|
|
30
202
|
// src/server.ts
|
|
31
|
-
var
|
|
32
|
-
|
|
33
|
-
|
|
203
|
+
var sourceMaps = /* @__PURE__ */ new Map();
|
|
204
|
+
setSourceMapResolver((file, line, column) => {
|
|
205
|
+
if (!file.includes("/ssr/") || !file.endsWith(".js")) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const mapFile = file + ".map";
|
|
209
|
+
if (!existsSync(mapFile)) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
let traceMap = sourceMaps.get(mapFile);
|
|
213
|
+
if (!traceMap) {
|
|
214
|
+
try {
|
|
215
|
+
const mapContent = readFileSync(mapFile, "utf-8");
|
|
216
|
+
traceMap = new TraceMap(mapContent);
|
|
217
|
+
sourceMaps.set(mapFile, traceMap);
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const original = originalPositionFor(traceMap, { line, column });
|
|
223
|
+
if (original.source) {
|
|
224
|
+
const mapDir = path.dirname(mapFile);
|
|
225
|
+
const resolvedPath = path.resolve(mapDir, original.source);
|
|
226
|
+
return {
|
|
227
|
+
file: resolvedPath,
|
|
228
|
+
line: original.line ?? line,
|
|
229
|
+
column: original.column ?? column
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
34
233
|
});
|
|
35
|
-
module.exports = __toCommonJS(server_exports);
|
|
36
|
-
var import_http = require("http");
|
|
37
|
-
var import_node_cluster = __toESM(require("node:cluster"), 1);
|
|
38
|
-
var import_node_os = require("node:os");
|
|
39
|
-
var process = __toESM(require("process"), 1);
|
|
40
234
|
var readableToString = (readable) => new Promise((resolve, reject) => {
|
|
41
235
|
let data = "";
|
|
42
236
|
readable.on("data", (chunk) => data += chunk);
|
|
@@ -44,49 +238,76 @@ var readableToString = (readable) => new Promise((resolve, reject) => {
|
|
|
44
238
|
readable.on("error", (err) => reject(err));
|
|
45
239
|
});
|
|
46
240
|
var server_default = (render, options) => {
|
|
47
|
-
const
|
|
48
|
-
const
|
|
241
|
+
const opts = typeof options === "number" ? { port: options } : options;
|
|
242
|
+
const { port = 13714, cluster: useCluster = false, handleErrors = true } = opts ?? {};
|
|
49
243
|
const log = (message) => {
|
|
50
244
|
console.log(
|
|
51
|
-
|
|
245
|
+
useCluster && !cluster.isPrimary ? `[${cluster.worker?.id ?? "N/A"} / ${cluster.worker?.process?.pid ?? "N/A"}] ${message}` : message
|
|
52
246
|
);
|
|
53
247
|
};
|
|
54
|
-
if (
|
|
248
|
+
if (useCluster && cluster.isPrimary) {
|
|
55
249
|
log("Primary Inertia SSR server process started...");
|
|
56
|
-
for (let i = 0; i <
|
|
57
|
-
|
|
250
|
+
for (let i = 0; i < availableParallelism(); i++) {
|
|
251
|
+
cluster.fork();
|
|
58
252
|
}
|
|
59
|
-
|
|
253
|
+
cluster.on("message", (_worker, message) => {
|
|
60
254
|
if (message === "shutdown") {
|
|
61
|
-
for (const id in
|
|
62
|
-
|
|
255
|
+
for (const id in cluster.workers) {
|
|
256
|
+
cluster.workers[id]?.kill();
|
|
63
257
|
}
|
|
64
|
-
|
|
258
|
+
process2.exit();
|
|
65
259
|
}
|
|
66
260
|
});
|
|
67
|
-
return;
|
|
261
|
+
return render;
|
|
68
262
|
}
|
|
263
|
+
const handleRender = async (request, response) => {
|
|
264
|
+
const page = JSON.parse(await readableToString(request));
|
|
265
|
+
const originalWarn = console.warn;
|
|
266
|
+
if (handleErrors) {
|
|
267
|
+
console.warn = () => {
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const result = await render(page);
|
|
272
|
+
response.writeHead(200, { "Content-Type": "application/json", Server: "Inertia.js SSR" });
|
|
273
|
+
response.write(JSON.stringify(result));
|
|
274
|
+
} catch (e) {
|
|
275
|
+
const error = e;
|
|
276
|
+
if (!handleErrors) {
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
const classified = classifySSRError(error, page.component, page.url);
|
|
280
|
+
console.error(formatConsoleError(classified));
|
|
281
|
+
response.writeHead(500, { "Content-Type": "application/json", Server: "Inertia.js SSR" });
|
|
282
|
+
response.write(JSON.stringify(classified));
|
|
283
|
+
} finally {
|
|
284
|
+
console.warn = originalWarn;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
69
287
|
const routes = {
|
|
70
288
|
"/health": async () => ({ status: "OK", timestamp: Date.now() }),
|
|
71
289
|
"/shutdown": async () => {
|
|
72
|
-
if (
|
|
73
|
-
|
|
290
|
+
if (cluster.isWorker) {
|
|
291
|
+
process2.send?.("shutdown");
|
|
74
292
|
}
|
|
75
|
-
|
|
293
|
+
process2.exit();
|
|
76
294
|
},
|
|
77
|
-
"/render":
|
|
295
|
+
"/render": handleRender,
|
|
78
296
|
"/404": async () => ({ status: "NOT_FOUND", timestamp: Date.now() })
|
|
79
297
|
};
|
|
80
|
-
|
|
81
|
-
const dispatchRoute = routes[request.url]
|
|
82
|
-
|
|
298
|
+
createServer(async (request, response) => {
|
|
299
|
+
const dispatchRoute = routes[request.url] ?? routes["/404"];
|
|
300
|
+
const result = await dispatchRoute(request, response);
|
|
301
|
+
if (!response.headersSent) {
|
|
83
302
|
response.writeHead(200, { "Content-Type": "application/json", Server: "Inertia.js SSR" });
|
|
84
|
-
response.write(JSON.stringify(
|
|
85
|
-
} catch (e) {
|
|
86
|
-
console.error(e);
|
|
303
|
+
response.write(JSON.stringify(result));
|
|
87
304
|
}
|
|
88
305
|
response.end();
|
|
89
|
-
}).listen(
|
|
90
|
-
log(`Starting SSR server on port ${
|
|
306
|
+
}).listen(port, () => log("Inertia SSR server started."));
|
|
307
|
+
log(`Starting SSR server on port ${port}...`);
|
|
308
|
+
return render;
|
|
309
|
+
};
|
|
310
|
+
export {
|
|
311
|
+
server_default as default
|
|
91
312
|
};
|
|
92
313
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/server.ts"],
|
|
4
|
-
"sourcesContent": ["import { createServer, IncomingMessage } from 'http'\nimport cluster from 'node:cluster'\nimport { availableParallelism } from 'node:os'\nimport * as process from 'process'\nimport { InertiaAppResponse, Page } from './types'\n\ntype AppCallback = (page: Page) => InertiaAppResponse\ntype RouteHandler = (request: IncomingMessage) => Promise<unknown>\ntype ServerOptions = {\n port?: number\n cluster?: boolean\n}\ntype Port = number\n\nconst readableToString: (readable: IncomingMessage) => Promise<string> = (readable) =>\n new Promise((resolve, reject) => {\n let data = ''\n readable.on('data', (chunk) => (data += chunk))\n readable.on('end', () => resolve(data))\n readable.on('error', (err) => reject(err))\n })\n\nexport default (render: AppCallback, options?: Port | ServerOptions): void => {\n const _port = typeof options === 'number' ? options : (options?.port ?? 13714)\n const _useCluster = typeof options === 'object' && options?.cluster !== undefined ? options.cluster : false\n\n const log = (message: string) => {\n console.log(\n _useCluster && !cluster.isPrimary\n ? `[${cluster.worker?.id ?? 'N/A'} / ${cluster.worker?.process?.pid ?? 'N/A'}] ${message}`\n : message,\n )\n }\n\n if (_useCluster && cluster.isPrimary) {\n log('Primary Inertia SSR server process started...')\n\n for (let i = 0; i < availableParallelism(); i++) {\n cluster.fork()\n }\n\n cluster.on('message', (_worker, message) => {\n if (message === 'shutdown') {\n for (const id in cluster.workers) {\n cluster.workers[id]?.kill()\n }\n\n process.exit()\n }\n })\n\n return\n }\n\n const routes: Record<string, RouteHandler> = {\n '/health': async () => ({ status: 'OK', timestamp: Date.now() }),\n '/shutdown': async () => {\n if (cluster.isWorker) {\n process.send?.('shutdown')\n }\n\n process.exit()\n },\n '/render': async (request) => render(JSON.parse(await readableToString(request))),\n '/404': async () => ({ status: 'NOT_FOUND', timestamp: Date.now() }),\n }\n\n createServer(async (request, response) => {\n const dispatchRoute = routes[<string>request.url] || routes['/404']\n\n try {\n response.writeHead(200, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(await dispatchRoute(request)))\n } catch (e) {\n console.error(e)\n }\n\n response.end()\n }).listen(_port, () => log('Inertia SSR server started.'))\n\n log(`Starting SSR server on port ${_port}...`)\n}\n"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["
|
|
3
|
+
"sources": ["../src/server.ts", "../src/ssrErrors.ts"],
|
|
4
|
+
"sourcesContent": ["import { originalPositionFor, TraceMap } from '@jridgewell/trace-mapping'\nimport { createServer, IncomingMessage, ServerResponse } from 'http'\nimport cluster from 'node:cluster'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { availableParallelism } from 'node:os'\nimport path from 'node:path'\nimport * as process from 'process'\nimport { classifySSRError, formatConsoleError, setSourceMapResolver } from './ssrErrors'\nimport { InertiaAppResponse, Page } from './types'\n\n// Cache parsed sourcemaps for performance\nconst sourceMaps = new Map<string, TraceMap>()\n\nsetSourceMapResolver((file: string, line: number, column: number) => {\n // Only resolve for bundled SSR files\n if (!file.includes('/ssr/') || !file.endsWith('.js')) {\n return null\n }\n\n const mapFile = file + '.map'\n\n if (!existsSync(mapFile)) {\n return null\n }\n\n let traceMap = sourceMaps.get(mapFile)\n\n if (!traceMap) {\n try {\n const mapContent = readFileSync(mapFile, 'utf-8')\n traceMap = new TraceMap(mapContent)\n sourceMaps.set(mapFile, traceMap)\n } catch {\n return null\n }\n }\n\n const original = originalPositionFor(traceMap, { line, column })\n\n if (original.source) {\n // Resolve the source path relative to the sourcemap location\n const mapDir = path.dirname(mapFile)\n const resolvedPath = path.resolve(mapDir, original.source)\n\n return {\n file: resolvedPath,\n line: original.line ?? line,\n column: original.column ?? column,\n }\n }\n\n return null\n})\n\ntype AppCallback = (page: Page) => InertiaAppResponse\ntype RouteHandler = (request: IncomingMessage, response: ServerResponse) => Promise<unknown>\ntype ServerOptions = {\n port?: number\n cluster?: boolean\n handleErrors?: boolean\n}\ntype Port = number\n\nconst readableToString: (readable: IncomingMessage) => Promise<string> = (readable) =>\n new Promise((resolve, reject) => {\n let data = ''\n readable.on('data', (chunk) => (data += chunk))\n readable.on('end', () => resolve(data))\n readable.on('error', (err) => reject(err))\n })\n\nexport default (render: AppCallback, options?: Port | ServerOptions): AppCallback => {\n const opts = typeof options === 'number' ? { port: options } : options\n const { port = 13714, cluster: useCluster = false, handleErrors = true } = opts ?? {}\n\n const log = (message: string) => {\n console.log(\n useCluster && !cluster.isPrimary\n ? `[${cluster.worker?.id ?? 'N/A'} / ${cluster.worker?.process?.pid ?? 'N/A'}] ${message}`\n : message,\n )\n }\n\n if (useCluster && cluster.isPrimary) {\n log('Primary Inertia SSR server process started...')\n\n for (let i = 0; i < availableParallelism(); i++) {\n cluster.fork()\n }\n\n cluster.on('message', (_worker, message) => {\n if (message === 'shutdown') {\n for (const id in cluster.workers) {\n cluster.workers[id]?.kill()\n }\n\n process.exit()\n }\n })\n\n return render\n }\n\n const handleRender = async (request: IncomingMessage, response: ServerResponse) => {\n const page: Page = JSON.parse(await readableToString(request))\n\n // Suppress framework warnings during render (they clutter the output)\n const originalWarn = console.warn\n\n if (handleErrors) {\n console.warn = () => {}\n }\n\n try {\n const result = await render(page)\n\n response.writeHead(200, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(result))\n } catch (e) {\n const error = e as Error\n\n if (!handleErrors) {\n throw error\n }\n\n const classified = classifySSRError(error, page.component, page.url)\n console.error(formatConsoleError(classified))\n\n response.writeHead(500, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(classified))\n } finally {\n console.warn = originalWarn\n }\n }\n\n const routes: Record<string, RouteHandler> = {\n '/health': async () => ({ status: 'OK', timestamp: Date.now() }),\n '/shutdown': async () => {\n if (cluster.isWorker) {\n process.send?.('shutdown')\n }\n\n process.exit()\n },\n '/render': handleRender,\n '/404': async () => ({ status: 'NOT_FOUND', timestamp: Date.now() }),\n }\n\n createServer(async (request, response) => {\n const dispatchRoute = routes[request.url as string] ?? routes['/404']\n const result = await dispatchRoute(request, response)\n\n if (!response.headersSent) {\n response.writeHead(200, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(result))\n }\n\n response.end()\n }).listen(port, () => log('Inertia SSR server started.'))\n\n log(`Starting SSR server on port ${port}...`)\n\n // Return the render callback so it can be exported for Vite SSR dev mode\n return render\n}\n", "/**\n * SSR Error Classification\n *\n * This module detects common SSR errors and provides helpful hints\n * to developers on how to fix them. The most common issue is using\n * browser-specific APIs (like window, document) that don't exist\n * in the Node.js server environment.\n */\n\nexport type SSRErrorType = 'browser-api' | 'component-resolution' | 'render' | 'unknown'\n\ntype SourceMapResolver = (\n file: string,\n line: number,\n column: number,\n) => { file: string; line: number; column: number } | null\n\nlet sourceMapResolver: SourceMapResolver | null = null\n\nexport function setSourceMapResolver(resolver: SourceMapResolver | null): void {\n sourceMapResolver = resolver\n}\n\nexport interface ClassifiedSSRError {\n error: string\n type: SSRErrorType\n component?: string\n url?: string\n browserApi?: string\n hint: string\n stack?: string\n sourceLocation?: string\n timestamp: string\n}\n\nconst BROWSER_APIS: Record<string, string> = {\n // Global objects\n window: 'The global window object',\n document: 'The DOM document object',\n navigator: 'The navigator object',\n location: 'The location object',\n history: 'The browser history API',\n screen: 'The screen object',\n localStorage: 'Browser local storage',\n sessionStorage: 'Browser session storage',\n\n // Viewport properties (accessed via window.X)\n innerWidth: 'Browser viewport width',\n innerHeight: 'Browser viewport height',\n outerWidth: 'Browser window width',\n outerHeight: 'Browser window height',\n scrollX: 'Horizontal scroll position',\n scrollY: 'Vertical scroll position',\n devicePixelRatio: 'The device pixel ratio',\n matchMedia: 'The matchMedia function',\n\n // Observers (commonly instantiated at module level)\n IntersectionObserver: 'The IntersectionObserver API',\n ResizeObserver: 'The ResizeObserver API',\n MutationObserver: 'The MutationObserver API',\n\n // Timing functions (commonly called at module level)\n requestAnimationFrame: 'The requestAnimationFrame function',\n requestIdleCallback: 'The requestIdleCallback function',\n\n // Constructors that might be used at module level\n Image: 'The Image constructor',\n Audio: 'The Audio constructor',\n Worker: 'The Worker constructor',\n BroadcastChannel: 'The BroadcastChannel constructor',\n\n // Network (older Node.js versions)\n fetch: 'The fetch API',\n XMLHttpRequest: 'The XMLHttpRequest API',\n}\n\nfunction detectBrowserApi(error: Error): string | null {\n const message = error.message.toLowerCase()\n\n for (const api of Object.keys(BROWSER_APIS)) {\n const patterns = [\n `${api.toLowerCase()} is not defined`,\n `'${api.toLowerCase()}' is not defined`,\n `\"${api.toLowerCase()}\" is not defined`,\n `cannot read properties of undefined (reading '${api.toLowerCase()}')`,\n `cannot read property '${api.toLowerCase()}'`,\n ]\n\n if (patterns.some((pattern) => message.includes(pattern))) {\n return api\n }\n }\n\n return null\n}\n\nfunction isComponentResolutionError(error: Error): boolean {\n const message = error.message.toLowerCase()\n\n return (\n message.includes('cannot find module') ||\n message.includes('failed to resolve') ||\n message.includes('module not found') ||\n message.includes('could not resolve')\n )\n}\n\nconst LIFECYCLE_HOOKS = 'onMounted/useEffect/onMount'\n\nfunction getBrowserApiHint(api: string): string {\n const apiDescription = BROWSER_APIS[api] || `The \"${api}\" object`\n\n if (['localStorage', 'sessionStorage'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Check \"typeof ${api} !== 'undefined'\" before using it, ` +\n `or move the code to a ${LIFECYCLE_HOOKS} lifecycle hook.`\n )\n }\n\n if (['window', 'document'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Wrap browser-specific code in a ${LIFECYCLE_HOOKS} lifecycle hook, ` +\n `or check \"typeof ${api} !== 'undefined'\" before using it.`\n )\n }\n\n if (['IntersectionObserver', 'ResizeObserver', 'MutationObserver'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Create observers inside a ${LIFECYCLE_HOOKS} lifecycle hook, not at the module level.`\n )\n }\n\n if (['fetch', 'XMLHttpRequest'].includes(api)) {\n return (\n `${apiDescription} may not be available in all Node.js versions. ` +\n `For SSR, ensure data fetching happens on the server (in your controller) ` +\n `and is passed as props, or use a ${LIFECYCLE_HOOKS} hook for client-side fetching.`\n )\n }\n\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Move this code to a ${LIFECYCLE_HOOKS} lifecycle hook, or guard it with ` +\n `\"typeof ${api} !== 'undefined'\".`\n )\n}\n\nfunction getComponentResolutionHint(component?: string): string {\n const componentPart = component ? ` \"${component}\"` : ''\n\n return (\n `Could not resolve component${componentPart}. ` +\n `Check that the file exists and the path is correct. ` +\n `Ensure the component name matches the file name exactly (case-sensitive).`\n )\n}\n\nfunction getRenderErrorHint(): string {\n return (\n 'An error occurred while rendering the component. ' +\n 'Check the component for browser-specific code that runs during initialization. ' +\n 'Move any code that accesses browser APIs to a lifecycle hook.'\n )\n}\n\nfunction extractSourceLocation(stack?: string): string | undefined {\n if (!stack) {\n return undefined\n }\n\n for (const line of stack.split('\\n')) {\n if (!line.includes('at ')) {\n continue\n }\n\n if (line.includes('node_modules') || line.includes('node:')) {\n continue\n }\n\n let match = line.match(/\\(([^)]+):(\\d+):(\\d+)\\)/)\n\n if (!match) {\n match = line.match(/at\\s+(?:file:\\/\\/)?(.+):(\\d+):(\\d+)\\s*$/)\n }\n\n if (match) {\n const file = match[1].replace(/^file:\\/\\//, '')\n const lineNum = parseInt(match[2], 10)\n const colNum = parseInt(match[3], 10)\n\n if (sourceMapResolver) {\n const resolved = sourceMapResolver(file, lineNum, colNum)\n\n if (resolved) {\n return `${resolved.file}:${resolved.line}:${resolved.column}`\n }\n }\n\n return `${file}:${lineNum}:${colNum}`\n }\n }\n\n return undefined\n}\n\nexport function classifySSRError(error: Error, component?: string, url?: string): ClassifiedSSRError {\n const timestamp = new Date().toISOString()\n const base = {\n error: error.message,\n component,\n url,\n stack: error.stack,\n sourceLocation: extractSourceLocation(error.stack),\n timestamp,\n }\n\n const browserApi = detectBrowserApi(error)\n\n if (browserApi) {\n return {\n ...base,\n type: 'browser-api',\n browserApi,\n hint: getBrowserApiHint(browserApi),\n }\n }\n\n if (isComponentResolutionError(error)) {\n return {\n ...base,\n type: 'component-resolution',\n hint: getComponentResolutionHint(component),\n }\n }\n\n return {\n ...base,\n type: 'render',\n hint: getRenderErrorHint(),\n }\n}\n\nconst colors = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n bgRed: '\\x1b[41m',\n white: '\\x1b[37m',\n}\n\nfunction makeRelative(path: string, root?: string): string {\n const base = root ?? process.cwd()\n\n if (path.startsWith(base + '/')) {\n return path.slice(base.length + 1)\n }\n\n return path\n}\n\nexport function formatConsoleError(\n classified: ClassifiedSSRError,\n root?: string,\n handleErrors: boolean = true,\n suppressedWarnings: string[] = [],\n): string {\n if (!handleErrors) {\n const component = classified.component ? `[${classified.component}]` : ''\n return `SSR Error ${component}: ${classified.error}`\n }\n\n const componentPart = classified.component ? ` ${colors.cyan}${classified.component}${colors.reset}` : ''\n\n const lines = [\n '',\n ` ${colors.bgRed}${colors.white}${colors.bold} SSR ERROR ${colors.reset}${componentPart}`,\n '',\n ` ${classified.error}`,\n ]\n\n if (classified.sourceLocation) {\n const relativePath = makeRelative(classified.sourceLocation, root)\n lines.push(` ${colors.dim}Source: ${relativePath}${colors.reset}`)\n }\n\n if (classified.url) {\n lines.push(` ${colors.dim}URL: ${classified.url}${colors.reset}`)\n }\n\n lines.push('', ` ${colors.yellow}Hint${colors.reset} ${classified.hint}`, '')\n\n if (classified.stack) {\n lines.push(` ${colors.dim}${classified.stack.split('\\n').join('\\n ')}${colors.reset}`, '')\n }\n\n if (suppressedWarnings.length > 0) {\n lines.push(` ${colors.dim}Suppressed ${suppressedWarnings.length} framework warning(s).${colors.reset}`, '')\n }\n\n return lines.join('\\n')\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["process", "path"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// src/ssrErrors.ts
|
|
2
|
+
var sourceMapResolver = null;
|
|
3
|
+
function setSourceMapResolver(resolver) {
|
|
4
|
+
sourceMapResolver = resolver;
|
|
5
|
+
}
|
|
6
|
+
var BROWSER_APIS = {
|
|
7
|
+
// Global objects
|
|
8
|
+
window: "The global window object",
|
|
9
|
+
document: "The DOM document object",
|
|
10
|
+
navigator: "The navigator object",
|
|
11
|
+
location: "The location object",
|
|
12
|
+
history: "The browser history API",
|
|
13
|
+
screen: "The screen object",
|
|
14
|
+
localStorage: "Browser local storage",
|
|
15
|
+
sessionStorage: "Browser session storage",
|
|
16
|
+
// Viewport properties (accessed via window.X)
|
|
17
|
+
innerWidth: "Browser viewport width",
|
|
18
|
+
innerHeight: "Browser viewport height",
|
|
19
|
+
outerWidth: "Browser window width",
|
|
20
|
+
outerHeight: "Browser window height",
|
|
21
|
+
scrollX: "Horizontal scroll position",
|
|
22
|
+
scrollY: "Vertical scroll position",
|
|
23
|
+
devicePixelRatio: "The device pixel ratio",
|
|
24
|
+
matchMedia: "The matchMedia function",
|
|
25
|
+
// Observers (commonly instantiated at module level)
|
|
26
|
+
IntersectionObserver: "The IntersectionObserver API",
|
|
27
|
+
ResizeObserver: "The ResizeObserver API",
|
|
28
|
+
MutationObserver: "The MutationObserver API",
|
|
29
|
+
// Timing functions (commonly called at module level)
|
|
30
|
+
requestAnimationFrame: "The requestAnimationFrame function",
|
|
31
|
+
requestIdleCallback: "The requestIdleCallback function",
|
|
32
|
+
// Constructors that might be used at module level
|
|
33
|
+
Image: "The Image constructor",
|
|
34
|
+
Audio: "The Audio constructor",
|
|
35
|
+
Worker: "The Worker constructor",
|
|
36
|
+
BroadcastChannel: "The BroadcastChannel constructor",
|
|
37
|
+
// Network (older Node.js versions)
|
|
38
|
+
fetch: "The fetch API",
|
|
39
|
+
XMLHttpRequest: "The XMLHttpRequest API"
|
|
40
|
+
};
|
|
41
|
+
function detectBrowserApi(error) {
|
|
42
|
+
const message = error.message.toLowerCase();
|
|
43
|
+
for (const api of Object.keys(BROWSER_APIS)) {
|
|
44
|
+
const patterns = [
|
|
45
|
+
`${api.toLowerCase()} is not defined`,
|
|
46
|
+
`'${api.toLowerCase()}' is not defined`,
|
|
47
|
+
`"${api.toLowerCase()}" is not defined`,
|
|
48
|
+
`cannot read properties of undefined (reading '${api.toLowerCase()}')`,
|
|
49
|
+
`cannot read property '${api.toLowerCase()}'`
|
|
50
|
+
];
|
|
51
|
+
if (patterns.some((pattern) => message.includes(pattern))) {
|
|
52
|
+
return api;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function isComponentResolutionError(error) {
|
|
58
|
+
const message = error.message.toLowerCase();
|
|
59
|
+
return message.includes("cannot find module") || message.includes("failed to resolve") || message.includes("module not found") || message.includes("could not resolve");
|
|
60
|
+
}
|
|
61
|
+
var LIFECYCLE_HOOKS = "onMounted/useEffect/onMount";
|
|
62
|
+
function getBrowserApiHint(api) {
|
|
63
|
+
const apiDescription = BROWSER_APIS[api] || `The "${api}" object`;
|
|
64
|
+
if (["localStorage", "sessionStorage"].includes(api)) {
|
|
65
|
+
return `${apiDescription} doesn't exist in Node.js. Check "typeof ${api} !== 'undefined'" before using it, or move the code to a ${LIFECYCLE_HOOKS} lifecycle hook.`;
|
|
66
|
+
}
|
|
67
|
+
if (["window", "document"].includes(api)) {
|
|
68
|
+
return `${apiDescription} doesn't exist in Node.js. Wrap browser-specific code in a ${LIFECYCLE_HOOKS} lifecycle hook, or check "typeof ${api} !== 'undefined'" before using it.`;
|
|
69
|
+
}
|
|
70
|
+
if (["IntersectionObserver", "ResizeObserver", "MutationObserver"].includes(api)) {
|
|
71
|
+
return `${apiDescription} doesn't exist in Node.js. Create observers inside a ${LIFECYCLE_HOOKS} lifecycle hook, not at the module level.`;
|
|
72
|
+
}
|
|
73
|
+
if (["fetch", "XMLHttpRequest"].includes(api)) {
|
|
74
|
+
return `${apiDescription} may not be available in all Node.js versions. For SSR, ensure data fetching happens on the server (in your controller) and is passed as props, or use a ${LIFECYCLE_HOOKS} hook for client-side fetching.`;
|
|
75
|
+
}
|
|
76
|
+
return `${apiDescription} doesn't exist in Node.js. Move this code to a ${LIFECYCLE_HOOKS} lifecycle hook, or guard it with "typeof ${api} !== 'undefined'".`;
|
|
77
|
+
}
|
|
78
|
+
function getComponentResolutionHint(component) {
|
|
79
|
+
const componentPart = component ? ` "${component}"` : "";
|
|
80
|
+
return `Could not resolve component${componentPart}. Check that the file exists and the path is correct. Ensure the component name matches the file name exactly (case-sensitive).`;
|
|
81
|
+
}
|
|
82
|
+
function getRenderErrorHint() {
|
|
83
|
+
return "An error occurred while rendering the component. Check the component for browser-specific code that runs during initialization. Move any code that accesses browser APIs to a lifecycle hook.";
|
|
84
|
+
}
|
|
85
|
+
function extractSourceLocation(stack) {
|
|
86
|
+
if (!stack) {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
for (const line of stack.split("\n")) {
|
|
90
|
+
if (!line.includes("at ")) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (line.includes("node_modules") || line.includes("node:")) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
let match = line.match(/\(([^)]+):(\d+):(\d+)\)/);
|
|
97
|
+
if (!match) {
|
|
98
|
+
match = line.match(/at\s+(?:file:\/\/)?(.+):(\d+):(\d+)\s*$/);
|
|
99
|
+
}
|
|
100
|
+
if (match) {
|
|
101
|
+
const file = match[1].replace(/^file:\/\//, "");
|
|
102
|
+
const lineNum = parseInt(match[2], 10);
|
|
103
|
+
const colNum = parseInt(match[3], 10);
|
|
104
|
+
if (sourceMapResolver) {
|
|
105
|
+
const resolved = sourceMapResolver(file, lineNum, colNum);
|
|
106
|
+
if (resolved) {
|
|
107
|
+
return `${resolved.file}:${resolved.line}:${resolved.column}`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return `${file}:${lineNum}:${colNum}`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return void 0;
|
|
114
|
+
}
|
|
115
|
+
function classifySSRError(error, component, url) {
|
|
116
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
117
|
+
const base = {
|
|
118
|
+
error: error.message,
|
|
119
|
+
component,
|
|
120
|
+
url,
|
|
121
|
+
stack: error.stack,
|
|
122
|
+
sourceLocation: extractSourceLocation(error.stack),
|
|
123
|
+
timestamp
|
|
124
|
+
};
|
|
125
|
+
const browserApi = detectBrowserApi(error);
|
|
126
|
+
if (browserApi) {
|
|
127
|
+
return {
|
|
128
|
+
...base,
|
|
129
|
+
type: "browser-api",
|
|
130
|
+
browserApi,
|
|
131
|
+
hint: getBrowserApiHint(browserApi)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (isComponentResolutionError(error)) {
|
|
135
|
+
return {
|
|
136
|
+
...base,
|
|
137
|
+
type: "component-resolution",
|
|
138
|
+
hint: getComponentResolutionHint(component)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
...base,
|
|
143
|
+
type: "render",
|
|
144
|
+
hint: getRenderErrorHint()
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
var colors = {
|
|
148
|
+
reset: "\x1B[0m",
|
|
149
|
+
red: "\x1B[31m",
|
|
150
|
+
yellow: "\x1B[33m",
|
|
151
|
+
cyan: "\x1B[36m",
|
|
152
|
+
dim: "\x1B[2m",
|
|
153
|
+
bold: "\x1B[1m",
|
|
154
|
+
bgRed: "\x1B[41m",
|
|
155
|
+
white: "\x1B[37m"
|
|
156
|
+
};
|
|
157
|
+
function makeRelative(path, root) {
|
|
158
|
+
const base = root ?? process.cwd();
|
|
159
|
+
if (path.startsWith(base + "/")) {
|
|
160
|
+
return path.slice(base.length + 1);
|
|
161
|
+
}
|
|
162
|
+
return path;
|
|
163
|
+
}
|
|
164
|
+
function formatConsoleError(classified, root, handleErrors = true, suppressedWarnings = []) {
|
|
165
|
+
if (!handleErrors) {
|
|
166
|
+
const component = classified.component ? `[${classified.component}]` : "";
|
|
167
|
+
return `SSR Error ${component}: ${classified.error}`;
|
|
168
|
+
}
|
|
169
|
+
const componentPart = classified.component ? ` ${colors.cyan}${classified.component}${colors.reset}` : "";
|
|
170
|
+
const lines = [
|
|
171
|
+
"",
|
|
172
|
+
` ${colors.bgRed}${colors.white}${colors.bold} SSR ERROR ${colors.reset}${componentPart}`,
|
|
173
|
+
"",
|
|
174
|
+
` ${classified.error}`
|
|
175
|
+
];
|
|
176
|
+
if (classified.sourceLocation) {
|
|
177
|
+
const relativePath = makeRelative(classified.sourceLocation, root);
|
|
178
|
+
lines.push(` ${colors.dim}Source: ${relativePath}${colors.reset}`);
|
|
179
|
+
}
|
|
180
|
+
if (classified.url) {
|
|
181
|
+
lines.push(` ${colors.dim}URL: ${classified.url}${colors.reset}`);
|
|
182
|
+
}
|
|
183
|
+
lines.push("", ` ${colors.yellow}Hint${colors.reset} ${classified.hint}`, "");
|
|
184
|
+
if (classified.stack) {
|
|
185
|
+
lines.push(` ${colors.dim}${classified.stack.split("\n").join("\n ")}${colors.reset}`, "");
|
|
186
|
+
}
|
|
187
|
+
if (suppressedWarnings.length > 0) {
|
|
188
|
+
lines.push(` ${colors.dim}Suppressed ${suppressedWarnings.length} framework warning(s).${colors.reset}`, "");
|
|
189
|
+
}
|
|
190
|
+
return lines.join("\n");
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
classifySSRError,
|
|
194
|
+
formatConsoleError,
|
|
195
|
+
setSourceMapResolver
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=ssrErrors.js.map
|