@blinkk/root 1.0.0-beta.5 → 1.0.0-beta.50
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-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-MNTHVGVU.js +1276 -0
- package/dist/chunk-MNTHVGVU.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/cli.d.ts +14 -4
- package/dist/cli.js +11 -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 +29 -0
- package/dist/functions.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 +426 -226
- package/dist/render.js.map +1 -1
- package/dist/{types-47b9b530.d.ts → types-cfffcc1d.d.ts} +103 -33
- package/package.json +32 -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") {
|
|
@@ -483,6 +650,8 @@ var Renderer = class {
|
|
|
483
650
|
const handlerContext = {
|
|
484
651
|
route,
|
|
485
652
|
params: routeParams,
|
|
653
|
+
i18nFallbackLocales: fallbackLocales,
|
|
654
|
+
getPreferredLocale,
|
|
486
655
|
render,
|
|
487
656
|
render404
|
|
488
657
|
};
|
|
@@ -505,10 +674,14 @@ var Renderer = class {
|
|
|
505
674
|
await render(props);
|
|
506
675
|
}
|
|
507
676
|
async renderComponent(Component, props, options) {
|
|
508
|
-
const { route, routeParams } = options;
|
|
509
|
-
const locale =
|
|
510
|
-
const translations =
|
|
677
|
+
const { currentPath, route, routeParams } = options;
|
|
678
|
+
const locale = options.locale;
|
|
679
|
+
const translations = {
|
|
680
|
+
...getTranslations(locale),
|
|
681
|
+
...options.translations || {}
|
|
682
|
+
};
|
|
511
683
|
const ctx = {
|
|
684
|
+
currentPath,
|
|
512
685
|
route,
|
|
513
686
|
props,
|
|
514
687
|
routeParams,
|
|
@@ -592,6 +765,8 @@ var Renderer = class {
|
|
|
592
765
|
);
|
|
593
766
|
}
|
|
594
767
|
let props = {};
|
|
768
|
+
let locale = route.locale;
|
|
769
|
+
let translations = void 0;
|
|
595
770
|
if (route.module.getStaticProps) {
|
|
596
771
|
const propsData = await route.module.getStaticProps({
|
|
597
772
|
rootConfig: this.rootConfig,
|
|
@@ -603,8 +778,25 @@ var Renderer = class {
|
|
|
603
778
|
if (propsData.props) {
|
|
604
779
|
props = propsData.props;
|
|
605
780
|
}
|
|
781
|
+
if (propsData.locale) {
|
|
782
|
+
locale = propsData.locale;
|
|
783
|
+
}
|
|
784
|
+
if (propsData.translations) {
|
|
785
|
+
translations = propsData.translations;
|
|
786
|
+
}
|
|
606
787
|
}
|
|
607
|
-
|
|
788
|
+
const routePath = route.isDefaultLocale ? route.routePath : route.localeRoutePath;
|
|
789
|
+
const currentPath = replaceParams(routePath, {
|
|
790
|
+
...routeParams,
|
|
791
|
+
locale
|
|
792
|
+
});
|
|
793
|
+
return this.renderComponent(Component, props, {
|
|
794
|
+
currentPath,
|
|
795
|
+
route,
|
|
796
|
+
routeParams,
|
|
797
|
+
locale,
|
|
798
|
+
translations
|
|
799
|
+
});
|
|
608
800
|
}
|
|
609
801
|
async getSitemap() {
|
|
610
802
|
const sitemap = {};
|
|
@@ -645,17 +837,23 @@ var Renderer = class {
|
|
|
645
837
|
${renderToString(page)}
|
|
646
838
|
`;
|
|
647
839
|
}
|
|
648
|
-
async render404() {
|
|
840
|
+
async render404(options) {
|
|
841
|
+
const currentPath = (options == null ? void 0 : options.currentPath) || "/404";
|
|
649
842
|
const [route, routeParams] = this.routes.get("/404");
|
|
650
843
|
if (route && route.src === "routes/404.tsx" && route.module.default) {
|
|
651
844
|
const Component = route.module.default;
|
|
652
|
-
return this.renderComponent(
|
|
845
|
+
return this.renderComponent(
|
|
846
|
+
Component,
|
|
847
|
+
{},
|
|
848
|
+
{ currentPath, route, routeParams, locale: "en" }
|
|
849
|
+
);
|
|
653
850
|
}
|
|
654
851
|
const mainHtml = renderToString(
|
|
655
852
|
/* @__PURE__ */ jsx4(ErrorPage, {
|
|
656
853
|
code: 404,
|
|
657
854
|
title: "Not found",
|
|
658
|
-
message: "Double-check the URL entered and try again."
|
|
855
|
+
message: "Double-check the URL entered and try again.",
|
|
856
|
+
align: "center"
|
|
659
857
|
})
|
|
660
858
|
);
|
|
661
859
|
const html = await this.renderHtml(mainHtml, {
|
|
@@ -671,21 +869,23 @@ ${renderToString(page)}
|
|
|
671
869
|
});
|
|
672
870
|
return { html };
|
|
673
871
|
}
|
|
674
|
-
async renderError(err) {
|
|
872
|
+
async renderError(err, options) {
|
|
873
|
+
const currentPath = (options == null ? void 0 : options.currentPath) || "/500";
|
|
675
874
|
const [route, routeParams] = this.routes.get("/500");
|
|
676
875
|
if (route && route.src === "routes/500.tsx" && route.module.default) {
|
|
677
876
|
const Component = route.module.default;
|
|
678
877
|
return this.renderComponent(
|
|
679
878
|
Component,
|
|
680
879
|
{ error: err },
|
|
681
|
-
{ route, routeParams }
|
|
880
|
+
{ currentPath, route, routeParams, locale: "en" }
|
|
682
881
|
);
|
|
683
882
|
}
|
|
684
883
|
const mainHtml = renderToString(
|
|
685
884
|
/* @__PURE__ */ jsx4(ErrorPage, {
|
|
686
885
|
code: 500,
|
|
687
886
|
title: "Something went wrong",
|
|
688
|
-
message: "An unknown error occurred."
|
|
887
|
+
message: "An unknown error occurred.",
|
|
888
|
+
align: "center"
|
|
689
889
|
})
|
|
690
890
|
);
|
|
691
891
|
const html = await this.renderHtml(mainHtml, {
|