@blinkk/root 1.0.0-beta.6 → 1.0.0-beta.60
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/root.js +4 -0
- package/dist/chunk-AJF5RXXV.js +182 -0
- package/dist/chunk-AJF5RXXV.js.map +1 -0
- package/dist/{chunk-WVRC46JG.js → chunk-DXD5LKU3.js} +18 -18
- package/dist/chunk-DXD5LKU3.js.map +1 -0
- package/dist/{chunk-WTSNW7BB.js → chunk-HQXIHYDN.js} +1 -1
- package/dist/chunk-HQXIHYDN.js.map +1 -0
- package/dist/{chunk-DTEQ2AIW.js → chunk-QKBMWK5B.js} +1 -1
- package/dist/chunk-QKBMWK5B.js.map +1 -0
- package/dist/{chunk-LTSJAEBG.js → chunk-SDT4J6VY.js} +18 -6
- package/dist/chunk-SDT4J6VY.js.map +1 -0
- package/dist/chunk-XG7BJNHU.js +1289 -0
- package/dist/chunk-XG7BJNHU.js.map +1 -0
- package/dist/cli.d.ts +14 -4
- package/dist/cli.js +12 -1101
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +10 -7
- package/dist/core.js +14 -13
- package/dist/core.js.map +1 -1
- package/dist/functions.d.ts +14 -0
- package/dist/functions.js +30 -0
- package/dist/functions.js.map +1 -0
- package/dist/middleware.d.ts +34 -0
- package/dist/middleware.js +17 -0
- package/dist/middleware.js.map +1 -0
- package/dist/node.d.ts +1 -1
- package/dist/node.js +2 -2
- package/dist/render.d.ts +1 -1
- package/dist/render.js +427 -226
- package/dist/render.js.map +1 -1
- package/dist/{types-47b9b530.d.ts → types-b5510320.d.ts} +158 -34
- package/package.json +40 -4
- package/dist/chunk-DTEQ2AIW.js.map +0 -1
- package/dist/chunk-LTSJAEBG.js.map +0 -1
- package/dist/chunk-WTSNW7BB.js.map +0 -1
- package/dist/chunk-WVRC46JG.js.map +0 -1
package/dist/render.js
CHANGED
|
@@ -1,18 +1,341 @@
|
|
|
1
|
-
import {
|
|
2
|
-
htmlMinify,
|
|
3
|
-
htmlPretty,
|
|
4
|
-
parseTagNames
|
|
5
|
-
} from "./chunk-WVRC46JG.js";
|
|
6
1
|
import {
|
|
7
2
|
HTML_CONTEXT,
|
|
8
3
|
I18N_CONTEXT,
|
|
9
4
|
REQUEST_CONTEXT,
|
|
10
5
|
getTranslations
|
|
11
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HQXIHYDN.js";
|
|
7
|
+
import {
|
|
8
|
+
htmlMinify,
|
|
9
|
+
htmlPretty,
|
|
10
|
+
parseTagNames
|
|
11
|
+
} from "./chunk-DXD5LKU3.js";
|
|
12
12
|
|
|
13
13
|
// src/render/render.tsx
|
|
14
14
|
import renderToString from "preact-render-to-string";
|
|
15
15
|
|
|
16
|
+
// src/core/pages/ErrorPage.tsx
|
|
17
|
+
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
18
|
+
var STYLES = `
|
|
19
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
|
|
20
|
+
|
|
21
|
+
:root {
|
|
22
|
+
--font-family-text: "Inter", sans-serif;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
body {
|
|
26
|
+
font-family: var(--font-family-text);
|
|
27
|
+
background: #F5F5F5;
|
|
28
|
+
padding: 40px 16px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.root {
|
|
32
|
+
max-width: 1200px;
|
|
33
|
+
margin: 0 auto;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.root.align-center {
|
|
37
|
+
text-align: center;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
h1.title {
|
|
41
|
+
margin-top: 0;
|
|
42
|
+
margin-bottom: 24px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
p.message {
|
|
46
|
+
margin-top: 0;
|
|
47
|
+
margin-bottom: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.box {
|
|
51
|
+
font-size: 16px;
|
|
52
|
+
line-height: 1.5;
|
|
53
|
+
padding: 16px;
|
|
54
|
+
border-radius: 12px;
|
|
55
|
+
background: #ffffff;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pre.box {
|
|
59
|
+
white-space: pre-wrap;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@media (min-width: 500px) {
|
|
63
|
+
body {
|
|
64
|
+
padding: 40px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.box {
|
|
68
|
+
padding: 24px;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@media (min-width: 1024px) {
|
|
73
|
+
body {
|
|
74
|
+
padding: 100px;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
function ErrorPage(props) {
|
|
79
|
+
const { code, message } = props;
|
|
80
|
+
const title = props.title || code;
|
|
81
|
+
return /* @__PURE__ */ jsxs(Fragment, {
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ jsx("style", {
|
|
84
|
+
dangerouslySetInnerHTML: { __html: STYLES }
|
|
85
|
+
}),
|
|
86
|
+
/* @__PURE__ */ jsxs("div", {
|
|
87
|
+
className: `root align-${props.align || "left"}`,
|
|
88
|
+
children: [
|
|
89
|
+
/* @__PURE__ */ jsx("h1", {
|
|
90
|
+
className: "title",
|
|
91
|
+
children: title
|
|
92
|
+
}),
|
|
93
|
+
message && /* @__PURE__ */ jsx("p", {
|
|
94
|
+
className: "message",
|
|
95
|
+
children: message
|
|
96
|
+
}),
|
|
97
|
+
props.children
|
|
98
|
+
]
|
|
99
|
+
})
|
|
100
|
+
]
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/core/pages/DevErrorPage.tsx
|
|
105
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
106
|
+
function DevErrorPage(props) {
|
|
107
|
+
var _a;
|
|
108
|
+
const req = props.req;
|
|
109
|
+
const err = props.error;
|
|
110
|
+
const route = props.route;
|
|
111
|
+
const routeParams = props.routeParams;
|
|
112
|
+
let errMsg = String(err);
|
|
113
|
+
if (err && err.stack) {
|
|
114
|
+
errMsg = err.stack.replace(/\(.*node_modules/g, "(node_modules").replace(/at \/.*node_modules/g, "at node_modules");
|
|
115
|
+
if ((_a = req.rootConfig) == null ? void 0 : _a.rootDir) {
|
|
116
|
+
errMsg = errMsg.replaceAll(req.rootConfig.rootDir, "<root>");
|
|
117
|
+
}
|
|
118
|
+
if (process.env.HOME) {
|
|
119
|
+
errMsg = errMsg.replaceAll(process.env.HOME, "$HOME");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return /* @__PURE__ */ jsxs2(ErrorPage, {
|
|
123
|
+
code: 500,
|
|
124
|
+
title: "Something went wrong",
|
|
125
|
+
children: [
|
|
126
|
+
errMsg && /* @__PURE__ */ jsxs2(Fragment2, {
|
|
127
|
+
children: [
|
|
128
|
+
/* @__PURE__ */ jsx2("h2", {
|
|
129
|
+
children: "Error"
|
|
130
|
+
}),
|
|
131
|
+
/* @__PURE__ */ jsx2("pre", {
|
|
132
|
+
className: "box",
|
|
133
|
+
children: /* @__PURE__ */ jsx2("code", {
|
|
134
|
+
children: errMsg
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
]
|
|
138
|
+
}),
|
|
139
|
+
/* @__PURE__ */ jsx2("h2", {
|
|
140
|
+
children: "Debug Info"
|
|
141
|
+
}),
|
|
142
|
+
/* @__PURE__ */ jsx2("pre", {
|
|
143
|
+
className: "box",
|
|
144
|
+
children: /* @__PURE__ */ jsx2("code", {
|
|
145
|
+
children: `url: ${req.originalUrl}
|
|
146
|
+
route: ${(route == null ? void 0 : route.src) || "null"}
|
|
147
|
+
routeParams: ${routeParams && JSON.stringify(routeParams) || "null"}`
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
]
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/core/pages/DevNotFoundPage.tsx
|
|
155
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
156
|
+
function DevNotFoundPage(props) {
|
|
157
|
+
const req = props.req;
|
|
158
|
+
const routesListMap = {};
|
|
159
|
+
let srcMaxLength = 0;
|
|
160
|
+
Object.keys(props.sitemap).forEach((urlPath) => {
|
|
161
|
+
const route = props.sitemap[urlPath].route;
|
|
162
|
+
routesListMap[route.src] ??= [];
|
|
163
|
+
routesListMap[route.src].push({ route, urlPath });
|
|
164
|
+
if (route.src.length > srcMaxLength) {
|
|
165
|
+
srcMaxLength = route.src.length;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
const routeSrcs = Object.keys(routesListMap).sort(sortRouteFiles);
|
|
169
|
+
const lines = [];
|
|
170
|
+
routeSrcs.forEach((routeSrc) => {
|
|
171
|
+
const routeUrls = routesListMap[routeSrc].sort(sortRouteURLs);
|
|
172
|
+
routeUrls.forEach((routeUrl, i) => {
|
|
173
|
+
const urlPath = routeUrl.urlPath;
|
|
174
|
+
if (i === 0) {
|
|
175
|
+
lines.push(`${routeSrc.padEnd(srcMaxLength, " ")} => ${urlPath}`);
|
|
176
|
+
} else {
|
|
177
|
+
lines.push(`${"".padEnd(srcMaxLength, " ")} => ${urlPath}`);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
const routesListString = lines.join("\n");
|
|
182
|
+
return /* @__PURE__ */ jsxs3(ErrorPage, {
|
|
183
|
+
code: 404,
|
|
184
|
+
title: "Not found",
|
|
185
|
+
children: [
|
|
186
|
+
/* @__PURE__ */ jsx3("h2", {
|
|
187
|
+
children: "Routes"
|
|
188
|
+
}),
|
|
189
|
+
Object.keys(routesListMap).length > 0 ? /* @__PURE__ */ jsx3("pre", {
|
|
190
|
+
className: "box",
|
|
191
|
+
children: /* @__PURE__ */ jsx3("code", {
|
|
192
|
+
children: routesListString
|
|
193
|
+
})
|
|
194
|
+
}) : /* @__PURE__ */ jsxs3("div", {
|
|
195
|
+
className: "box",
|
|
196
|
+
children: [
|
|
197
|
+
"Add your first route at ",
|
|
198
|
+
/* @__PURE__ */ jsx3("code", {
|
|
199
|
+
children: "/routes/index.tsx"
|
|
200
|
+
})
|
|
201
|
+
]
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ jsx3("h2", {
|
|
204
|
+
children: "Debug Info"
|
|
205
|
+
}),
|
|
206
|
+
/* @__PURE__ */ jsx3("pre", {
|
|
207
|
+
className: "box",
|
|
208
|
+
children: /* @__PURE__ */ jsx3("code", {
|
|
209
|
+
children: `url: ${req.originalUrl}`
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
]
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function sortRouteFiles(a, b) {
|
|
216
|
+
if (a === "routes/index.tsx") {
|
|
217
|
+
return -1;
|
|
218
|
+
}
|
|
219
|
+
if (b === "routes/index.tsx") {
|
|
220
|
+
return 1;
|
|
221
|
+
}
|
|
222
|
+
return a.localeCompare(b);
|
|
223
|
+
}
|
|
224
|
+
function sortRouteURLs(a, b) {
|
|
225
|
+
if (a.route.isDefaultLocale && !b.route.isDefaultLocale) {
|
|
226
|
+
return -1;
|
|
227
|
+
}
|
|
228
|
+
if (!a.route.isDefaultLocale && b.route.isDefaultLocale) {
|
|
229
|
+
return 1;
|
|
230
|
+
}
|
|
231
|
+
return a.urlPath.localeCompare(b.urlPath);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/render/accept-language.ts
|
|
235
|
+
var ACCEPT_LANG_RE = /((([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\*)(;q=[0-1](\.[0-9]+)?)?)*/g;
|
|
236
|
+
function parseAcceptLanguage(value) {
|
|
237
|
+
const matches = String(value).match(ACCEPT_LANG_RE);
|
|
238
|
+
if (!matches) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
const results = [];
|
|
242
|
+
matches.forEach((m) => {
|
|
243
|
+
if (!m) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const parts = m.split(";");
|
|
247
|
+
const ietf = parts[0].split("-");
|
|
248
|
+
const hasScript = ietf.length === 3;
|
|
249
|
+
results.push({
|
|
250
|
+
code: ietf[0],
|
|
251
|
+
script: hasScript ? ietf[1] : void 0,
|
|
252
|
+
region: hasScript ? ietf[2] : ietf[1],
|
|
253
|
+
quality: parts[1] ? parseFloat(parts[1].split("=")[1]) : 1
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
results.sort((a, b) => b.quality - a.quality);
|
|
257
|
+
return results;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/render/i18n-fallbacks.ts
|
|
261
|
+
var UNKNOWN_COUNTRY = "zz";
|
|
262
|
+
function getFallbackLocales(req) {
|
|
263
|
+
var _a, _b;
|
|
264
|
+
const hl = getFirstQueryParam(req, "hl");
|
|
265
|
+
const countryCode = getCountry(req);
|
|
266
|
+
if (isWebCrawler(req)) {
|
|
267
|
+
const defaultLocale = ((_b = (_a = req.rootConfig) == null ? void 0 : _a.i18n) == null ? void 0 : _b.defaultLocale) || "en";
|
|
268
|
+
if (hl && hl !== defaultLocale) {
|
|
269
|
+
return [hl, defaultLocale];
|
|
270
|
+
}
|
|
271
|
+
return [defaultLocale];
|
|
272
|
+
}
|
|
273
|
+
const locales = /* @__PURE__ */ new Set();
|
|
274
|
+
if (hl) {
|
|
275
|
+
const langCode = hl;
|
|
276
|
+
locales.add(`${langCode}_${countryCode}`);
|
|
277
|
+
locales.add(`${langCode}_ALL`);
|
|
278
|
+
locales.add(langCode);
|
|
279
|
+
}
|
|
280
|
+
const langs = getFallbackLanguages(req);
|
|
281
|
+
langs.forEach((langCode) => {
|
|
282
|
+
locales.add(`${langCode}_${countryCode}`);
|
|
283
|
+
});
|
|
284
|
+
locales.add(`ALL_${countryCode}`);
|
|
285
|
+
langs.forEach((langCode) => {
|
|
286
|
+
locales.add(`${langCode}_ALL`);
|
|
287
|
+
locales.add(langCode);
|
|
288
|
+
});
|
|
289
|
+
return Array.from(locales);
|
|
290
|
+
}
|
|
291
|
+
function getCountry(req) {
|
|
292
|
+
const normalize = (countryCode) => String(countryCode).toLowerCase();
|
|
293
|
+
const gl = getFirstQueryParam(req, "gl");
|
|
294
|
+
if (gl) {
|
|
295
|
+
return normalize(gl);
|
|
296
|
+
}
|
|
297
|
+
const gaeCountry = req.get("x-country-code") || req.get("x-appengine-country");
|
|
298
|
+
if (gaeCountry) {
|
|
299
|
+
return normalize(gaeCountry);
|
|
300
|
+
}
|
|
301
|
+
return UNKNOWN_COUNTRY;
|
|
302
|
+
}
|
|
303
|
+
function getFallbackLanguages(req) {
|
|
304
|
+
const langs = /* @__PURE__ */ new Set();
|
|
305
|
+
const acceptLangHeader = req.get("accept-language") || "";
|
|
306
|
+
if (acceptLangHeader) {
|
|
307
|
+
parseAcceptLanguage(acceptLangHeader).forEach((lang) => {
|
|
308
|
+
if (lang.region) {
|
|
309
|
+
langs.add(`${lang.code}-${lang.region}`);
|
|
310
|
+
}
|
|
311
|
+
langs.add(lang.code);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
langs.add("en");
|
|
315
|
+
return Array.from(langs);
|
|
316
|
+
}
|
|
317
|
+
function getFirstQueryParam(req, key) {
|
|
318
|
+
const val = req.query[key];
|
|
319
|
+
if (val === null || val === void 0) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
if (Array.isArray(val)) {
|
|
323
|
+
if (val.length === 0) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
return String(val[0]);
|
|
327
|
+
}
|
|
328
|
+
return String(val);
|
|
329
|
+
}
|
|
330
|
+
function isWebCrawler(req) {
|
|
331
|
+
const userAgentHeader = req.get("User-Agent");
|
|
332
|
+
if (!userAgentHeader) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
const userAgent = userAgentHeader.toLowerCase();
|
|
336
|
+
return userAgent.includes("googlebot") || userAgent.includes("bingbot") || userAgent.includes("twitterbot");
|
|
337
|
+
}
|
|
338
|
+
|
|
16
339
|
// src/render/router.ts
|
|
17
340
|
import path from "node:path";
|
|
18
341
|
|
|
@@ -35,11 +358,14 @@ var RouteTrie = class {
|
|
|
35
358
|
}
|
|
36
359
|
let nextNode;
|
|
37
360
|
if (head.startsWith("[") && head.endsWith("]")) {
|
|
38
|
-
if (!this.
|
|
39
|
-
|
|
40
|
-
this.paramChild = new ParamChild(paramName);
|
|
361
|
+
if (!this.paramChildren) {
|
|
362
|
+
this.paramChildren = {};
|
|
41
363
|
}
|
|
42
|
-
|
|
364
|
+
const paramName = head.slice(1, -1);
|
|
365
|
+
if (!this.paramChildren[paramName]) {
|
|
366
|
+
this.paramChildren[paramName] = new ParamChild(paramName);
|
|
367
|
+
}
|
|
368
|
+
nextNode = this.paramChildren[paramName].trie;
|
|
43
369
|
} else {
|
|
44
370
|
nextNode = this.children[head];
|
|
45
371
|
if (!nextNode) {
|
|
@@ -64,11 +390,13 @@ var RouteTrie = class {
|
|
|
64
390
|
if (this.route) {
|
|
65
391
|
addPromise(cb("/", this.route));
|
|
66
392
|
}
|
|
67
|
-
if (this.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
393
|
+
if (this.paramChildren) {
|
|
394
|
+
Object.values(this.paramChildren).forEach((paramChild) => {
|
|
395
|
+
const param = `[${paramChild.name}]`;
|
|
396
|
+
paramChild.trie.walk((childPath, route) => {
|
|
397
|
+
const paramUrlPath = `/${param}${childPath}`;
|
|
398
|
+
addPromise(cb(paramUrlPath, route));
|
|
399
|
+
});
|
|
72
400
|
});
|
|
73
401
|
}
|
|
74
402
|
if (this.wildcardChild) {
|
|
@@ -86,7 +414,7 @@ var RouteTrie = class {
|
|
|
86
414
|
}
|
|
87
415
|
clear() {
|
|
88
416
|
this.children = {};
|
|
89
|
-
this.
|
|
417
|
+
this.paramChildren = void 0;
|
|
90
418
|
this.wildcardChild = void 0;
|
|
91
419
|
this.route = void 0;
|
|
92
420
|
}
|
|
@@ -103,11 +431,13 @@ var RouteTrie = class {
|
|
|
103
431
|
return route;
|
|
104
432
|
}
|
|
105
433
|
}
|
|
106
|
-
if (this.
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
434
|
+
if (this.paramChildren) {
|
|
435
|
+
for (const paramChild of Object.values(this.paramChildren)) {
|
|
436
|
+
const route = paramChild.trie.getRoute(tail, params);
|
|
437
|
+
if (route) {
|
|
438
|
+
params[paramChild.name] = head;
|
|
439
|
+
return route;
|
|
440
|
+
}
|
|
111
441
|
}
|
|
112
442
|
}
|
|
113
443
|
if (this.wildcardChild) {
|
|
@@ -170,8 +500,9 @@ function getRoutes(config) {
|
|
|
170
500
|
src,
|
|
171
501
|
module: routes[modulePath],
|
|
172
502
|
locale: defaultLocale,
|
|
173
|
-
|
|
174
|
-
|
|
503
|
+
isDefaultLocale: true,
|
|
504
|
+
routePath: normalizeUrlPath(routePath),
|
|
505
|
+
localeRoutePath: normalizeUrlPath(localeRoutePath)
|
|
175
506
|
});
|
|
176
507
|
locales.forEach((locale) => {
|
|
177
508
|
const localePath = localeRoutePath.replace("[locale]", locale);
|
|
@@ -180,6 +511,7 @@ function getRoutes(config) {
|
|
|
180
511
|
src,
|
|
181
512
|
module: routes[modulePath],
|
|
182
513
|
locale,
|
|
514
|
+
isDefaultLocale: false,
|
|
183
515
|
routePath,
|
|
184
516
|
localeRoutePath
|
|
185
517
|
});
|
|
@@ -206,7 +538,7 @@ async function getAllPathsForRoute(urlPathFormat, route) {
|
|
|
206
538
|
);
|
|
207
539
|
} else {
|
|
208
540
|
urlPaths.push({
|
|
209
|
-
urlPath:
|
|
541
|
+
urlPath: normalizeUrlPath(urlPath),
|
|
210
542
|
params: pathParams.params || {}
|
|
211
543
|
});
|
|
212
544
|
}
|
|
@@ -218,7 +550,7 @@ async function getAllPathsForRoute(urlPathFormat, route) {
|
|
|
218
550
|
`path contains placeholders: ${urlPathFormat}, did you forget to define getStaticPaths()? more info: https://rootjs.dev/guide/routes#getStaticPaths`
|
|
219
551
|
);
|
|
220
552
|
} else {
|
|
221
|
-
urlPaths.push({ urlPath: urlPathFormat, params: {} });
|
|
553
|
+
urlPaths.push({ urlPath: normalizeUrlPath(urlPathFormat), params: {} });
|
|
222
554
|
}
|
|
223
555
|
return urlPaths;
|
|
224
556
|
}
|
|
@@ -235,6 +567,12 @@ function replaceParams(urlPathFormat, params) {
|
|
|
235
567
|
);
|
|
236
568
|
return urlPath;
|
|
237
569
|
}
|
|
570
|
+
function normalizeUrlPath(urlPath) {
|
|
571
|
+
if (urlPath !== "/" && urlPath.endsWith("/")) {
|
|
572
|
+
urlPath = urlPath.replace(/\/*$/g, "");
|
|
573
|
+
}
|
|
574
|
+
return urlPath;
|
|
575
|
+
}
|
|
238
576
|
function pathContainsPlaceholders(urlPath) {
|
|
239
577
|
const segments = urlPath.split("/");
|
|
240
578
|
return segments.some((segment) => {
|
|
@@ -242,194 +580,6 @@ function pathContainsPlaceholders(urlPath) {
|
|
|
242
580
|
});
|
|
243
581
|
}
|
|
244
582
|
|
|
245
|
-
// src/core/pages/ErrorPage.tsx
|
|
246
|
-
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
247
|
-
var STYLES = `
|
|
248
|
-
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
|
|
249
|
-
|
|
250
|
-
:root {
|
|
251
|
-
--font-family-text: "Inter", sans-serif;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
body {
|
|
255
|
-
font-family: var(--font-family-text);
|
|
256
|
-
background: #F5F5F5;
|
|
257
|
-
padding: 40px 16px;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.root {
|
|
261
|
-
max-width: 1200px;
|
|
262
|
-
margin: 0 auto;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.root.align-center {
|
|
266
|
-
text-align: center;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
h1.title {
|
|
270
|
-
margin-top: 0;
|
|
271
|
-
margin-bottom: 24px;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
p.message {
|
|
275
|
-
margin-top: 0;
|
|
276
|
-
margin-bottom: 0;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
.box {
|
|
280
|
-
font-size: 16px;
|
|
281
|
-
line-height: 1.5;
|
|
282
|
-
padding: 16px;
|
|
283
|
-
border-radius: 12px;
|
|
284
|
-
background: #ffffff;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
pre.box {
|
|
288
|
-
white-space: pre-wrap;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
@media (min-width: 500px) {
|
|
292
|
-
body {
|
|
293
|
-
padding: 40px;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
.box {
|
|
297
|
-
padding: 24px;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
@media (min-width: 1024px) {
|
|
302
|
-
body {
|
|
303
|
-
padding: 100px;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
`;
|
|
307
|
-
function ErrorPage(props) {
|
|
308
|
-
const { code, message } = props;
|
|
309
|
-
const title = props.title || code;
|
|
310
|
-
return /* @__PURE__ */ jsxs(Fragment, {
|
|
311
|
-
children: [
|
|
312
|
-
/* @__PURE__ */ jsx("style", {
|
|
313
|
-
dangerouslySetInnerHTML: { __html: STYLES }
|
|
314
|
-
}),
|
|
315
|
-
/* @__PURE__ */ jsxs("div", {
|
|
316
|
-
className: `root align-${props.align || "left"}`,
|
|
317
|
-
children: [
|
|
318
|
-
/* @__PURE__ */ jsx("h1", {
|
|
319
|
-
className: "title",
|
|
320
|
-
children: title
|
|
321
|
-
}),
|
|
322
|
-
message && /* @__PURE__ */ jsx("p", {
|
|
323
|
-
className: "message",
|
|
324
|
-
children: message
|
|
325
|
-
}),
|
|
326
|
-
props.children
|
|
327
|
-
]
|
|
328
|
-
})
|
|
329
|
-
]
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// src/core/pages/DevNotFoundPage.tsx
|
|
334
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
335
|
-
function DevNotFoundPage(props) {
|
|
336
|
-
const req = props.req;
|
|
337
|
-
const routesList = [];
|
|
338
|
-
let urlMaxLength = 0;
|
|
339
|
-
Object.keys(props.sitemap).forEach((urlPath) => {
|
|
340
|
-
const route = props.sitemap[urlPath].route;
|
|
341
|
-
routesList.push(Object.assign({}, route, { urlPath }));
|
|
342
|
-
if (urlPath.length > urlMaxLength) {
|
|
343
|
-
urlMaxLength = urlPath.length;
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
const routesListString = routesList.map((route) => {
|
|
347
|
-
return `${route.urlPath.padEnd(urlMaxLength, " ")} => ${route.src}`;
|
|
348
|
-
}).join("\n");
|
|
349
|
-
return /* @__PURE__ */ jsxs2(ErrorPage, {
|
|
350
|
-
code: 404,
|
|
351
|
-
title: "Not found",
|
|
352
|
-
children: [
|
|
353
|
-
/* @__PURE__ */ jsx2("h2", {
|
|
354
|
-
children: "Routes"
|
|
355
|
-
}),
|
|
356
|
-
routesList.length > 0 ? /* @__PURE__ */ jsx2("pre", {
|
|
357
|
-
className: "box",
|
|
358
|
-
children: /* @__PURE__ */ jsx2("code", {
|
|
359
|
-
children: routesListString
|
|
360
|
-
})
|
|
361
|
-
}) : /* @__PURE__ */ jsxs2("div", {
|
|
362
|
-
className: "box",
|
|
363
|
-
children: [
|
|
364
|
-
"Add your first route at ",
|
|
365
|
-
/* @__PURE__ */ jsx2("code", {
|
|
366
|
-
children: "/routes/index.tsx"
|
|
367
|
-
})
|
|
368
|
-
]
|
|
369
|
-
}),
|
|
370
|
-
/* @__PURE__ */ jsx2("h2", {
|
|
371
|
-
children: "Debug Info"
|
|
372
|
-
}),
|
|
373
|
-
/* @__PURE__ */ jsx2("pre", {
|
|
374
|
-
className: "box",
|
|
375
|
-
children: /* @__PURE__ */ jsx2("code", {
|
|
376
|
-
children: `url: ${req.originalUrl}`
|
|
377
|
-
})
|
|
378
|
-
})
|
|
379
|
-
]
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// src/core/pages/DevErrorPage.tsx
|
|
384
|
-
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
385
|
-
function DevErrorPage(props) {
|
|
386
|
-
var _a;
|
|
387
|
-
const req = props.req;
|
|
388
|
-
const err = props.error;
|
|
389
|
-
const route = props.route;
|
|
390
|
-
const routeParams = props.routeParams;
|
|
391
|
-
let errMsg = String(err);
|
|
392
|
-
if (err && err.stack) {
|
|
393
|
-
errMsg = err.stack.replace(/\(.*node_modules/g, "(node_modules").replace(/at \/.*node_modules/g, "at node_modules");
|
|
394
|
-
if ((_a = req.rootConfig) == null ? void 0 : _a.rootDir) {
|
|
395
|
-
errMsg = errMsg.replaceAll(req.rootConfig.rootDir, "<root>");
|
|
396
|
-
}
|
|
397
|
-
if (process.env.HOME) {
|
|
398
|
-
errMsg = errMsg.replaceAll(process.env.HOME, "$HOME");
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return /* @__PURE__ */ jsxs3(ErrorPage, {
|
|
402
|
-
code: 500,
|
|
403
|
-
title: "Something went wrong",
|
|
404
|
-
children: [
|
|
405
|
-
errMsg && /* @__PURE__ */ jsxs3(Fragment2, {
|
|
406
|
-
children: [
|
|
407
|
-
/* @__PURE__ */ jsx3("h2", {
|
|
408
|
-
children: "Error"
|
|
409
|
-
}),
|
|
410
|
-
/* @__PURE__ */ jsx3("pre", {
|
|
411
|
-
className: "box",
|
|
412
|
-
children: /* @__PURE__ */ jsx3("code", {
|
|
413
|
-
children: errMsg
|
|
414
|
-
})
|
|
415
|
-
})
|
|
416
|
-
]
|
|
417
|
-
}),
|
|
418
|
-
/* @__PURE__ */ jsx3("h2", {
|
|
419
|
-
children: "Debug Info"
|
|
420
|
-
}),
|
|
421
|
-
/* @__PURE__ */ jsx3("pre", {
|
|
422
|
-
className: "box",
|
|
423
|
-
children: /* @__PURE__ */ jsx3("code", {
|
|
424
|
-
children: `url: ${req.originalUrl}
|
|
425
|
-
route: ${(route == null ? void 0 : route.src) || "null"}
|
|
426
|
-
routeParams: ${routeParams && JSON.stringify(routeParams) || "null"}`
|
|
427
|
-
})
|
|
428
|
-
})
|
|
429
|
-
]
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
|
|
433
583
|
// src/render/render.tsx
|
|
434
584
|
import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
435
585
|
var Renderer = class {
|
|
@@ -449,18 +599,35 @@ var Renderer = class {
|
|
|
449
599
|
if (route.locale) {
|
|
450
600
|
routeParams.$locale = route.locale;
|
|
451
601
|
}
|
|
602
|
+
const fallbackLocales = route.isDefaultLocale ? getFallbackLocales(req) : [route.locale];
|
|
603
|
+
const getPreferredLocale = (availableLocales) => {
|
|
604
|
+
var _a, _b;
|
|
605
|
+
const lowerLocales = availableLocales.map((l) => l.toLowerCase());
|
|
606
|
+
for (const fallbackLocale of fallbackLocales) {
|
|
607
|
+
if (lowerLocales.includes(fallbackLocale.toLowerCase())) {
|
|
608
|
+
return fallbackLocale;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return ((_b = (_a = req.rootConfig) == null ? void 0 : _a.i18n) == null ? void 0 : _b.defaultLocale) || "en";
|
|
612
|
+
};
|
|
452
613
|
const render404 = async () => {
|
|
453
614
|
next();
|
|
454
615
|
};
|
|
455
|
-
const render = async (props2) => {
|
|
616
|
+
const render = async (props2, options) => {
|
|
456
617
|
if (!route.module.default) {
|
|
457
618
|
console.error(`no default component exported in route: ${route.src}`);
|
|
458
619
|
render404();
|
|
459
620
|
return;
|
|
460
621
|
}
|
|
622
|
+
const currentPath = req.path;
|
|
623
|
+
const locale = (options == null ? void 0 : options.locale) || route.locale;
|
|
624
|
+
const translations = options == null ? void 0 : options.translations;
|
|
461
625
|
const output = await this.renderComponent(route.module.default, props2, {
|
|
626
|
+
currentPath,
|
|
462
627
|
route,
|
|
463
|
-
routeParams
|
|
628
|
+
routeParams,
|
|
629
|
+
locale,
|
|
630
|
+
translations
|
|
464
631
|
});
|
|
465
632
|
let html = output.html;
|
|
466
633
|
if (this.rootConfig.prettyHtml) {
|
|
@@ -469,7 +636,7 @@ var Renderer = class {
|
|
|
469
636
|
html = await htmlMinify(html, this.rootConfig.minifyHtmlOptions);
|
|
470
637
|
}
|
|
471
638
|
if (req.viteServer) {
|
|
472
|
-
html = await req.viteServer.transformIndexHtml(
|
|
639
|
+
html = await req.viteServer.transformIndexHtml(currentPath, html);
|
|
473
640
|
}
|
|
474
641
|
let statusCode = 200;
|
|
475
642
|
if (route.src === "routes/404.tsx") {
|
|
@@ -477,12 +644,15 @@ var Renderer = class {
|
|
|
477
644
|
} else if (route.src === "routes/500.tsx") {
|
|
478
645
|
statusCode = 500;
|
|
479
646
|
}
|
|
647
|
+
req.hooks.trigger("preRender");
|
|
480
648
|
res.status(statusCode).set({ "Content-Type": "text/html" }).end(html);
|
|
481
649
|
};
|
|
482
650
|
if (route.module.handle) {
|
|
483
651
|
const handlerContext = {
|
|
484
652
|
route,
|
|
485
653
|
params: routeParams,
|
|
654
|
+
i18nFallbackLocales: fallbackLocales,
|
|
655
|
+
getPreferredLocale,
|
|
486
656
|
render,
|
|
487
657
|
render404
|
|
488
658
|
};
|
|
@@ -505,10 +675,14 @@ var Renderer = class {
|
|
|
505
675
|
await render(props);
|
|
506
676
|
}
|
|
507
677
|
async renderComponent(Component, props, options) {
|
|
508
|
-
const { route, routeParams } = options;
|
|
509
|
-
const locale =
|
|
510
|
-
const translations =
|
|
678
|
+
const { currentPath, route, routeParams } = options;
|
|
679
|
+
const locale = options.locale;
|
|
680
|
+
const translations = {
|
|
681
|
+
...getTranslations(locale),
|
|
682
|
+
...options.translations || {}
|
|
683
|
+
};
|
|
511
684
|
const ctx = {
|
|
685
|
+
currentPath,
|
|
512
686
|
route,
|
|
513
687
|
props,
|
|
514
688
|
routeParams,
|
|
@@ -592,6 +766,8 @@ var Renderer = class {
|
|
|
592
766
|
);
|
|
593
767
|
}
|
|
594
768
|
let props = {};
|
|
769
|
+
let locale = route.locale;
|
|
770
|
+
let translations = void 0;
|
|
595
771
|
if (route.module.getStaticProps) {
|
|
596
772
|
const propsData = await route.module.getStaticProps({
|
|
597
773
|
rootConfig: this.rootConfig,
|
|
@@ -603,8 +779,25 @@ var Renderer = class {
|
|
|
603
779
|
if (propsData.props) {
|
|
604
780
|
props = propsData.props;
|
|
605
781
|
}
|
|
782
|
+
if (propsData.locale) {
|
|
783
|
+
locale = propsData.locale;
|
|
784
|
+
}
|
|
785
|
+
if (propsData.translations) {
|
|
786
|
+
translations = propsData.translations;
|
|
787
|
+
}
|
|
606
788
|
}
|
|
607
|
-
|
|
789
|
+
const routePath = route.isDefaultLocale ? route.routePath : route.localeRoutePath;
|
|
790
|
+
const currentPath = replaceParams(routePath, {
|
|
791
|
+
...routeParams,
|
|
792
|
+
locale
|
|
793
|
+
});
|
|
794
|
+
return this.renderComponent(Component, props, {
|
|
795
|
+
currentPath,
|
|
796
|
+
route,
|
|
797
|
+
routeParams,
|
|
798
|
+
locale,
|
|
799
|
+
translations
|
|
800
|
+
});
|
|
608
801
|
}
|
|
609
802
|
async getSitemap() {
|
|
610
803
|
const sitemap = {};
|
|
@@ -645,17 +838,23 @@ var Renderer = class {
|
|
|
645
838
|
${renderToString(page)}
|
|
646
839
|
`;
|
|
647
840
|
}
|
|
648
|
-
async render404() {
|
|
841
|
+
async render404(options) {
|
|
842
|
+
const currentPath = (options == null ? void 0 : options.currentPath) || "/404";
|
|
649
843
|
const [route, routeParams] = this.routes.get("/404");
|
|
650
844
|
if (route && route.src === "routes/404.tsx" && route.module.default) {
|
|
651
845
|
const Component = route.module.default;
|
|
652
|
-
return this.renderComponent(
|
|
846
|
+
return this.renderComponent(
|
|
847
|
+
Component,
|
|
848
|
+
{},
|
|
849
|
+
{ currentPath, route, routeParams, locale: "en" }
|
|
850
|
+
);
|
|
653
851
|
}
|
|
654
852
|
const mainHtml = renderToString(
|
|
655
853
|
/* @__PURE__ */ jsx4(ErrorPage, {
|
|
656
854
|
code: 404,
|
|
657
855
|
title: "Not found",
|
|
658
|
-
message: "Double-check the URL entered and try again."
|
|
856
|
+
message: "Double-check the URL entered and try again.",
|
|
857
|
+
align: "center"
|
|
659
858
|
})
|
|
660
859
|
);
|
|
661
860
|
const html = await this.renderHtml(mainHtml, {
|
|
@@ -671,21 +870,23 @@ ${renderToString(page)}
|
|
|
671
870
|
});
|
|
672
871
|
return { html };
|
|
673
872
|
}
|
|
674
|
-
async renderError(err) {
|
|
873
|
+
async renderError(err, options) {
|
|
874
|
+
const currentPath = (options == null ? void 0 : options.currentPath) || "/500";
|
|
675
875
|
const [route, routeParams] = this.routes.get("/500");
|
|
676
876
|
if (route && route.src === "routes/500.tsx" && route.module.default) {
|
|
677
877
|
const Component = route.module.default;
|
|
678
878
|
return this.renderComponent(
|
|
679
879
|
Component,
|
|
680
880
|
{ error: err },
|
|
681
|
-
{ route, routeParams }
|
|
881
|
+
{ currentPath, route, routeParams, locale: "en" }
|
|
682
882
|
);
|
|
683
883
|
}
|
|
684
884
|
const mainHtml = renderToString(
|
|
685
885
|
/* @__PURE__ */ jsx4(ErrorPage, {
|
|
686
886
|
code: 500,
|
|
687
887
|
title: "Something went wrong",
|
|
688
|
-
message: "An unknown error occurred."
|
|
888
|
+
message: "An unknown error occurred.",
|
|
889
|
+
align: "center"
|
|
689
890
|
})
|
|
690
891
|
);
|
|
691
892
|
const html = await this.renderHtml(mainHtml, {
|