@blinkk/root 1.0.0-alpha.2 → 1.0.0-alpha.20
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 +27 -16
- package/dist/{chunk-4SVK7R4Z.js → chunk-X6ZHRE6O.js} +52 -22
- package/dist/chunk-X6ZHRE6O.js.map +1 -0
- package/dist/chunk-ZB7R6ZOY.js +31 -0
- package/dist/chunk-ZB7R6ZOY.js.map +1 -0
- package/dist/cli.d.ts +10 -4
- package/dist/cli.js +789 -259
- package/dist/cli.js.map +1 -1
- package/dist/config-db7c34b8.d.ts +195 -0
- package/dist/core.d.ts +14 -6
- package/dist/core.js +13 -3
- package/dist/core.js.map +1 -1
- package/dist/render.d.ts +3 -68
- package/dist/render.js +273 -112
- package/dist/render.js.map +1 -1
- package/package.json +22 -22
- package/dist/chunk-4SVK7R4Z.js.map +0 -1
- package/dist/config-5b8f6033.d.ts +0 -15
package/dist/render.js
CHANGED
|
@@ -2,13 +2,12 @@ import {
|
|
|
2
2
|
ErrorPage,
|
|
3
3
|
HEAD_CONTEXT,
|
|
4
4
|
I18N_CONTEXT,
|
|
5
|
+
REQUEST_CONTEXT,
|
|
5
6
|
SCRIPT_CONTEXT,
|
|
6
7
|
getTranslations
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-X6ZHRE6O.js";
|
|
8
9
|
|
|
9
10
|
// src/render/render.tsx
|
|
10
|
-
import path2 from "node:path";
|
|
11
|
-
import { h } from "preact";
|
|
12
11
|
import renderToString from "preact-render-to-string";
|
|
13
12
|
|
|
14
13
|
// src/render/router.ts
|
|
@@ -19,13 +18,13 @@ var RouteTrie = class {
|
|
|
19
18
|
constructor() {
|
|
20
19
|
this.children = {};
|
|
21
20
|
}
|
|
22
|
-
add(
|
|
23
|
-
|
|
24
|
-
if (
|
|
21
|
+
add(path2, route) {
|
|
22
|
+
path2 = this.normalizePath(path2);
|
|
23
|
+
if (path2 === "") {
|
|
25
24
|
this.route = route;
|
|
26
25
|
return;
|
|
27
26
|
}
|
|
28
|
-
const [head, tail] = this.splitPath(
|
|
27
|
+
const [head, tail] = this.splitPath(path2);
|
|
29
28
|
if (head.startsWith("[...") && head.endsWith("]")) {
|
|
30
29
|
const paramName = head.slice(4, -1);
|
|
31
30
|
this.wildcardChild = new WildcardChild(paramName, route);
|
|
@@ -47,9 +46,9 @@ var RouteTrie = class {
|
|
|
47
46
|
}
|
|
48
47
|
nextNode.add(tail, route);
|
|
49
48
|
}
|
|
50
|
-
get(
|
|
49
|
+
get(path2) {
|
|
51
50
|
const params = {};
|
|
52
|
-
const route = this.getRoute(
|
|
51
|
+
const route = this.getRoute(path2, params);
|
|
53
52
|
return [route, params];
|
|
54
53
|
}
|
|
55
54
|
walk(cb) {
|
|
@@ -79,7 +78,8 @@ var RouteTrie = class {
|
|
|
79
78
|
addPromise(cb(`/${subpath}${childPath}`, childRoute));
|
|
80
79
|
});
|
|
81
80
|
}
|
|
82
|
-
return Promise.all(promises)
|
|
81
|
+
return Promise.all(promises).then(() => {
|
|
82
|
+
});
|
|
83
83
|
}
|
|
84
84
|
clear() {
|
|
85
85
|
this.children = {};
|
|
@@ -113,15 +113,15 @@ var RouteTrie = class {
|
|
|
113
113
|
}
|
|
114
114
|
return void 0;
|
|
115
115
|
}
|
|
116
|
-
normalizePath(
|
|
117
|
-
return
|
|
116
|
+
normalizePath(path2) {
|
|
117
|
+
return path2.replace(/^\/+/g, "");
|
|
118
118
|
}
|
|
119
|
-
splitPath(
|
|
120
|
-
const i =
|
|
119
|
+
splitPath(path2) {
|
|
120
|
+
const i = path2.indexOf("/");
|
|
121
121
|
if (i === -1) {
|
|
122
|
-
return [
|
|
122
|
+
return [path2, ""];
|
|
123
123
|
}
|
|
124
|
-
return [
|
|
124
|
+
return [path2.slice(0, i), path2.slice(i + 1)];
|
|
125
125
|
}
|
|
126
126
|
};
|
|
127
127
|
var ParamChild = class {
|
|
@@ -143,12 +143,16 @@ function getRoutes(config) {
|
|
|
143
143
|
const locales = ((_a = config.i18n) == null ? void 0 : _a.locales) || [];
|
|
144
144
|
const i18nUrlFormat = ((_b = config.i18n) == null ? void 0 : _b.urlFormat) || "/{locale}/{path}";
|
|
145
145
|
const defaultLocale = ((_c = config.i18n) == null ? void 0 : _c.defaultLocale) || "en";
|
|
146
|
-
const routes = import.meta.glob(
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
const routes = import.meta.glob(
|
|
147
|
+
["/routes/*.ts", "/routes/**/*.ts", "/routes/*.tsx", "/routes/**/*.tsx"],
|
|
148
|
+
{
|
|
149
|
+
eager: true
|
|
150
|
+
}
|
|
151
|
+
);
|
|
149
152
|
const trie = new RouteTrie();
|
|
150
|
-
Object.keys(routes).forEach((
|
|
151
|
-
|
|
153
|
+
Object.keys(routes).forEach((modulePath) => {
|
|
154
|
+
const src = modulePath.slice(1);
|
|
155
|
+
let routePath = modulePath.replace(/^\/routes/, "");
|
|
152
156
|
const parts = path.parse(routePath);
|
|
153
157
|
if (parts.name.startsWith("_")) {
|
|
154
158
|
return;
|
|
@@ -159,16 +163,16 @@ function getRoutes(config) {
|
|
|
159
163
|
routePath = path.join(parts.dir, parts.name);
|
|
160
164
|
}
|
|
161
165
|
trie.add(routePath, {
|
|
162
|
-
|
|
163
|
-
module: routes[
|
|
166
|
+
src,
|
|
167
|
+
module: routes[modulePath],
|
|
164
168
|
locale: defaultLocale
|
|
165
169
|
});
|
|
166
170
|
locales.forEach((locale) => {
|
|
167
171
|
const localeRoutePath = i18nUrlFormat.replace("{locale}", locale).replace("{path}", routePath.replace(/^\/*/, ""));
|
|
168
172
|
if (localeRoutePath !== routePath) {
|
|
169
173
|
trie.add(localeRoutePath, {
|
|
170
|
-
|
|
171
|
-
module: routes[
|
|
174
|
+
src,
|
|
175
|
+
module: routes[modulePath],
|
|
172
176
|
locale
|
|
173
177
|
});
|
|
174
178
|
}
|
|
@@ -181,54 +185,175 @@ async function getAllPathsForRoute(urlPathFormat, route) {
|
|
|
181
185
|
const routeModule = route.module;
|
|
182
186
|
if (routeModule.getStaticPaths) {
|
|
183
187
|
const staticPaths = await routeModule.getStaticPaths();
|
|
184
|
-
staticPaths.paths
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
if (staticPaths.paths) {
|
|
189
|
+
staticPaths.paths.forEach(
|
|
190
|
+
(pathParams) => {
|
|
191
|
+
const urlPath = replaceParams(urlPathFormat, pathParams.params || {});
|
|
192
|
+
if (pathContainsPlaceholders(urlPath)) {
|
|
193
|
+
console.warn(
|
|
194
|
+
`path contains placeholders: ${urlPathFormat}, double check getStaticPaths() and ensure all params are returned`
|
|
195
|
+
);
|
|
196
|
+
} else {
|
|
197
|
+
urlPaths.push({
|
|
198
|
+
urlPath: replaceParams(urlPathFormat, pathParams.params),
|
|
199
|
+
params: pathParams.params || {}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
} else if (pathContainsPlaceholders(urlPathFormat)) {
|
|
206
|
+
console.warn(
|
|
207
|
+
`path contains placeholders: ${urlPathFormat}, did you forget to define getStaticPaths()?`
|
|
208
|
+
);
|
|
190
209
|
} else {
|
|
191
210
|
urlPaths.push({ urlPath: urlPathFormat, params: {} });
|
|
192
211
|
}
|
|
193
212
|
return urlPaths;
|
|
194
213
|
}
|
|
195
214
|
function replaceParams(urlPathFormat, params) {
|
|
196
|
-
const urlPath = urlPathFormat.replaceAll(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
215
|
+
const urlPath = urlPathFormat.replaceAll(
|
|
216
|
+
/\[(\.\.\.)?([\w\-_]*)\]/g,
|
|
217
|
+
(match, _wildcard, key) => {
|
|
218
|
+
const val = params[key];
|
|
219
|
+
if (!val) {
|
|
220
|
+
throw new Error(`unreplaced param ${match} in url: ${urlPathFormat}`);
|
|
221
|
+
}
|
|
222
|
+
return val;
|
|
200
223
|
}
|
|
201
|
-
|
|
202
|
-
});
|
|
224
|
+
);
|
|
203
225
|
return urlPath;
|
|
204
226
|
}
|
|
227
|
+
function pathContainsPlaceholders(urlPath) {
|
|
228
|
+
const segments = urlPath.split("/");
|
|
229
|
+
return segments.some((segment) => {
|
|
230
|
+
return segment.startsWith("[") && segment.endsWith("]");
|
|
231
|
+
});
|
|
232
|
+
}
|
|
205
233
|
|
|
206
234
|
// src/render/render.tsx
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
235
|
+
import { elementsMap } from "virtual:root-elements";
|
|
236
|
+
|
|
237
|
+
// src/core/components/dev-not-found-page.tsx
|
|
238
|
+
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
239
|
+
var STYLES = `
|
|
240
|
+
body {
|
|
241
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
h1 {
|
|
245
|
+
margin-bottom: 40px;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.route-list {
|
|
249
|
+
display: grid;
|
|
250
|
+
grid-template-columns: auto 1fr;
|
|
251
|
+
align-items: center;
|
|
252
|
+
column-gap: 24px;
|
|
253
|
+
row-gap: 8px;
|
|
254
|
+
padding: 0 40px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.route-module {
|
|
258
|
+
text-align: right;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
hr {
|
|
262
|
+
margin: 60px 0;
|
|
263
|
+
}
|
|
264
|
+
`;
|
|
265
|
+
function DevNotFoundPage(props) {
|
|
266
|
+
const routesList = [];
|
|
267
|
+
Object.keys(props.sitemap).forEach((urlPath) => {
|
|
268
|
+
const route = props.sitemap[urlPath].route;
|
|
269
|
+
routesList.push(Object.assign({}, route, { urlPath }));
|
|
270
|
+
});
|
|
271
|
+
return /* @__PURE__ */ jsxs(Fragment, {
|
|
272
|
+
children: [
|
|
273
|
+
/* @__PURE__ */ jsx("style", {
|
|
274
|
+
dangerouslySetInnerHTML: { __html: STYLES }
|
|
275
|
+
}),
|
|
276
|
+
/* @__PURE__ */ jsxs("div", {
|
|
277
|
+
children: [
|
|
278
|
+
/* @__PURE__ */ jsxs("h1", {
|
|
279
|
+
children: [
|
|
280
|
+
/* @__PURE__ */ jsx("strong", {
|
|
281
|
+
children: "404:"
|
|
282
|
+
}),
|
|
283
|
+
" Not Found"
|
|
284
|
+
]
|
|
285
|
+
}),
|
|
286
|
+
routesList.length > 0 ? /* @__PURE__ */ jsxs(Fragment, {
|
|
287
|
+
children: [
|
|
288
|
+
/* @__PURE__ */ jsx("h2", {
|
|
289
|
+
children: "Routes found in the project:"
|
|
290
|
+
}),
|
|
291
|
+
/* @__PURE__ */ jsx("div", {
|
|
292
|
+
class: "route-list",
|
|
293
|
+
children: routesList.map((route) => /* @__PURE__ */ jsxs(Fragment, {
|
|
294
|
+
children: [
|
|
295
|
+
/* @__PURE__ */ jsx("div", {
|
|
296
|
+
class: "route-module",
|
|
297
|
+
children: route.modulePath
|
|
298
|
+
}),
|
|
299
|
+
/* @__PURE__ */ jsx("a", {
|
|
300
|
+
class: "route-url",
|
|
301
|
+
href: route.urlPath,
|
|
302
|
+
children: route.urlPath
|
|
303
|
+
})
|
|
304
|
+
]
|
|
305
|
+
}))
|
|
306
|
+
})
|
|
307
|
+
]
|
|
308
|
+
}) : /* @__PURE__ */ jsxs(Fragment, {
|
|
309
|
+
children: [
|
|
310
|
+
/* @__PURE__ */ jsx("h2", {
|
|
311
|
+
children: "No routes found in the project"
|
|
312
|
+
}),
|
|
313
|
+
/* @__PURE__ */ jsxs("p", {
|
|
314
|
+
children: [
|
|
315
|
+
"Add your first route at ",
|
|
316
|
+
/* @__PURE__ */ jsx("code", {
|
|
317
|
+
children: "/routes/index.tsx"
|
|
318
|
+
})
|
|
319
|
+
]
|
|
320
|
+
})
|
|
321
|
+
]
|
|
322
|
+
}),
|
|
323
|
+
/* @__PURE__ */ jsx("hr", {}),
|
|
324
|
+
/* @__PURE__ */ jsx("p", {
|
|
325
|
+
children: "Note: This page could use some love! If you're interested in helping to style this page, please reach out to the root.js developers!"
|
|
326
|
+
})
|
|
327
|
+
]
|
|
328
|
+
})
|
|
329
|
+
]
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/render/render.tsx
|
|
334
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
216
335
|
var Renderer = class {
|
|
217
|
-
constructor(
|
|
218
|
-
this.
|
|
336
|
+
constructor(rootConfig, options) {
|
|
337
|
+
this.rootConfig = rootConfig;
|
|
338
|
+
this.routes = getRoutes(this.rootConfig);
|
|
339
|
+
this.assetMap = options.assetMap;
|
|
219
340
|
}
|
|
220
|
-
async render(url
|
|
221
|
-
const assetMap = options.assetMap;
|
|
341
|
+
async render(url) {
|
|
222
342
|
const [route, routeParams] = this.routes.get(url);
|
|
223
343
|
if (route && route.module && route.module.default) {
|
|
224
|
-
return await this.renderRoute(route, { routeParams
|
|
344
|
+
return await this.renderRoute(route, { routeParams });
|
|
225
345
|
}
|
|
226
|
-
return
|
|
346
|
+
return { notFound: true };
|
|
227
347
|
}
|
|
228
348
|
async renderRoute(route, options) {
|
|
229
349
|
const routeParams = options.routeParams;
|
|
230
|
-
const assetMap =
|
|
350
|
+
const assetMap = this.assetMap;
|
|
231
351
|
const Component = route.module.default;
|
|
352
|
+
if (!Component) {
|
|
353
|
+
throw new Error(
|
|
354
|
+
"unable to render route. the route should have a default export that renders a jsx component."
|
|
355
|
+
);
|
|
356
|
+
}
|
|
232
357
|
let props = {};
|
|
233
358
|
if (route.module.getStaticProps) {
|
|
234
359
|
const propsData = await route.module.getStaticProps({
|
|
@@ -241,48 +366,57 @@ var Renderer = class {
|
|
|
241
366
|
props = propsData.props;
|
|
242
367
|
}
|
|
243
368
|
}
|
|
369
|
+
const ctx = { route };
|
|
244
370
|
const locale = route.locale;
|
|
245
371
|
const translations = getTranslations(locale);
|
|
246
372
|
const headComponents = [];
|
|
247
373
|
const userScripts = [];
|
|
248
|
-
const vdom = /* @__PURE__ */
|
|
249
|
-
value:
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
374
|
+
const vdom = /* @__PURE__ */ jsx2(REQUEST_CONTEXT.Provider, {
|
|
375
|
+
value: ctx,
|
|
376
|
+
children: /* @__PURE__ */ jsx2(I18N_CONTEXT.Provider, {
|
|
377
|
+
value: { locale, translations },
|
|
378
|
+
children: /* @__PURE__ */ jsx2(SCRIPT_CONTEXT.Provider, {
|
|
379
|
+
value: userScripts,
|
|
380
|
+
children: /* @__PURE__ */ jsx2(HEAD_CONTEXT.Provider, {
|
|
381
|
+
value: headComponents,
|
|
382
|
+
children: /* @__PURE__ */ jsx2(Component, {
|
|
383
|
+
...props
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
})
|
|
387
|
+
})
|
|
388
|
+
});
|
|
257
389
|
const mainHtml = renderToString(vdom, {}, { pretty: true });
|
|
258
|
-
const
|
|
259
|
-
const cssDeps =
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
href: cssUrl
|
|
265
|
-
}));
|
|
266
|
-
});
|
|
390
|
+
const jsDeps = /* @__PURE__ */ new Set();
|
|
391
|
+
const cssDeps = /* @__PURE__ */ new Set();
|
|
392
|
+
const pageAsset = await assetMap.get(route.src);
|
|
393
|
+
if (pageAsset) {
|
|
394
|
+
const pageCssDeps = await pageAsset.getCssDeps();
|
|
395
|
+
pageCssDeps.forEach((dep) => cssDeps.add(dep));
|
|
267
396
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
397
|
+
await this.collectElementDeps(mainHtml, jsDeps, cssDeps);
|
|
398
|
+
await Promise.all(
|
|
399
|
+
userScripts.map(async (scriptDep) => {
|
|
400
|
+
const scriptAsset = await assetMap.get(scriptDep.src.slice(1));
|
|
401
|
+
if (scriptAsset) {
|
|
402
|
+
jsDeps.add(scriptAsset.assetUrl);
|
|
403
|
+
const scriptJsDeps = await scriptAsset.getJsDeps();
|
|
404
|
+
scriptJsDeps.forEach((dep) => jsDeps.add(dep));
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
);
|
|
408
|
+
cssDeps.forEach((cssUrl) => {
|
|
409
|
+
headComponents.push(/* @__PURE__ */ jsx2("link", {
|
|
410
|
+
rel: "stylesheet",
|
|
411
|
+
href: cssUrl
|
|
273
412
|
}));
|
|
274
413
|
});
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (!scriptAsset && import.meta.env.PROD) {
|
|
278
|
-
console.log(`could not find precompiled asset: ${scriptDep.src}`);
|
|
279
|
-
}
|
|
280
|
-
const scriptUrl = scriptAsset ? scriptAsset.assetUrl : scriptDep.src;
|
|
281
|
-
headComponents.push(/* @__PURE__ */ h("script", {
|
|
414
|
+
jsDeps.forEach((jsUrls) => {
|
|
415
|
+
headComponents.push(/* @__PURE__ */ jsx2("script", {
|
|
282
416
|
type: "module",
|
|
283
|
-
src:
|
|
417
|
+
src: jsUrls
|
|
284
418
|
}));
|
|
285
|
-
})
|
|
419
|
+
});
|
|
286
420
|
const html = await this.renderHtml({ mainHtml, locale, headComponents });
|
|
287
421
|
return { html };
|
|
288
422
|
}
|
|
@@ -300,18 +434,28 @@ var Renderer = class {
|
|
|
300
434
|
return sitemap;
|
|
301
435
|
}
|
|
302
436
|
async renderHtml(options) {
|
|
303
|
-
const page = /* @__PURE__ */
|
|
304
|
-
lang: options.locale
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
437
|
+
const page = /* @__PURE__ */ jsxs2("html", {
|
|
438
|
+
lang: options.locale,
|
|
439
|
+
children: [
|
|
440
|
+
/* @__PURE__ */ jsxs2("head", {
|
|
441
|
+
children: [
|
|
442
|
+
/* @__PURE__ */ jsx2("meta", {
|
|
443
|
+
charSet: "utf-8"
|
|
444
|
+
}),
|
|
445
|
+
options.headComponents
|
|
446
|
+
]
|
|
447
|
+
}),
|
|
448
|
+
/* @__PURE__ */ jsx2("body", {
|
|
449
|
+
dangerouslySetInnerHTML: { __html: options.mainHtml }
|
|
450
|
+
})
|
|
451
|
+
]
|
|
452
|
+
});
|
|
313
453
|
const html = `<!doctype html>
|
|
314
|
-
${renderToString(
|
|
454
|
+
${renderToString(
|
|
455
|
+
page,
|
|
456
|
+
{},
|
|
457
|
+
{ pretty: true }
|
|
458
|
+
)}
|
|
315
459
|
`;
|
|
316
460
|
return html;
|
|
317
461
|
}
|
|
@@ -322,30 +466,47 @@ ${renderToString(page, {}, { pretty: true })}
|
|
|
322
466
|
};
|
|
323
467
|
}
|
|
324
468
|
async renderError(error) {
|
|
325
|
-
const mainHtml = renderToString(/* @__PURE__ */
|
|
469
|
+
const mainHtml = renderToString(/* @__PURE__ */ jsx2(ErrorPage, {
|
|
326
470
|
error
|
|
327
471
|
}));
|
|
328
472
|
const html = await this.renderHtml({ mainHtml, locale: "en" });
|
|
329
473
|
return { html };
|
|
330
474
|
}
|
|
331
|
-
async
|
|
332
|
-
const
|
|
333
|
-
const
|
|
475
|
+
async renderDevServer404() {
|
|
476
|
+
const sitemap = await this.getSitemap();
|
|
477
|
+
const mainHtml = renderToString(/* @__PURE__ */ jsx2(DevNotFoundPage, {
|
|
478
|
+
sitemap
|
|
479
|
+
}));
|
|
480
|
+
const html = await this.renderHtml({
|
|
481
|
+
mainHtml,
|
|
482
|
+
locale: "en",
|
|
483
|
+
headComponents: [/* @__PURE__ */ jsx2("title", {
|
|
484
|
+
children: "404: Not Found"
|
|
485
|
+
})]
|
|
486
|
+
});
|
|
487
|
+
return { html };
|
|
488
|
+
}
|
|
489
|
+
async collectElementDeps(html, jsDeps, cssDeps) {
|
|
490
|
+
const assetMap = this.assetMap;
|
|
334
491
|
const re = /<(\w[\w-]+\w)/g;
|
|
335
492
|
const matches = Array.from(html.matchAll(re));
|
|
336
|
-
await Promise.all(
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
493
|
+
await Promise.all(
|
|
494
|
+
matches.map(async (match) => {
|
|
495
|
+
const tagName = match[1];
|
|
496
|
+
if (tagName && tagName.includes("-") && tagName in elementsMap) {
|
|
497
|
+
const elementModule = elementsMap[tagName];
|
|
498
|
+
const asset = await assetMap.get(elementModule.src);
|
|
499
|
+
if (!asset) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const assetJsDeps = await asset.getJsDeps();
|
|
503
|
+
assetJsDeps.forEach((dep) => jsDeps.add(dep));
|
|
504
|
+
const assetCssDeps = await asset.getCssDeps();
|
|
505
|
+
assetCssDeps.forEach((dep) => cssDeps.add(dep));
|
|
343
506
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}));
|
|
348
|
-
return Array.from(deps);
|
|
507
|
+
})
|
|
508
|
+
);
|
|
509
|
+
return { jsDeps, cssDeps };
|
|
349
510
|
}
|
|
350
511
|
};
|
|
351
512
|
export {
|
package/dist/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/render/render.tsx","../src/render/router.ts","../src/render/route-trie.ts"],"sourcesContent":["import path from 'node:path';\nimport {h, ComponentChildren} from 'preact';\nimport renderToString from 'preact-render-to-string';\nimport {getRoutes, RouteModule, Route, getAllPathsForRoute} from './router';\nimport {HEAD_CONTEXT} from '../core/components/head';\nimport {ErrorPage} from '../core/components/error-page';\nimport {getTranslations, I18N_CONTEXT} from '../core/i18n';\nimport {ScriptProps, SCRIPT_CONTEXT} from '../core/components/script';\nimport {AssetMap} from './asset-map/asset-map';\nimport {RootConfig} from '../core/config';\nimport {RouteTrie} from './route-trie';\n\n// TODO(stevenle): this should be added via config.\nconst ELEMENTS_MAP: Record<string, string> = {};\nconst ELEMENTS_MODULES = import.meta.glob([\n '/elements/**/*.ts',\n '/elements/**/*.tsx',\n]) as Record<string, () => Promise<RouteModule>>;\nObject.keys(ELEMENTS_MODULES).forEach((elementPath) => {\n const parts = path.parse(elementPath);\n ELEMENTS_MAP[parts.name] = elementPath;\n});\n\ninterface RenderOptions {\n assetMap: AssetMap;\n}\n\ninterface RenderHtmlOptions {\n mainHtml: string;\n locale: string;\n headComponents?: ComponentChildren[];\n}\n\nexport class Renderer {\n private routes: RouteTrie<Route>;\n\n constructor(config: RootConfig) {\n this.routes = getRoutes(config);\n }\n\n async render(\n url: string,\n options: RenderOptions\n ): Promise<{html: string; notFound?: boolean}> {\n const assetMap = options.assetMap;\n const [route, routeParams] = this.routes.get(url);\n if (route && route.module && route.module.default) {\n return await this.renderRoute(route, {routeParams, assetMap});\n }\n return this.render404();\n }\n\n async renderRoute(\n route: Route,\n options: {routeParams: Record<string, string>; assetMap: AssetMap}\n ): Promise<{html: string; notFound?: boolean}> {\n const routeParams = options.routeParams;\n const assetMap = options.assetMap;\n const Component = route.module.default;\n let props = {};\n if (route.module.getStaticProps) {\n const propsData = await route.module.getStaticProps({\n params: routeParams,\n });\n if (propsData.notFound) {\n return this.render404();\n }\n if (propsData.props) {\n props = propsData.props;\n }\n }\n\n const locale = route.locale;\n const translations = getTranslations(locale);\n\n const headComponents: ComponentChildren[] = [];\n const userScripts: ScriptProps[] = [];\n const vdom = (\n <I18N_CONTEXT.Provider value={{locale, translations}}>\n <SCRIPT_CONTEXT.Provider value={userScripts}>\n <HEAD_CONTEXT.Provider value={headComponents}>\n <Component {...props} />\n </HEAD_CONTEXT.Provider>\n </SCRIPT_CONTEXT.Provider>\n </I18N_CONTEXT.Provider>\n );\n const mainHtml = renderToString(vdom, {}, {pretty: true});\n\n // Walk the page's dependency tree for CSS dependencies that are added via\n // `import 'foo.scss'` or `import 'foo.module.scss'`.\n const pageAsset = await assetMap.get(route.modulePath);\n const cssDeps = await pageAsset?.getCssDeps();\n if (cssDeps) {\n cssDeps.forEach((cssUrl) => {\n headComponents.push(<link rel=\"stylesheet\" href={cssUrl} />);\n });\n }\n\n // Parse the HTML for custom elements that are found within the project\n // and automatically inject the script deps for them.\n const scriptDeps = await this.getScriptDeps(mainHtml, {assetMap});\n scriptDeps.forEach((jsUrls) => {\n headComponents.push(<script type=\"module\" src={jsUrls} />);\n });\n\n // Add user defined scripts added via the `<Script>` component.\n await Promise.all(\n userScripts.map(async (scriptDep) => {\n const scriptAsset = await assetMap.get(scriptDep.src);\n if (!scriptAsset && import.meta.env.PROD) {\n console.log(`could not find precompiled asset: ${scriptDep.src}`);\n }\n const scriptUrl = scriptAsset ? scriptAsset.assetUrl : scriptDep.src;\n headComponents.push(<script type=\"module\" src={scriptUrl} />);\n })\n );\n\n const html = await this.renderHtml({mainHtml, locale, headComponents});\n return {html};\n }\n\n async getSitemap(): Promise<\n Record<string, {route: Route; params: Record<string, string>}>\n > {\n const sitemap: Record<\n string,\n {route: Route; params: Record<string, string>}\n > = {};\n await this.routes.walk(async (urlPath: string, route: Route) => {\n const routePaths = await getAllPathsForRoute(urlPath, route);\n routePaths.forEach((routePath) => {\n sitemap[routePath.urlPath] = {\n route,\n params: routePath.params,\n };\n });\n });\n return sitemap;\n }\n\n private async renderHtml(options: RenderHtmlOptions) {\n const page = (\n <html lang={options.locale}>\n <head>\n <meta charSet=\"utf-8\" />\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1.0\"\n />\n {options.headComponents}\n </head>\n <body dangerouslySetInnerHTML={{__html: options.mainHtml}} />\n </html>\n );\n const html = `<!doctype html>\\n${renderToString(\n page,\n {},\n {pretty: true}\n )}\\n`;\n return html;\n }\n\n async render404() {\n return {\n html: '<!doctype html><html><title>404 Not Found</title><h1>404 Not Found</h1>',\n notFound: true,\n };\n }\n\n async renderError(error: unknown) {\n const mainHtml = renderToString(<ErrorPage error={error} />);\n const html = await this.renderHtml({mainHtml, locale: 'en'});\n return {html};\n }\n\n private async getScriptDeps(\n html: string,\n options: {assetMap: AssetMap}\n ): Promise<string[]> {\n const assetMap = options.assetMap as AssetMap;\n const deps = new Set<string>();\n\n const re = /<(\\w[\\w-]+\\w)/g;\n const matches = Array.from(html.matchAll(re));\n await Promise.all(\n matches.map(async (match) => {\n const tagName = match[1];\n // Custom elements require a dash.\n if (tagName && tagName.includes('-') && tagName in ELEMENTS_MAP) {\n const modulePath = ELEMENTS_MAP[tagName];\n const asset = await assetMap.get(modulePath);\n if (!asset) {\n return;\n }\n const assetJsDeps = await asset.getJsDeps();\n assetJsDeps.forEach((dep) => deps.add(dep));\n }\n })\n );\n\n return Array.from(deps);\n }\n}\n","import path from 'node:path';\nimport {RootConfig} from '../core/config';\nimport {GetStaticPaths, GetStaticProps} from '../core/types';\nimport {RouteTrie} from './route-trie';\n\nexport interface RouteModule {\n default: any;\n getStaticPaths?: GetStaticPaths;\n getStaticProps?: GetStaticProps;\n}\n\nexport interface Route {\n modulePath: string;\n module: RouteModule;\n locale: string;\n}\n\nexport function getRoutes(config: RootConfig) {\n const locales = config.i18n?.locales || [];\n const i18nUrlFormat = config.i18n?.urlFormat || '/{locale}/{path}';\n const defaultLocale = config.i18n?.defaultLocale || 'en';\n\n const routes = import.meta.glob(\n ['/routes/*.ts', '/routes/**/*.ts', '/routes/*.tsx', '/routes/**/*.tsx'],\n {\n eager: true,\n }\n );\n const trie = new RouteTrie<Route>();\n Object.keys(routes).forEach((filePath) => {\n let routePath = filePath.replace(/^\\/routes/, '');\n const parts = path.parse(routePath);\n if (parts.name.startsWith('_')) {\n return;\n }\n if (parts.name === 'index') {\n routePath = parts.dir;\n } else {\n routePath = path.join(parts.dir, parts.name);\n }\n trie.add(routePath, {\n modulePath: filePath,\n module: routes[filePath] as RouteModule,\n locale: defaultLocale,\n });\n\n // At the moment, all routes are assumed to use the site-wide i18n config.\n // TODO(stevenle): provide routes with a way to override the default\n // i18n serving behavior.\n locales.forEach((locale) => {\n const localeRoutePath = i18nUrlFormat\n .replace('{locale}', locale)\n .replace('{path}', routePath.replace(/^\\/*/, ''));\n if (localeRoutePath !== routePath) {\n trie.add(localeRoutePath, {\n modulePath: filePath,\n module: routes[filePath] as RouteModule,\n locale: locale,\n });\n }\n });\n });\n return trie;\n}\n\nexport async function getAllPathsForRoute(\n urlPathFormat: string,\n route: Route\n): Promise<Array<{urlPath: string; params: Record<string, string>}>> {\n const urlPaths: Array<{urlPath: string; params: Record<string, string>}> = [];\n const routeModule = route.module;\n if (routeModule.getStaticPaths) {\n const staticPaths = await routeModule.getStaticPaths();\n staticPaths.paths.forEach(\n (pathParams: {params: Record<string, string>}) => {\n urlPaths.push({\n urlPath: replaceParams(urlPathFormat, pathParams.params),\n params: pathParams.params || {},\n });\n }\n );\n } else {\n urlPaths.push({urlPath: urlPathFormat, params: {}});\n }\n return urlPaths;\n}\n\nexport function replaceParams(\n urlPathFormat: string,\n params: Record<string, string>\n) {\n const urlPath = urlPathFormat.replaceAll(\n /\\[(\\.\\.\\.)?([\\w\\-_]*)\\]/g,\n (match: string, _wildcard: string, key: string) => {\n const val = params[key];\n if (!val) {\n throw new Error(`unreplaced param ${match} in url: ${urlPathFormat}`);\n }\n return val;\n }\n );\n return urlPath;\n}\n","/**\n * A trie data structure that stores routes. The trie supports `:param` and\n * `*wildcard` values.\n */\nexport class RouteTrie<T> {\n private children: Record<string, RouteTrie<T>> = {};\n private paramChild?: ParamChild<T>;\n private wildcardChild?: WildcardChild<T>;\n private route?: T;\n\n /**\n * Adds a route to the trie.\n */\n add(path: string, route: T) {\n path = this.normalizePath(path);\n\n // If the end was reached, save the value to the node.\n if (path === '') {\n this.route = route;\n return;\n }\n\n const [head, tail] = this.splitPath(path);\n if (head.startsWith('[...') && head.endsWith(']')) {\n const paramName = head.slice(4, -1);\n this.wildcardChild = new WildcardChild(paramName, route);\n return;\n }\n\n let nextNode: RouteTrie<T>;\n if (head.startsWith('[') && head.endsWith(']')) {\n if (!this.paramChild) {\n const paramName = head.slice(1, -1);\n this.paramChild = new ParamChild(paramName);\n }\n nextNode = this.paramChild.trie;\n } else {\n nextNode = this.children[head];\n if (!nextNode) {\n nextNode = new RouteTrie();\n this.children[head] = nextNode;\n }\n }\n nextNode.add(tail, route);\n }\n\n /**\n * Returns a route mapped to the given path and any parameter values from the\n * URL.\n */\n get(path: string): [T | undefined, Record<string, string>] {\n const params = {};\n const route = this.getRoute(path, params);\n return [route, params];\n }\n\n /**\n * Walks the route trie and calls a callback function for each route.\n */\n walk(cb: (urlPath: string, route: T) => Promise<any> | void): Promise<any> {\n const promises: Array<Promise<any>> = [];\n const addPromise = (promise: Promise<any> | void) => {\n if (promise) {\n promises.push(promise);\n }\n };\n if (this.route) {\n addPromise(cb('/', this.route));\n }\n if (this.paramChild) {\n const param = `[${this.paramChild.name}]`;\n this.paramChild.trie.walk((childPath: string, route: T) => {\n const paramUrlPath = `/${param}${childPath}`;\n addPromise(cb(paramUrlPath, route));\n });\n }\n if (this.wildcardChild) {\n const wildcardUrlPath = `/[...${this.wildcardChild.name}]`;\n addPromise(cb(wildcardUrlPath, this.wildcardChild.route));\n }\n for (const subpath of Object.keys(this.children)) {\n const childTrie = this.children[subpath];\n childTrie.walk((childPath: string, childRoute: T) => {\n addPromise(cb(`/${subpath}${childPath}`, childRoute));\n });\n }\n return Promise.all(promises);\n }\n\n /**\n * Removes all routes from the trie.\n */\n clear() {\n this.children = {};\n this.paramChild = undefined;\n this.wildcardChild = undefined;\n this.route = undefined;\n }\n\n private getRoute(\n urlPath: string,\n params: Record<string, string>\n ): T | undefined {\n urlPath = this.normalizePath(urlPath);\n if (urlPath === '') {\n return this.route;\n }\n\n const [head, tail] = this.splitPath(urlPath);\n\n const child = this.children[head];\n if (child) {\n const route = child.getRoute(tail, params);\n if (route) {\n return route;\n }\n }\n\n if (this.paramChild) {\n const route = this.paramChild.trie.getRoute(tail, params);\n if (route) {\n params[this.paramChild.name] = head;\n return route;\n }\n }\n\n if (this.wildcardChild) {\n params[this.wildcardChild.name] = urlPath;\n return this.wildcardChild.route;\n }\n\n return undefined;\n }\n\n /**\n * Normalizes a path for inclusion into the route trie.\n */\n private normalizePath(path: string) {\n // Remove leading slashes.\n return path.replace(/^\\/+/g, '');\n }\n\n /**\n * Splits the parent directory from its children, e.g.:\n *\n * splitPath(\"foo/bar/baz\") -> [\"foo\", \"bar/baz\"]\n */\n private splitPath(path: string): [string, string] {\n const i = path.indexOf('/');\n if (i === -1) {\n return [path, ''];\n }\n return [path.slice(0, i), path.slice(i + 1)];\n }\n}\n\n/**\n * A node in the RouteTrie for a :param child.\n */\nclass ParamChild<T> {\n readonly name: string;\n readonly trie: RouteTrie<T> = new RouteTrie();\n\n constructor(name: string) {\n this.name = name;\n }\n}\n\n/**\n * A node in the RouteTrie for a *wildcard child.\n */\nclass WildcardChild<T> {\n readonly name: string;\n readonly route: T;\n\n constructor(name: string, route: T) {\n this.name = name;\n this.route = route;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/render/render.tsx","../src/render/router.ts","../src/render/route-trie.ts","../src/core/components/dev-not-found-page.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport {ComponentChildren} from 'preact';\nimport renderToString from 'preact-render-to-string';\nimport {getRoutes, Route, getAllPathsForRoute} from './router';\nimport {HEAD_CONTEXT} from '../core/components/head';\nimport {ErrorPage} from '../core/components/error-page';\nimport {getTranslations, I18N_CONTEXT} from '../core/i18n';\nimport {ScriptProps, SCRIPT_CONTEXT} from '../core/components/script';\nimport {AssetMap} from './asset-map/asset-map';\nimport {RootConfig} from '../core/config';\nimport {RouteTrie} from './route-trie';\nimport {elementsMap} from 'virtual:root-elements';\nimport {DevNotFoundPage} from '../core/components/dev-not-found-page';\nimport {RequestContext, REQUEST_CONTEXT} from '../core/context';\n\ninterface RenderHtmlOptions {\n mainHtml: string;\n locale: string;\n headComponents?: ComponentChildren[];\n}\n\nexport class Renderer {\n private rootConfig: RootConfig;\n private routes: RouteTrie<Route>;\n private assetMap: AssetMap;\n\n constructor(rootConfig: RootConfig, options: {assetMap: AssetMap}) {\n this.rootConfig = rootConfig;\n this.routes = getRoutes(this.rootConfig);\n this.assetMap = options.assetMap;\n }\n\n async render(url: string): Promise<{html?: string; notFound?: boolean}> {\n const [route, routeParams] = this.routes.get(url);\n if (route && route.module && route.module.default) {\n return await this.renderRoute(route, {routeParams});\n }\n return {notFound: true};\n }\n\n async renderRoute(\n route: Route,\n options: {routeParams: Record<string, string>}\n ): Promise<{html?: string; notFound?: boolean}> {\n const routeParams = options.routeParams;\n const assetMap = this.assetMap;\n const Component = route.module.default;\n if (!Component) {\n throw new Error(\n 'unable to render route. the route should have a default export that renders a jsx component.'\n );\n }\n let props = {};\n if (route.module.getStaticProps) {\n const propsData = await route.module.getStaticProps({\n params: routeParams,\n });\n if (propsData.notFound) {\n return this.render404();\n }\n if (propsData.props) {\n props = propsData.props;\n }\n }\n\n const ctx: RequestContext = {route};\n const locale = route.locale;\n const translations = getTranslations(locale);\n const headComponents: ComponentChildren[] = [];\n const userScripts: ScriptProps[] = [];\n const vdom = (\n <REQUEST_CONTEXT.Provider value={ctx}>\n <I18N_CONTEXT.Provider value={{locale, translations}}>\n <SCRIPT_CONTEXT.Provider value={userScripts}>\n <HEAD_CONTEXT.Provider value={headComponents}>\n <Component {...props} />\n </HEAD_CONTEXT.Provider>\n </SCRIPT_CONTEXT.Provider>\n </I18N_CONTEXT.Provider>\n </REQUEST_CONTEXT.Provider>\n );\n const mainHtml = renderToString(vdom, {}, {pretty: true});\n\n const jsDeps = new Set<string>();\n const cssDeps = new Set<string>();\n\n // Walk the page's dependency tree for CSS dependencies that are added via\n // `import 'foo.scss'` or `import 'foo.module.scss'`.\n const pageAsset = await assetMap.get(route.src);\n if (pageAsset) {\n const pageCssDeps = await pageAsset.getCssDeps();\n pageCssDeps.forEach((dep) => cssDeps.add(dep));\n }\n\n // Parse the HTML for custom elements that are found within the project\n // and automatically inject the script deps for them.\n await this.collectElementDeps(mainHtml, jsDeps, cssDeps);\n\n // Add user defined scripts added via the `<Script>` component.\n await Promise.all(\n userScripts.map(async (scriptDep) => {\n const scriptAsset = await assetMap.get(scriptDep.src.slice(1));\n if (scriptAsset) {\n jsDeps.add(scriptAsset.assetUrl);\n const scriptJsDeps = await scriptAsset.getJsDeps();\n scriptJsDeps.forEach((dep) => jsDeps.add(dep));\n }\n })\n );\n\n cssDeps.forEach((cssUrl) => {\n headComponents.push(<link rel=\"stylesheet\" href={cssUrl} />);\n });\n jsDeps.forEach((jsUrls) => {\n headComponents.push(<script type=\"module\" src={jsUrls} />);\n });\n\n const html = await this.renderHtml({mainHtml, locale, headComponents});\n return {html};\n }\n\n async getSitemap(): Promise<\n Record<string, {route: Route; params: Record<string, string>}>\n > {\n const sitemap: Record<\n string,\n {route: Route; params: Record<string, string>}\n > = {};\n await this.routes.walk(async (urlPath: string, route: Route) => {\n const routePaths = await getAllPathsForRoute(urlPath, route);\n routePaths.forEach((routePath) => {\n sitemap[routePath.urlPath] = {\n route,\n params: routePath.params,\n };\n });\n });\n return sitemap;\n }\n\n private async renderHtml(options: RenderHtmlOptions) {\n const page = (\n <html lang={options.locale}>\n <head>\n <meta charSet=\"utf-8\" />\n {options.headComponents}\n </head>\n <body dangerouslySetInnerHTML={{__html: options.mainHtml}} />\n </html>\n );\n const html = `<!doctype html>\\n${renderToString(\n page,\n {},\n {pretty: true}\n )}\\n`;\n return html;\n }\n\n async render404() {\n return {\n html: '<!doctype html><html><title>404 Not Found</title><h1>404 Not Found</h1>',\n notFound: true,\n };\n }\n\n async renderError(error: unknown) {\n const mainHtml = renderToString(<ErrorPage error={error} />);\n const html = await this.renderHtml({mainHtml, locale: 'en'});\n return {html};\n }\n\n async renderDevServer404() {\n const sitemap = await this.getSitemap();\n const mainHtml = renderToString(<DevNotFoundPage sitemap={sitemap} />);\n const html = await this.renderHtml({\n mainHtml,\n locale: 'en',\n headComponents: [<title>404: Not Found</title>],\n });\n return {html};\n }\n\n /**\n * Parses rendered HTML for custom element tags used on the page and\n * automatically adds the JS/CSS deps to the page.\n */\n private async collectElementDeps(\n html: string,\n jsDeps: Set<string>,\n cssDeps: Set<string>,\n ): Promise<{jsDeps: Set<string>; cssDeps: Set<string>}> {\n const assetMap = this.assetMap;\n\n const re = /<(\\w[\\w-]+\\w)/g;\n const matches = Array.from(html.matchAll(re));\n await Promise.all(\n matches.map(async (match) => {\n const tagName = match[1];\n // Custom elements require a dash.\n if (tagName && tagName.includes('-') && tagName in elementsMap) {\n const elementModule = elementsMap[tagName];\n const asset = await assetMap.get(elementModule.src);\n if (!asset) {\n return;\n }\n const assetJsDeps = await asset.getJsDeps();\n assetJsDeps.forEach((dep) => jsDeps.add(dep));\n const assetCssDeps = await asset.getCssDeps();\n assetCssDeps.forEach((dep) => cssDeps.add(dep));\n }\n })\n );\n\n return {jsDeps, cssDeps};\n }\n}\n","import path from 'node:path';\nimport {ComponentType} from 'preact';\nimport {RootConfig} from '../core/config';\nimport {GetStaticPaths, GetStaticProps} from '../core/types';\nimport {RouteTrie} from './route-trie';\n\nexport interface RouteModule {\n default?: ComponentType<unknown>;\n getStaticPaths?: GetStaticPaths;\n getStaticProps?: GetStaticProps;\n}\n\nexport interface Route {\n src: string;\n module: RouteModule;\n locale: string;\n}\n\nexport function getRoutes(config: RootConfig) {\n const locales = config.i18n?.locales || [];\n const i18nUrlFormat = config.i18n?.urlFormat || '/{locale}/{path}';\n const defaultLocale = config.i18n?.defaultLocale || 'en';\n\n const routes = import.meta.glob(\n ['/routes/*.ts', '/routes/**/*.ts', '/routes/*.tsx', '/routes/**/*.tsx'],\n {\n eager: true,\n }\n );\n const trie = new RouteTrie<Route>();\n Object.keys(routes).forEach((modulePath) => {\n const src = modulePath.slice(1);\n let routePath = modulePath.replace(/^\\/routes/, '');\n const parts = path.parse(routePath);\n if (parts.name.startsWith('_')) {\n return;\n }\n if (parts.name === 'index') {\n routePath = parts.dir;\n } else {\n routePath = path.join(parts.dir, parts.name);\n }\n trie.add(routePath, {\n src,\n module: routes[modulePath] as RouteModule,\n locale: defaultLocale,\n });\n\n // At the moment, all routes are assumed to use the site-wide i18n config.\n // TODO(stevenle): provide routes with a way to override the default\n // i18n serving behavior.\n locales.forEach((locale) => {\n const localeRoutePath = i18nUrlFormat\n .replace('{locale}', locale)\n .replace('{path}', routePath.replace(/^\\/*/, ''));\n if (localeRoutePath !== routePath) {\n trie.add(localeRoutePath, {\n src,\n module: routes[modulePath] as RouteModule,\n locale: locale,\n });\n }\n });\n });\n return trie;\n}\n\nexport async function getAllPathsForRoute(\n urlPathFormat: string,\n route: Route\n): Promise<Array<{urlPath: string; params: Record<string, string>}>> {\n const urlPaths: Array<{urlPath: string; params: Record<string, string>}> = [];\n const routeModule = route.module;\n if (routeModule.getStaticPaths) {\n const staticPaths = await routeModule.getStaticPaths();\n if (staticPaths.paths) {\n staticPaths.paths.forEach(\n (pathParams: {params: Record<string, string>}) => {\n const urlPath = replaceParams(urlPathFormat, pathParams.params || {});\n if (pathContainsPlaceholders(urlPath)) {\n console.warn(\n `path contains placeholders: ${urlPathFormat}, double check getStaticPaths() and ensure all params are returned`\n );\n } else {\n urlPaths.push({\n urlPath: replaceParams(urlPathFormat, pathParams.params),\n params: pathParams.params || {},\n });\n }\n }\n );\n }\n } else if (pathContainsPlaceholders(urlPathFormat)) {\n console.warn(\n `path contains placeholders: ${urlPathFormat}, did you forget to define getStaticPaths()?`\n );\n } else {\n urlPaths.push({urlPath: urlPathFormat, params: {}});\n }\n return urlPaths;\n}\n\nexport function replaceParams(\n urlPathFormat: string,\n params: Record<string, string>\n) {\n const urlPath = urlPathFormat.replaceAll(\n /\\[(\\.\\.\\.)?([\\w\\-_]*)\\]/g,\n (match: string, _wildcard: string, key: string) => {\n const val = params[key];\n if (!val) {\n throw new Error(`unreplaced param ${match} in url: ${urlPathFormat}`);\n }\n return val;\n }\n );\n return urlPath;\n}\n\nfunction pathContainsPlaceholders(urlPath: string) {\n const segments = urlPath.split('/');\n return segments.some((segment) => {\n return segment.startsWith('[') && segment.endsWith(']');\n });\n}\n","/**\n * A trie data structure that stores routes. The trie supports `:param` and\n * `*wildcard` values.\n */\nexport class RouteTrie<T> {\n private children: Record<string, RouteTrie<T>> = {};\n private paramChild?: ParamChild<T>;\n private wildcardChild?: WildcardChild<T>;\n private route?: T;\n\n /**\n * Adds a route to the trie.\n */\n add(path: string, route: T) {\n path = this.normalizePath(path);\n\n // If the end was reached, save the value to the node.\n if (path === '') {\n this.route = route;\n return;\n }\n\n const [head, tail] = this.splitPath(path);\n if (head.startsWith('[...') && head.endsWith(']')) {\n const paramName = head.slice(4, -1);\n this.wildcardChild = new WildcardChild(paramName, route);\n return;\n }\n\n let nextNode: RouteTrie<T>;\n if (head.startsWith('[') && head.endsWith(']')) {\n if (!this.paramChild) {\n const paramName = head.slice(1, -1);\n this.paramChild = new ParamChild(paramName);\n }\n nextNode = this.paramChild.trie;\n } else {\n nextNode = this.children[head];\n if (!nextNode) {\n nextNode = new RouteTrie();\n this.children[head] = nextNode;\n }\n }\n nextNode.add(tail, route);\n }\n\n /**\n * Returns a route mapped to the given path and any parameter values from the\n * URL.\n */\n get(path: string): [T | undefined, Record<string, string>] {\n const params = {};\n const route = this.getRoute(path, params);\n return [route, params];\n }\n\n /**\n * Walks the route trie and calls a callback function for each route.\n */\n walk(cb: (urlPath: string, route: T) => Promise<void> | void): Promise<void> {\n const promises: Array<Promise<void>> = [];\n const addPromise = (promise: Promise<void> | void) => {\n if (promise) {\n promises.push(promise);\n }\n };\n if (this.route) {\n addPromise(cb('/', this.route));\n }\n if (this.paramChild) {\n const param = `[${this.paramChild.name}]`;\n this.paramChild.trie.walk((childPath: string, route: T) => {\n const paramUrlPath = `/${param}${childPath}`;\n addPromise(cb(paramUrlPath, route));\n });\n }\n if (this.wildcardChild) {\n const wildcardUrlPath = `/[...${this.wildcardChild.name}]`;\n addPromise(cb(wildcardUrlPath, this.wildcardChild.route));\n }\n for (const subpath of Object.keys(this.children)) {\n const childTrie = this.children[subpath];\n childTrie.walk((childPath: string, childRoute: T) => {\n addPromise(cb(`/${subpath}${childPath}`, childRoute));\n });\n }\n return Promise.all(promises).then(() => {});\n }\n\n /**\n * Removes all routes from the trie.\n */\n clear() {\n this.children = {};\n this.paramChild = undefined;\n this.wildcardChild = undefined;\n this.route = undefined;\n }\n\n private getRoute(\n urlPath: string,\n params: Record<string, string>\n ): T | undefined {\n urlPath = this.normalizePath(urlPath);\n if (urlPath === '') {\n return this.route;\n }\n\n const [head, tail] = this.splitPath(urlPath);\n\n const child = this.children[head];\n if (child) {\n const route = child.getRoute(tail, params);\n if (route) {\n return route;\n }\n }\n\n if (this.paramChild) {\n const route = this.paramChild.trie.getRoute(tail, params);\n if (route) {\n params[this.paramChild.name] = head;\n return route;\n }\n }\n\n if (this.wildcardChild) {\n params[this.wildcardChild.name] = urlPath;\n return this.wildcardChild.route;\n }\n\n return undefined;\n }\n\n /**\n * Normalizes a path for inclusion into the route trie.\n */\n private normalizePath(path: string) {\n // Remove leading slashes.\n return path.replace(/^\\/+/g, '');\n }\n\n /**\n * Splits the parent directory from its children, e.g.:\n *\n * splitPath(\"foo/bar/baz\") -> [\"foo\", \"bar/baz\"]\n */\n private splitPath(path: string): [string, string] {\n const i = path.indexOf('/');\n if (i === -1) {\n return [path, ''];\n }\n return [path.slice(0, i), path.slice(i + 1)];\n }\n}\n\n/**\n * A node in the RouteTrie for a :param child.\n */\nclass ParamChild<T> {\n readonly name: string;\n readonly trie: RouteTrie<T> = new RouteTrie();\n\n constructor(name: string) {\n this.name = name;\n }\n}\n\n/**\n * A node in the RouteTrie for a *wildcard child.\n */\nclass WildcardChild<T> {\n readonly name: string;\n readonly route: T;\n\n constructor(name: string, route: T) {\n this.name = name;\n this.route = route;\n }\n}\n","import {Route} from '../../render/router';\n\nconst STYLES = `\nbody {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n}\n\nh1 {\n margin-bottom: 40px;\n}\n\n.route-list {\n display: grid;\n grid-template-columns: auto 1fr;\n align-items: center;\n column-gap: 24px;\n row-gap: 8px;\n padding: 0 40px;\n}\n\n.route-module {\n text-align: right;\n}\n\nhr {\n margin: 60px 0;\n}\n`;\n\ninterface DevNotFoundPageProps {\n sitemap: Record<string, {route: Route; params: Record<string, string>}>;\n}\n\nexport function DevNotFoundPage(props: DevNotFoundPageProps) {\n const routesList: Array<Route & {urlPath: string}> = [];\n Object.keys(props.sitemap).forEach((urlPath) => {\n const route = props.sitemap[urlPath].route;\n routesList.push(Object.assign({}, route, {urlPath}));\n });\n return (\n <>\n <style dangerouslySetInnerHTML={{__html: STYLES}}></style>\n <div>\n <h1>\n <strong>404:</strong> Not Found\n </h1>\n {routesList.length > 0 ? (\n <>\n <h2>Routes found in the project:</h2>\n <div class=\"route-list\">\n {routesList.map((route) => (\n <>\n <div class=\"route-module\">{route.modulePath}</div>\n <a class=\"route-url\" href={route.urlPath}>\n {route.urlPath}\n </a>\n </>\n ))}\n </div>\n </>\n ) : (\n <>\n <h2>No routes found in the project</h2>\n <p>\n Add your first route at <code>/routes/index.tsx</code>\n </p>\n </>\n )}\n <hr />\n <p>\n Note: This page could use some love! If you're interested in helping\n to style this page, please reach out to the root.js developers!\n </p>\n </div>\n </>\n );\n}\n"],"mappings":";;;;;;;;;;AAEA,OAAO,oBAAoB;;;ACF3B,OAAO,UAAU;;;ACIV,IAAM,YAAN,MAAmB;AAAA,EAAnB;AACL,SAAQ,WAAyC,CAAC;AAAA;AAAA,EAQlD,IAAIA,OAAc,OAAU;AAC1B,IAAAA,QAAO,KAAK,cAAcA,KAAI;AAG9B,QAAIA,UAAS,IAAI;AACf,WAAK,QAAQ;AACb;AAAA,IACF;AAEA,UAAM,CAAC,MAAM,IAAI,IAAI,KAAK,UAAUA,KAAI;AACxC,QAAI,KAAK,WAAW,MAAM,KAAK,KAAK,SAAS,GAAG,GAAG;AACjD,YAAM,YAAY,KAAK,MAAM,GAAG,EAAE;AAClC,WAAK,gBAAgB,IAAI,cAAc,WAAW,KAAK;AACvD;AAAA,IACF;AAEA,QAAI;AACJ,QAAI,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,GAAG;AAC9C,UAAI,CAAC,KAAK,YAAY;AACpB,cAAM,YAAY,KAAK,MAAM,GAAG,EAAE;AAClC,aAAK,aAAa,IAAI,WAAW,SAAS;AAAA,MAC5C;AACA,iBAAW,KAAK,WAAW;AAAA,IAC7B,OAAO;AACL,iBAAW,KAAK,SAAS;AACzB,UAAI,CAAC,UAAU;AACb,mBAAW,IAAI,UAAU;AACzB,aAAK,SAAS,QAAQ;AAAA,MACxB;AAAA,IACF;AACA,aAAS,IAAI,MAAM,KAAK;AAAA,EAC1B;AAAA,EAMA,IAAIA,OAAuD;AACzD,UAAM,SAAS,CAAC;AAChB,UAAM,QAAQ,KAAK,SAASA,OAAM,MAAM;AACxC,WAAO,CAAC,OAAO,MAAM;AAAA,EACvB;AAAA,EAKA,KAAK,IAAwE;AAC3E,UAAM,WAAiC,CAAC;AACxC,UAAM,aAAa,CAAC,YAAkC;AACpD,UAAI,SAAS;AACX,iBAAS,KAAK,OAAO;AAAA,MACvB;AAAA,IACF;AACA,QAAI,KAAK,OAAO;AACd,iBAAW,GAAG,KAAK,KAAK,KAAK,CAAC;AAAA,IAChC;AACA,QAAI,KAAK,YAAY;AACnB,YAAM,QAAQ,IAAI,KAAK,WAAW;AAClC,WAAK,WAAW,KAAK,KAAK,CAAC,WAAmB,UAAa;AACzD,cAAM,eAAe,IAAI,QAAQ;AACjC,mBAAW,GAAG,cAAc,KAAK,CAAC;AAAA,MACpC,CAAC;AAAA,IACH;AACA,QAAI,KAAK,eAAe;AACtB,YAAM,kBAAkB,QAAQ,KAAK,cAAc;AACnD,iBAAW,GAAG,iBAAiB,KAAK,cAAc,KAAK,CAAC;AAAA,IAC1D;AACA,eAAW,WAAW,OAAO,KAAK,KAAK,QAAQ,GAAG;AAChD,YAAM,YAAY,KAAK,SAAS;AAChC,gBAAU,KAAK,CAAC,WAAmB,eAAkB;AACnD,mBAAW,GAAG,IAAI,UAAU,aAAa,UAAU,CAAC;AAAA,MACtD,CAAC;AAAA,IACH;AACA,WAAO,QAAQ,IAAI,QAAQ,EAAE,KAAK,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5C;AAAA,EAKA,QAAQ;AACN,SAAK,WAAW,CAAC;AACjB,SAAK,aAAa;AAClB,SAAK,gBAAgB;AACrB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEQ,SACN,SACA,QACe;AACf,cAAU,KAAK,cAAc,OAAO;AACpC,QAAI,YAAY,IAAI;AAClB,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,CAAC,MAAM,IAAI,IAAI,KAAK,UAAU,OAAO;AAE3C,UAAM,QAAQ,KAAK,SAAS;AAC5B,QAAI,OAAO;AACT,YAAM,QAAQ,MAAM,SAAS,MAAM,MAAM;AACzC,UAAI,OAAO;AACT,eAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI,KAAK,YAAY;AACnB,YAAM,QAAQ,KAAK,WAAW,KAAK,SAAS,MAAM,MAAM;AACxD,UAAI,OAAO;AACT,eAAO,KAAK,WAAW,QAAQ;AAC/B,eAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI,KAAK,eAAe;AACtB,aAAO,KAAK,cAAc,QAAQ;AAClC,aAAO,KAAK,cAAc;AAAA,IAC5B;AAEA,WAAO;AAAA,EACT;AAAA,EAKQ,cAAcA,OAAc;AAElC,WAAOA,MAAK,QAAQ,SAAS,EAAE;AAAA,EACjC;AAAA,EAOQ,UAAUA,OAAgC;AAChD,UAAM,IAAIA,MAAK,QAAQ,GAAG;AAC1B,QAAI,MAAM,IAAI;AACZ,aAAO,CAACA,OAAM,EAAE;AAAA,IAClB;AACA,WAAO,CAACA,MAAK,MAAM,GAAG,CAAC,GAAGA,MAAK,MAAM,IAAI,CAAC,CAAC;AAAA,EAC7C;AACF;AAKA,IAAM,aAAN,MAAoB;AAAA,EAIlB,YAAY,MAAc;AAF1B,SAAS,OAAqB,IAAI,UAAU;AAG1C,SAAK,OAAO;AAAA,EACd;AACF;AAKA,IAAM,gBAAN,MAAuB;AAAA,EAIrB,YAAY,MAAc,OAAU;AAClC,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;;;ADjKO,SAAS,UAAU,QAAoB;AAlB9C;AAmBE,QAAM,YAAU,YAAO,SAAP,mBAAa,YAAW,CAAC;AACzC,QAAM,kBAAgB,YAAO,SAAP,mBAAa,cAAa;AAChD,QAAM,kBAAgB,YAAO,SAAP,mBAAa,kBAAiB;AAEpD,QAAM,SAAS,YAAY;AAAA,IACzB,CAAC,gBAAgB,mBAAmB,iBAAiB,kBAAkB;AAAA,IACvE;AAAA,MACE,OAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,OAAO,IAAI,UAAiB;AAClC,SAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,eAAe;AAC1C,UAAM,MAAM,WAAW,MAAM,CAAC;AAC9B,QAAI,YAAY,WAAW,QAAQ,aAAa,EAAE;AAClD,UAAM,QAAQ,KAAK,MAAM,SAAS;AAClC,QAAI,MAAM,KAAK,WAAW,GAAG,GAAG;AAC9B;AAAA,IACF;AACA,QAAI,MAAM,SAAS,SAAS;AAC1B,kBAAY,MAAM;AAAA,IACpB,OAAO;AACL,kBAAY,KAAK,KAAK,MAAM,KAAK,MAAM,IAAI;AAAA,IAC7C;AACA,SAAK,IAAI,WAAW;AAAA,MAClB;AAAA,MACA,QAAQ,OAAO;AAAA,MACf,QAAQ;AAAA,IACV,CAAC;AAKD,YAAQ,QAAQ,CAAC,WAAW;AAC1B,YAAM,kBAAkB,cACrB,QAAQ,YAAY,MAAM,EAC1B,QAAQ,UAAU,UAAU,QAAQ,QAAQ,EAAE,CAAC;AAClD,UAAI,oBAAoB,WAAW;AACjC,aAAK,IAAI,iBAAiB;AAAA,UACxB;AAAA,UACA,QAAQ,OAAO;AAAA,UACf;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,oBACpB,eACA,OACmE;AACnE,QAAM,WAAqE,CAAC;AAC5E,QAAM,cAAc,MAAM;AAC1B,MAAI,YAAY,gBAAgB;AAC9B,UAAM,cAAc,MAAM,YAAY,eAAe;AACrD,QAAI,YAAY,OAAO;AACrB,kBAAY,MAAM;AAAA,QAChB,CAAC,eAAiD;AAChD,gBAAM,UAAU,cAAc,eAAe,WAAW,UAAU,CAAC,CAAC;AACpE,cAAI,yBAAyB,OAAO,GAAG;AACrC,oBAAQ;AAAA,cACN,+BAA+B;AAAA,YACjC;AAAA,UACF,OAAO;AACL,qBAAS,KAAK;AAAA,cACZ,SAAS,cAAc,eAAe,WAAW,MAAM;AAAA,cACvD,QAAQ,WAAW,UAAU,CAAC;AAAA,YAChC,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,WAAW,yBAAyB,aAAa,GAAG;AAClD,YAAQ;AAAA,MACN,+BAA+B;AAAA,IACjC;AAAA,EACF,OAAO;AACL,aAAS,KAAK,EAAC,SAAS,eAAe,QAAQ,CAAC,EAAC,CAAC;AAAA,EACpD;AACA,SAAO;AACT;AAEO,SAAS,cACd,eACA,QACA;AACA,QAAM,UAAU,cAAc;AAAA,IAC5B;AAAA,IACA,CAAC,OAAe,WAAmB,QAAgB;AACjD,YAAM,MAAM,OAAO;AACnB,UAAI,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,oBAAoB,iBAAiB,eAAe;AAAA,MACtE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,SAAiB;AACjD,QAAM,WAAW,QAAQ,MAAM,GAAG;AAClC,SAAO,SAAS,KAAK,CAAC,YAAY;AAChC,WAAO,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG;AAAA,EACxD,CAAC;AACH;;;ADjHA,SAAQ,mBAAkB;;;AG8BpB,SAUU,UAVV,KAEE,YAFF;AAvCN,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BR,SAAS,gBAAgB,OAA6B;AAC3D,QAAM,aAA+C,CAAC;AACtD,SAAO,KAAK,MAAM,OAAO,EAAE,QAAQ,CAAC,YAAY;AAC9C,UAAM,QAAQ,MAAM,QAAQ,SAAS;AACrC,eAAW,KAAK,OAAO,OAAO,CAAC,GAAG,OAAO,EAAC,QAAO,CAAC,CAAC;AAAA,EACrD,CAAC;AACD,SACE;AAAA,IACE;AAAA,0BAAC;AAAA,QAAM,yBAAyB,EAAC,QAAQ,OAAM;AAAA,OAAG;AAAA,MAClD,qBAAC;AAAA,QACC;AAAA,+BAAC;AAAA,YACC;AAAA,kCAAC;AAAA,gBAAO;AAAA,eAAI;AAAA,cAAS;AAAA;AAAA,WACvB;AAAA,UACC,WAAW,SAAS,IACnB;AAAA,YACE;AAAA,kCAAC;AAAA,gBAAG;AAAA,eAA4B;AAAA,cAChC,oBAAC;AAAA,gBAAI,OAAM;AAAA,gBACR,qBAAW,IAAI,CAAC,UACf;AAAA,kBACE;AAAA,wCAAC;AAAA,sBAAI,OAAM;AAAA,sBAAgB,gBAAM;AAAA,qBAAW;AAAA,oBAC5C,oBAAC;AAAA,sBAAE,OAAM;AAAA,sBAAY,MAAM,MAAM;AAAA,sBAC9B,gBAAM;AAAA,qBACT;AAAA;AAAA,iBACF,CACD;AAAA,eACH;AAAA;AAAA,WACF,IAEA;AAAA,YACE;AAAA,kCAAC;AAAA,gBAAG;AAAA,eAA8B;AAAA,cAClC,qBAAC;AAAA,gBAAE;AAAA;AAAA,kBACuB,oBAAC;AAAA,oBAAK;AAAA,mBAAiB;AAAA;AAAA,eACjD;AAAA;AAAA,WACF;AAAA,UAEF,oBAAC,QAAG;AAAA,UACJ,oBAAC;AAAA,YAAE;AAAA,WAGH;AAAA;AAAA,OACF;AAAA;AAAA,GACF;AAEJ;;;AHDc,gBAAAC,MAoEN,QAAAC,aApEM;AAtDP,IAAM,WAAN,MAAe;AAAA,EAKpB,YAAY,YAAwB,SAA+B;AACjE,SAAK,aAAa;AAClB,SAAK,SAAS,UAAU,KAAK,UAAU;AACvC,SAAK,WAAW,QAAQ;AAAA,EAC1B;AAAA,EAEA,MAAM,OAAO,KAA2D;AACtE,UAAM,CAAC,OAAO,WAAW,IAAI,KAAK,OAAO,IAAI,GAAG;AAChD,QAAI,SAAS,MAAM,UAAU,MAAM,OAAO,SAAS;AACjD,aAAO,MAAM,KAAK,YAAY,OAAO,EAAC,YAAW,CAAC;AAAA,IACpD;AACA,WAAO,EAAC,UAAU,KAAI;AAAA,EACxB;AAAA,EAEA,MAAM,YACJ,OACA,SAC8C;AAC9C,UAAM,cAAc,QAAQ;AAC5B,UAAM,WAAW,KAAK;AACtB,UAAM,YAAY,MAAM,OAAO;AAC/B,QAAI,CAAC,WAAW;AACd,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,QAAQ,CAAC;AACb,QAAI,MAAM,OAAO,gBAAgB;AAC/B,YAAM,YAAY,MAAM,MAAM,OAAO,eAAe;AAAA,QAClD,QAAQ;AAAA,MACV,CAAC;AACD,UAAI,UAAU,UAAU;AACtB,eAAO,KAAK,UAAU;AAAA,MACxB;AACA,UAAI,UAAU,OAAO;AACnB,gBAAQ,UAAU;AAAA,MACpB;AAAA,IACF;AAEA,UAAM,MAAsB,EAAC,MAAK;AAClC,UAAM,SAAS,MAAM;AACrB,UAAM,eAAe,gBAAgB,MAAM;AAC3C,UAAM,iBAAsC,CAAC;AAC7C,UAAM,cAA6B,CAAC;AACpC,UAAM,OACJ,gBAAAD,KAAC,gBAAgB,UAAhB;AAAA,MAAyB,OAAO;AAAA,MAC/B,0BAAAA,KAAC,aAAa,UAAb;AAAA,QAAsB,OAAO,EAAC,QAAQ,aAAY;AAAA,QACjD,0BAAAA,KAAC,eAAe,UAAf;AAAA,UAAwB,OAAO;AAAA,UAC9B,0BAAAA,KAAC,aAAa,UAAb;AAAA,YAAsB,OAAO;AAAA,YAC5B,0BAAAA,KAAC;AAAA,cAAW,GAAG;AAAA,aAAO;AAAA,WACxB;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEF,UAAM,WAAW,eAAe,MAAM,CAAC,GAAG,EAAC,QAAQ,KAAI,CAAC;AAExD,UAAM,SAAS,oBAAI,IAAY;AAC/B,UAAM,UAAU,oBAAI,IAAY;AAIhC,UAAM,YAAY,MAAM,SAAS,IAAI,MAAM,GAAG;AAC9C,QAAI,WAAW;AACb,YAAM,cAAc,MAAM,UAAU,WAAW;AAC/C,kBAAY,QAAQ,CAAC,QAAQ,QAAQ,IAAI,GAAG,CAAC;AAAA,IAC/C;AAIA,UAAM,KAAK,mBAAmB,UAAU,QAAQ,OAAO;AAGvD,UAAM,QAAQ;AAAA,MACZ,YAAY,IAAI,OAAO,cAAc;AACnC,cAAM,cAAc,MAAM,SAAS,IAAI,UAAU,IAAI,MAAM,CAAC,CAAC;AAC7D,YAAI,aAAa;AACf,iBAAO,IAAI,YAAY,QAAQ;AAC/B,gBAAM,eAAe,MAAM,YAAY,UAAU;AACjD,uBAAa,QAAQ,CAAC,QAAQ,OAAO,IAAI,GAAG,CAAC;AAAA,QAC/C;AAAA,MACF,CAAC;AAAA,IACH;AAEA,YAAQ,QAAQ,CAAC,WAAW;AAC1B,qBAAe,KAAK,gBAAAA,KAAC;AAAA,QAAK,KAAI;AAAA,QAAa,MAAM;AAAA,OAAQ,CAAE;AAAA,IAC7D,CAAC;AACD,WAAO,QAAQ,CAAC,WAAW;AACzB,qBAAe,KAAK,gBAAAA,KAAC;AAAA,QAAO,MAAK;AAAA,QAAS,KAAK;AAAA,OAAQ,CAAE;AAAA,IAC3D,CAAC;AAED,UAAM,OAAO,MAAM,KAAK,WAAW,EAAC,UAAU,QAAQ,eAAc,CAAC;AACrE,WAAO,EAAC,KAAI;AAAA,EACd;AAAA,EAEA,MAAM,aAEJ;AACA,UAAM,UAGF,CAAC;AACL,UAAM,KAAK,OAAO,KAAK,OAAO,SAAiB,UAAiB;AAC9D,YAAM,aAAa,MAAM,oBAAoB,SAAS,KAAK;AAC3D,iBAAW,QAAQ,CAAC,cAAc;AAChC,gBAAQ,UAAU,WAAW;AAAA,UAC3B;AAAA,UACA,QAAQ,UAAU;AAAA,QACpB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,WAAW,SAA4B;AACnD,UAAM,OACJ,gBAAAC,MAAC;AAAA,MAAK,MAAM,QAAQ;AAAA,MAClB;AAAA,wBAAAA,MAAC;AAAA,UACC;AAAA,4BAAAD,KAAC;AAAA,cAAK,SAAQ;AAAA,aAAQ;AAAA,YACrB,QAAQ;AAAA;AAAA,SACX;AAAA,QACA,gBAAAA,KAAC;AAAA,UAAK,yBAAyB,EAAC,QAAQ,QAAQ,SAAQ;AAAA,SAAG;AAAA;AAAA,KAC7D;AAEF,UAAM,OAAO;AAAA,EAAoB;AAAA,MAC/B;AAAA,MACA,CAAC;AAAA,MACD,EAAC,QAAQ,KAAI;AAAA,IACf;AAAA;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY;AAChB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,OAAgB;AAChC,UAAM,WAAW,eAAe,gBAAAA,KAAC;AAAA,MAAU;AAAA,KAAc,CAAE;AAC3D,UAAM,OAAO,MAAM,KAAK,WAAW,EAAC,UAAU,QAAQ,KAAI,CAAC;AAC3D,WAAO,EAAC,KAAI;AAAA,EACd;AAAA,EAEA,MAAM,qBAAqB;AACzB,UAAM,UAAU,MAAM,KAAK,WAAW;AACtC,UAAM,WAAW,eAAe,gBAAAA,KAAC;AAAA,MAAgB;AAAA,KAAkB,CAAE;AACrE,UAAM,OAAO,MAAM,KAAK,WAAW;AAAA,MACjC;AAAA,MACA,QAAQ;AAAA,MACR,gBAAgB,CAAC,gBAAAA,KAAC;AAAA,QAAM;AAAA,OAAc,CAAQ;AAAA,IAChD,CAAC;AACD,WAAO,EAAC,KAAI;AAAA,EACd;AAAA,EAMA,MAAc,mBACZ,MACA,QACA,SACsD;AACtD,UAAM,WAAW,KAAK;AAEtB,UAAM,KAAK;AACX,UAAM,UAAU,MAAM,KAAK,KAAK,SAAS,EAAE,CAAC;AAC5C,UAAM,QAAQ;AAAA,MACZ,QAAQ,IAAI,OAAO,UAAU;AAC3B,cAAM,UAAU,MAAM;AAEtB,YAAI,WAAW,QAAQ,SAAS,GAAG,KAAK,WAAW,aAAa;AAC9D,gBAAM,gBAAgB,YAAY;AAClC,gBAAM,QAAQ,MAAM,SAAS,IAAI,cAAc,GAAG;AAClD,cAAI,CAAC,OAAO;AACV;AAAA,UACF;AACA,gBAAM,cAAc,MAAM,MAAM,UAAU;AAC1C,sBAAY,QAAQ,CAAC,QAAQ,OAAO,IAAI,GAAG,CAAC;AAC5C,gBAAM,eAAe,MAAM,MAAM,WAAW;AAC5C,uBAAa,QAAQ,CAAC,QAAQ,QAAQ,IAAI,GAAG,CAAC;AAAA,QAChD;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,EAAC,QAAQ,QAAO;AAAA,EACzB;AACF;","names":["path","jsx","jsxs"]}
|