routerino 2.2.2-rc2 → 2.2.2-rc4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/routerino.js +40 -38
- package/dist/routerino.umd.cjs +1 -1
- package/package.json +1 -1
- package/routerino-forge.js +55 -19
package/dist/routerino.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsx as g, jsxs as
|
|
2
|
-
import { createContext as
|
|
1
|
+
import { jsx as g, jsxs as j, Fragment as M } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as J, Component as Q, useContext as V, useState as X, useEffect as Y } from "react";
|
|
3
3
|
import t from "prop-types";
|
|
4
|
-
const
|
|
5
|
-
function
|
|
6
|
-
const i =
|
|
4
|
+
const _ = J(null);
|
|
5
|
+
function ne() {
|
|
6
|
+
const i = V(_);
|
|
7
7
|
if (!i)
|
|
8
8
|
throw new Error(
|
|
9
9
|
"useRouterino must be used within a Routerino router. Make sure your component is rendered inside a <Routerino> component."
|
|
@@ -23,13 +23,13 @@ function l({ tag: i = "meta", soft: c = !1, ...a }) {
|
|
|
23
23
|
;
|
|
24
24
|
h && c || (h || (h = document.createElement(i)), o.forEach((f) => h.setAttribute(f, a[f])), document.querySelector("head").appendChild(h));
|
|
25
25
|
}
|
|
26
|
-
function
|
|
26
|
+
function Z({ routePattern: i, currentRoute: c }) {
|
|
27
27
|
let a = {}, o = i.split("/"), h = c.split("/");
|
|
28
28
|
return o.forEach((f, m) => {
|
|
29
29
|
f.startsWith(":") && (a[f.slice(1)] = h[m]);
|
|
30
30
|
}), a;
|
|
31
31
|
}
|
|
32
|
-
class
|
|
32
|
+
class z extends Q {
|
|
33
33
|
constructor(c) {
|
|
34
34
|
super(c), this.state = { hasError: !1 };
|
|
35
35
|
}
|
|
@@ -48,7 +48,7 @@ class M extends K {
|
|
|
48
48
|
return this.state.hasError ? this.props.fallback : this.props.children;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
z.propTypes = {
|
|
52
52
|
/** The child components to render when there's no error */
|
|
53
53
|
children: t.node,
|
|
54
54
|
/** The fallback UI to display when an error is caught */
|
|
@@ -62,7 +62,7 @@ M.propTypes = {
|
|
|
62
62
|
/** Whether to log debug messages to console (optional) */
|
|
63
63
|
debug: t.bool
|
|
64
64
|
};
|
|
65
|
-
function
|
|
65
|
+
function N({
|
|
66
66
|
routes: i = [
|
|
67
67
|
{
|
|
68
68
|
path: "/",
|
|
@@ -72,26 +72,26 @@ function Y({
|
|
|
72
72
|
tags: [{ property: "og:locale", content: "en_US" }]
|
|
73
73
|
}
|
|
74
74
|
],
|
|
75
|
-
notFoundTemplate: c = /* @__PURE__ */
|
|
75
|
+
notFoundTemplate: c = /* @__PURE__ */ j(M, { children: [
|
|
76
76
|
/* @__PURE__ */ g("p", { children: "No page found for this URL. [404]" }),
|
|
77
77
|
/* @__PURE__ */ g("p", { children: /* @__PURE__ */ g("a", { href: "/", children: "Home" }) })
|
|
78
78
|
] }),
|
|
79
79
|
notFoundTitle: a = "Page not found [404]",
|
|
80
|
-
errorTemplate: o = /* @__PURE__ */
|
|
80
|
+
errorTemplate: o = /* @__PURE__ */ j(M, { children: [
|
|
81
81
|
/* @__PURE__ */ g("p", { children: "Page failed to load. [500]" }),
|
|
82
82
|
/* @__PURE__ */ g("p", { children: /* @__PURE__ */ g("a", { href: "/", children: "Home" }) })
|
|
83
83
|
] }),
|
|
84
84
|
errorTitle: h = "Page error [500]",
|
|
85
85
|
useTrailingSlash: f = !0,
|
|
86
86
|
usePrerenderTags: m = !1,
|
|
87
|
-
baseUrl:
|
|
87
|
+
baseUrl: G = null,
|
|
88
88
|
title: R = "",
|
|
89
89
|
separator: y = " | ",
|
|
90
90
|
imageUrl: v = null,
|
|
91
91
|
touchIconUrl: T = null,
|
|
92
92
|
debug: s = !1
|
|
93
93
|
}) {
|
|
94
|
-
var P, U, C, L, q;
|
|
94
|
+
var P, U, C, L, q, B, W;
|
|
95
95
|
const k = `${h}${y}${R}`, x = `${a}${y}${R}`;
|
|
96
96
|
try {
|
|
97
97
|
if (s) {
|
|
@@ -109,8 +109,10 @@ function Y({
|
|
|
109
109
|
""
|
|
110
110
|
));
|
|
111
111
|
}
|
|
112
|
-
const [$,
|
|
113
|
-
|
|
112
|
+
const [$, A] = X(((P = window == null ? void 0 : window.location) == null ? void 0 : P.href) ?? "/");
|
|
113
|
+
Y(() => {
|
|
114
|
+
if (typeof window > "u" || typeof document > "u")
|
|
115
|
+
return;
|
|
114
116
|
const e = (w) => {
|
|
115
117
|
s && console.debug(
|
|
116
118
|
"%c[Routerino]%c click occurred",
|
|
@@ -176,7 +178,7 @@ function Y({
|
|
|
176
178
|
"%c[Routerino]%c target link is same origin, will use push-state transitioning",
|
|
177
179
|
"color: #6b7280; font-weight: bold",
|
|
178
180
|
""
|
|
179
|
-
), w.preventDefault(), d.href !== window.location.href && (
|
|
181
|
+
), w.preventDefault(), d.href !== window.location.href && (A(d.href), window.history.pushState({}, "", d.href)), window.scrollTo({
|
|
180
182
|
top: 0,
|
|
181
183
|
behavior: "auto"
|
|
182
184
|
})) : s && console.debug(
|
|
@@ -192,25 +194,25 @@ function Y({
|
|
|
192
194
|
"color: #6b7280; font-weight: bold",
|
|
193
195
|
"",
|
|
194
196
|
window.location.pathname
|
|
195
|
-
),
|
|
197
|
+
), A(window.location.href);
|
|
196
198
|
};
|
|
197
199
|
return window.addEventListener("popstate", u), () => {
|
|
198
200
|
document.removeEventListener("click", e), window.removeEventListener("popstate", u);
|
|
199
201
|
};
|
|
200
202
|
}, [$]);
|
|
201
|
-
let r = ((
|
|
203
|
+
let r = ((U = window == null ? void 0 : window.location) == null ? void 0 : U.pathname) ?? "/";
|
|
202
204
|
(r === "/index.html" || r === "") && (r = "/");
|
|
203
|
-
const
|
|
205
|
+
const F = i.find((e) => e.path === r), H = i.find(
|
|
204
206
|
(e) => `${e.path}/` === r || e.path === `${r}/`
|
|
205
|
-
),
|
|
207
|
+
), I = i.find((e) => {
|
|
206
208
|
const u = e.path.endsWith("/") ? e.path.slice(0, -1) : e.path, w = r.endsWith("/") ? r.slice(0, -1) : r, d = u.split("/").filter(Boolean), p = w.split("/").filter(Boolean);
|
|
207
209
|
return d.length !== p.length ? !1 : d.every((b, S) => b.startsWith(":") ? !0 : b === p[S]);
|
|
208
|
-
}), n =
|
|
210
|
+
}), n = F ?? H ?? I;
|
|
209
211
|
if (s && console.debug(
|
|
210
212
|
"%c[Routerino]%c Route matching:",
|
|
211
213
|
"color: #6b7280; font-weight: bold",
|
|
212
214
|
"",
|
|
213
|
-
{ match: n, exactMatch:
|
|
215
|
+
{ match: n, exactMatch: F, addSlashMatch: H, paramsMatch: I }
|
|
214
216
|
), !n)
|
|
215
217
|
return s && (console.group(
|
|
216
218
|
"%c[Routerino]%c 404 - No matching route",
|
|
@@ -237,39 +239,39 @@ function Y({
|
|
|
237
239
|
);
|
|
238
240
|
u && u.remove();
|
|
239
241
|
}
|
|
240
|
-
const
|
|
242
|
+
const D = f && !r.endsWith("/") && r !== "/", O = !f && r.endsWith("/") && r !== "/", K = D ? `${r}/` : O ? r.slice(0, -1) : r, E = `${G ?? ((C = window == null ? void 0 : window.location) == null ? void 0 : C.origin) ?? ""}${K}`;
|
|
241
243
|
if (n.title) {
|
|
242
244
|
const e = `${n.title}${y}${R}`;
|
|
243
245
|
document.title = e, l({
|
|
244
246
|
tag: "link",
|
|
245
247
|
rel: "canonical",
|
|
246
248
|
href: E
|
|
247
|
-
}), (
|
|
249
|
+
}), (L = n.tags) != null && L.find(({ property: u }) => u === "og:title") || l({
|
|
248
250
|
property: "og:title",
|
|
249
251
|
content: e
|
|
250
|
-
}), (
|
|
252
|
+
}), (q = n.tags) != null && q.find(({ property: u }) => u === "og:url") || l({
|
|
251
253
|
property: "og:url",
|
|
252
254
|
content: E
|
|
253
255
|
});
|
|
254
256
|
}
|
|
255
|
-
if (n.description && (l({ name: "description", content: n.description }), (
|
|
257
|
+
if (n.description && (l({ name: "description", content: n.description }), (B = n.tags) != null && B.find(({ property: e }) => e === "og:description") || l({
|
|
256
258
|
property: "og:description",
|
|
257
259
|
content: n.description
|
|
258
260
|
})), (v || n.imageUrl) && l({
|
|
259
261
|
property: "og:image",
|
|
260
262
|
content: n.imageUrl ?? v
|
|
261
|
-
}), (
|
|
263
|
+
}), (W = n.tags) != null && W.find(({ property: e }) => e === "twitter:card") || l({
|
|
262
264
|
name: "twitter:card",
|
|
263
265
|
content: "summary_large_image"
|
|
264
266
|
}), T && l({
|
|
265
267
|
tag: "link",
|
|
266
268
|
rel: "apple-touch-icon",
|
|
267
269
|
href: T
|
|
268
|
-
}), m && (
|
|
270
|
+
}), m && (D || O) && (l({ name: "prerender-status-code", content: "301" }), l({
|
|
269
271
|
name: "prerender-header",
|
|
270
272
|
content: `Location: ${E}`
|
|
271
273
|
})), n.tags && n.tags.length ? (n.tags.find(({ property: e }) => e === "og:type") || l({ property: "og:type", content: "website" }), n.tags.forEach((e) => l(e))) : l({ property: "og:type", content: "website" }), n.element) {
|
|
272
|
-
const e =
|
|
274
|
+
const e = Z({
|
|
273
275
|
routePattern: n.path,
|
|
274
276
|
currentRoute: r
|
|
275
277
|
}), u = {
|
|
@@ -278,8 +280,8 @@ function Y({
|
|
|
278
280
|
routePattern: n.path,
|
|
279
281
|
updateHeadTag: l
|
|
280
282
|
};
|
|
281
|
-
return /* @__PURE__ */ g(
|
|
282
|
-
|
|
283
|
+
return /* @__PURE__ */ g(_.Provider, { value: u, children: /* @__PURE__ */ g(
|
|
284
|
+
z,
|
|
283
285
|
{
|
|
284
286
|
fallback: o,
|
|
285
287
|
errorTitleString: k,
|
|
@@ -317,7 +319,7 @@ function Y({
|
|
|
317
319
|
), console.groupEnd()), m && l({ name: "prerender-status-code", content: "500" }), document.title = k, o;
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
|
-
const
|
|
322
|
+
const ee = t.exact({
|
|
321
323
|
path: (i, c, a) => {
|
|
322
324
|
const o = i[c];
|
|
323
325
|
return o == null ? new Error(
|
|
@@ -334,8 +336,8 @@ const Z = t.exact({
|
|
|
334
336
|
tags: t.arrayOf(t.object),
|
|
335
337
|
imageUrl: t.string
|
|
336
338
|
});
|
|
337
|
-
|
|
338
|
-
routes: t.arrayOf(
|
|
339
|
+
N.propTypes = {
|
|
340
|
+
routes: t.arrayOf(ee),
|
|
339
341
|
title: t.string,
|
|
340
342
|
separator: t.string,
|
|
341
343
|
notFoundTemplate: t.element,
|
|
@@ -363,9 +365,9 @@ Y.propTypes = {
|
|
|
363
365
|
debug: t.bool
|
|
364
366
|
};
|
|
365
367
|
export {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
368
|
+
z as ErrorBoundary,
|
|
369
|
+
N as Routerino,
|
|
370
|
+
N as default,
|
|
369
371
|
l as updateHeadTag,
|
|
370
|
-
|
|
372
|
+
ne as useRouterino
|
|
371
373
|
};
|
package/dist/routerino.umd.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(g,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("react/jsx-runtime"),require("react"),require("prop-types")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","prop-types"],a):(g=typeof globalThis<"u"?globalThis:g||self,a(g.routerino={},g["react/jsx-runtime"],g.React,g.PropTypes))})(this,function(g,a,$,t){"use strict";const q=$.createContext(null);function
|
|
1
|
+
(function(g,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("react/jsx-runtime"),require("react"),require("prop-types")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","prop-types"],a):(g=typeof globalThis<"u"?globalThis:g||self,a(g.routerino={},g["react/jsx-runtime"],g.React,g.PropTypes))})(this,function(g,a,$,t){"use strict";const q=$.createContext(null);function J(){const c=$.useContext(q);if(!c)throw new Error("useRouterino must be used within a Routerino router. Make sure your component is rendered inside a <Routerino> component.");return c}function i({tag:c="meta",soft:l=!1,...s}){const o=Object.keys(s);if(o.length<1)return console.error(`[Routerino] updateHeadTag() received no attributes to set for ${c} tag`);let p=null;for(let h=0;h<o.length&&(o[h]!=="content"&&(p=document.querySelector(`${c}[${o[h]}='${s[o[h]]}']`)),!p);h++);p&&l||(p||(p=document.createElement(c)),o.forEach(h=>p.setAttribute(h,s[h])),document.querySelector("head").appendChild(p))}function Q({routePattern:c,currentRoute:l}){let s={},o=c.split("/"),p=l.split("/");return o.forEach((h,b)=>{h.startsWith(":")&&(s[h.slice(1)]=p[b])}),s}class E extends $.Component{constructor(l){super(l),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(l,s){this.props.debug&&(console.group("%c[Routerino]%c Error Boundary Caught an Error","color: #ff6b6b; font-weight: bold","",l),console.error("[Routerino] Component Stack:",s.componentStack),this.props.routePath&&console.error("[Routerino] Failed Route:",this.props.routePath),console.error("[Routerino] Error occurred at:",new Date().toISOString()),console.groupEnd()),document.title=this.props.errorTitleString,this.props.usePrerenderTags&&i({name:"prerender-status-code",content:"500"})}render(){return this.state.hasError?this.props.fallback:this.props.children}}E.propTypes={children:t.node,fallback:t.node,errorTitleString:t.string.isRequired,usePrerenderTags:t.bool,routePath:t.string,debug:t.bool};function S({routes:c=[{path:"/",element:a.jsx("p",{children:"This is the default route. Pass an array of routes to the Routerino component in order to configure your own pages. Each route is a dictionary with at least `path` and `element` defined."}),title:"Routerino default route example",description:"The default route example description.",tags:[{property:"og:locale",content:"en_US"}]}],notFoundTemplate:l=a.jsxs(a.Fragment,{children:[a.jsx("p",{children:"No page found for this URL. [404]"}),a.jsx("p",{children:a.jsx("a",{href:"/",children:"Home"})})]}),notFoundTitle:s="Page not found [404]",errorTemplate:o=a.jsxs(a.Fragment,{children:[a.jsx("p",{children:"Page failed to load. [500]"}),a.jsx("p",{children:a.jsx("a",{href:"/",children:"Home"})})]}),errorTitle:p="Page error [500]",useTrailingSlash:h=!0,usePrerenderTags:b=!1,baseUrl:X=null,title:v="",separator:y=" | ",imageUrl:C=null,touchIconUrl:B=null,debug:u=!1}){var F,H,j,A,I,M,O;const L=`${p}${y}${v}`,W=`${s}${y}${v}`;try{if(u){const e=c.map(m=>m.path),d=e.filter((m,f)=>e.indexOf(m)!==f);d.length>0&&(console.warn("%c[Routerino]%c Duplicate route paths detected:","color: #f59e0b; font-weight: bold","",[...new Set(d)]),console.warn("%c[Routerino]%c The first matching route will be used","color: #f59e0b; font-weight: bold",""))}const[k,D]=$.useState(((F=window==null?void 0:window.location)==null?void 0:F.href)??"/");$.useEffect(()=>{if(typeof window>"u"||typeof document>"u")return;const e=m=>{u&&console.debug("%c[Routerino]%c click occurred","color: #6b7280; font-weight: bold","");let f=m.target;for(;f.tagName!=="A"&&f.parentElement;)f=f.parentElement;if(f.tagName!=="A"){u&&console.debug("%c[Routerino]%c no anchor tag found during click","color: #6b7280; font-weight: bold","");return}const w=f.getAttribute("href")||f.href;if(!w){u&&console.debug("%c[Routerino]%c anchor tag has no href","color: #6b7280; font-weight: bold","");return}if(!/^(https?:\/\/|\/|\.\/|\.\.\/|[^:]+$)/i.test(w)){u&&console.debug("%c[Routerino]%c skipping non-http URL:","color: #6b7280; font-weight: bold","",w);return}u&&console.debug("%c[Routerino]%c click target href:","color: #6b7280; font-weight: bold","",w);let R;try{R=new URL(w,window.location.href)}catch(U){u&&console.debug("%c[Routerino]%c Invalid URL:","color: #6b7280; font-weight: bold","",w,U);return}u&&console.debug("%c[Routerino]%c targetUrl:","color: #6b7280; font-weight: bold","",R,"current:",window.location),R&&window.location.origin===R.origin?(u&&console.debug("%c[Routerino]%c target link is same origin, will use push-state transitioning","color: #6b7280; font-weight: bold",""),m.preventDefault(),f.href!==window.location.href&&(D(f.href),window.history.pushState({},"",f.href)),window.scrollTo({top:0,behavior:"auto"})):u&&console.debug("%c[Routerino]%c target link does not share an origin, standard browser link handling applies","color: #6b7280; font-weight: bold","")};document.addEventListener("click",e);const d=()=>{u&&console.debug("%c[Routerino]%c route change ->","color: #6b7280; font-weight: bold","",window.location.pathname),D(window.location.href)};return window.addEventListener("popstate",d),()=>{document.removeEventListener("click",e),window.removeEventListener("popstate",d)}},[k]);let r=((H=window==null?void 0:window.location)==null?void 0:H.pathname)??"/";(r==="/index.html"||r==="")&&(r="/");const _=c.find(e=>e.path===r),z=c.find(e=>`${e.path}/`===r||e.path===`${r}/`),T=c.find(e=>{const d=e.path.endsWith("/")?e.path.slice(0,-1):e.path,m=r.endsWith("/")?r.slice(0,-1):r,f=d.split("/").filter(Boolean),w=m.split("/").filter(Boolean);return f.length!==w.length?!1:f.every((R,U)=>R.startsWith(":")?!0:R===w[U])}),n=_??z??T;if(u&&console.debug("%c[Routerino]%c Route matching:","color: #6b7280; font-weight: bold","",{match:n,exactMatch:_,addSlashMatch:z,paramsMatch:T}),!n)return u&&(console.group("%c[Routerino]%c 404 - No matching route","color: #f59e0b; font-weight: bold",""),console.warn("%c[Routerino]%c Requested path:","color: #f59e0b; font-weight: bold","",r),console.warn("%c[Routerino]%c Available routes:","color: #f59e0b; font-weight: bold","",c.map(e=>e.path)),console.groupEnd()),document.title=W,b&&i({name:"prerender-status-code",content:"404"}),l;if(b){const e=document.querySelector('meta[name="prerender-status-code"]');e&&e.remove();const d=document.querySelector('meta[name="prerender-header"]');d&&d.remove()}const G=h&&!r.endsWith("/")&&r!=="/",K=!h&&r.endsWith("/")&&r!=="/",Y=G?`${r}/`:K?r.slice(0,-1):r,x=`${X??((j=window==null?void 0:window.location)==null?void 0:j.origin)??""}${Y}`;if(n.title){const e=`${n.title}${y}${v}`;document.title=e,i({tag:"link",rel:"canonical",href:x}),(A=n.tags)!=null&&A.find(({property:d})=>d==="og:title")||i({property:"og:title",content:e}),(I=n.tags)!=null&&I.find(({property:d})=>d==="og:url")||i({property:"og:url",content:x})}if(n.description&&(i({name:"description",content:n.description}),(M=n.tags)!=null&&M.find(({property:e})=>e==="og:description")||i({property:"og:description",content:n.description})),(C||n.imageUrl)&&i({property:"og:image",content:n.imageUrl??C}),(O=n.tags)!=null&&O.find(({property:e})=>e==="twitter:card")||i({name:"twitter:card",content:"summary_large_image"}),B&&i({tag:"link",rel:"apple-touch-icon",href:B}),b&&(G||K)&&(i({name:"prerender-status-code",content:"301"}),i({name:"prerender-header",content:`Location: ${x}`})),n.tags&&n.tags.length?(n.tags.find(({property:e})=>e==="og:type")||i({property:"og:type",content:"website"}),n.tags.forEach(e=>i(e))):i({property:"og:type",content:"website"}),n.element){const e=Q({routePattern:n.path,currentRoute:r}),d={currentRoute:r,params:e,routePattern:n.path,updateHeadTag:i};return a.jsx(q.Provider,{value:d,children:a.jsx(E,{fallback:o,errorTitleString:L,usePrerenderTags:b,routePath:r,debug:u,children:n.element})})}return u&&console.error("%c[Routerino]%c No route found for","color: #ff6b6b; font-weight: bold","",r),document.title=W,b&&i({name:"prerender-status-code",content:"404"}),l}catch(k){return u&&(console.group("%c[Routerino]%c Fatal Error","color: #ff6b6b; font-weight: bold",""),console.error("%c[Routerino]%c An error occurred in the router itself (not in a route component)","color: #ff6b6b; font-weight: bold",""),console.error("%c[Routerino]%c Error:","color: #ff6b6b; font-weight: bold","",k),console.error("%c[Routerino]%c This typically means an issue with route configuration or router setup","color: #ff6b6b; font-weight: bold",""),console.groupEnd()),b&&i({name:"prerender-status-code",content:"500"}),document.title=L,o}}const V=t.exact({path:(c,l,s)=>{const o=c[l];return o==null?new Error(`The prop \`${l}\` is marked as required in \`${s}\`, but its value is \`${o}\`.`):typeof o!="string"?new Error(`Invalid prop \`${l}\` of type \`${typeof o}\` supplied to \`${s}\`, expected \`string\`.`):o.startsWith("/")?null:new Error(`Invalid prop \`${l}\` value \`${o}\` supplied to \`${s}\`. Route paths must start with a forward slash (/).`)},element:t.element.isRequired,title:t.string,description:t.string,tags:t.arrayOf(t.object),imageUrl:t.string});S.propTypes={routes:t.arrayOf(V),title:t.string,separator:t.string,notFoundTemplate:t.element,notFoundTitle:t.string,errorTemplate:t.element,errorTitle:t.string,useTrailingSlash:t.bool,usePrerenderTags:t.bool,baseUrl:(c,l,s)=>{const o=c[l];if(o!=null){if(typeof o!="string")return new Error(`Invalid prop \`${l}\` of type \`${typeof o}\` supplied to \`${s}\`, expected \`string\`.`);if(o.endsWith("/"))return new Error(`Invalid prop \`${l}\` supplied to \`${s}\`. The baseUrl should not end with a slash. Got: "${o}"`)}return null},imageUrl:t.string,touchIconUrl:t.string,debug:t.bool},g.ErrorBoundary=E,g.Routerino=S,g.default=S,g.updateHeadTag=i,g.useRouterino=J,Object.defineProperties(g,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/package.json
CHANGED
package/routerino-forge.js
CHANGED
|
@@ -474,7 +474,9 @@ const routes = routesModule.routes || routesModule.default;
|
|
|
474
474
|
const notFoundTemplate = routesModule.notFoundTemplate;
|
|
475
475
|
|
|
476
476
|
// Check if App component is exported from routes file
|
|
477
|
-
|
|
477
|
+
// App can be: named export, default export, or App property on default export
|
|
478
|
+
const App = routesModule.App ||
|
|
479
|
+
(typeof routesModule.default === 'function' ? routesModule.default : routesModule.default?.App);
|
|
478
480
|
|
|
479
481
|
if (!routes) {
|
|
480
482
|
throw new Error('Could not find routes export. Expected "export const routes" or "export default" from ${relativePath}');
|
|
@@ -508,37 +510,65 @@ function mockWindow(url, baseUrl) {
|
|
|
508
510
|
removeEventListener: () => {},
|
|
509
511
|
dispatchEvent: () => {}
|
|
510
512
|
};
|
|
513
|
+
// Mock for document with more complete implementation
|
|
514
|
+
const mockElements = [];
|
|
511
515
|
global.document = {
|
|
516
|
+
title: '', // Mock title property for SSG
|
|
512
517
|
addEventListener: () => {},
|
|
513
518
|
removeEventListener: () => {},
|
|
514
|
-
querySelector: () =>
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
+
querySelector: (selector) => {
|
|
520
|
+
// Return mock head for head selector
|
|
521
|
+
if (selector === 'head') {
|
|
522
|
+
return {
|
|
523
|
+
appendChild: (elem) => {
|
|
524
|
+
mockElements.push(elem);
|
|
525
|
+
return elem;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
// For meta tag queries, return null (tag not found)
|
|
530
|
+
return null;
|
|
531
|
+
},
|
|
532
|
+
createElement: (tagName) => {
|
|
533
|
+
const elem = {
|
|
534
|
+
tagName,
|
|
535
|
+
attributes: {},
|
|
536
|
+
setAttribute: function(name, value) {
|
|
537
|
+
this.attributes[name] = value;
|
|
538
|
+
},
|
|
539
|
+
appendChild: () => {}
|
|
540
|
+
};
|
|
541
|
+
return elem;
|
|
542
|
+
},
|
|
519
543
|
head: {
|
|
520
|
-
appendChild: () => {
|
|
544
|
+
appendChild: (elem) => {
|
|
545
|
+
mockElements.push(elem);
|
|
546
|
+
return elem;
|
|
547
|
+
},
|
|
548
|
+
querySelector: () => null,
|
|
549
|
+
querySelectorAll: () => []
|
|
521
550
|
}
|
|
522
551
|
};
|
|
523
552
|
}
|
|
524
553
|
|
|
525
554
|
export function render(url, baseUrl) {
|
|
526
|
-
// Check if we should render the full App or just the route element
|
|
555
|
+
// Check if we should render the full App or just the route element
|
|
527
556
|
if (App) {
|
|
557
|
+
// Find the route to render
|
|
558
|
+
const route = routes.find(r => {
|
|
559
|
+
if (r.path === url) return true;
|
|
560
|
+
if (r.path === '/' && url === '/') return true;
|
|
561
|
+
if (isDynamicRoute(r.path)) return false;
|
|
562
|
+
return r.path === url;
|
|
563
|
+
});
|
|
564
|
+
|
|
528
565
|
// Mock window for the current route
|
|
529
566
|
mockWindow(url, baseUrl);
|
|
530
567
|
|
|
531
568
|
try {
|
|
532
|
-
// Render the full App component (which includes Routerino)
|
|
533
|
-
const html = ReactDOMServer.renderToString(React.createElement(App));
|
|
534
569
|
|
|
535
|
-
//
|
|
536
|
-
const
|
|
537
|
-
if (r.path === url) return true;
|
|
538
|
-
if (r.path === '/' && url === '/') return true;
|
|
539
|
-
if (isDynamicRoute(r.path)) return false;
|
|
540
|
-
return r.path === url;
|
|
541
|
-
});
|
|
570
|
+
// Render the App with Routerino SSG-aware
|
|
571
|
+
const html = ReactDOMServer.renderToString(React.createElement(App));
|
|
542
572
|
|
|
543
573
|
return {
|
|
544
574
|
html,
|
|
@@ -549,6 +579,7 @@ export function render(url, baseUrl) {
|
|
|
549
579
|
};
|
|
550
580
|
} catch (error) {
|
|
551
581
|
console.error(\`[Routerino Forge] Failed to render App for route \${url}:\`, error.message);
|
|
582
|
+
console.error(\`[Routerino Forge] Stack trace:\`, error.stack);
|
|
552
583
|
// Fall back to route-only rendering
|
|
553
584
|
} finally {
|
|
554
585
|
// Clean up global mocks
|
|
@@ -558,12 +589,17 @@ export function render(url, baseUrl) {
|
|
|
558
589
|
}
|
|
559
590
|
|
|
560
591
|
// Original behavior: render just the route element
|
|
561
|
-
|
|
592
|
+
// Need to find the route again if App path wasn't taken
|
|
593
|
+
const route = App ? null : routes.find(r => {
|
|
562
594
|
if (r.path === url) return true;
|
|
563
595
|
if (r.path === '/' && url === '/') return true;
|
|
564
596
|
if (isDynamicRoute(r.path)) return false;
|
|
565
597
|
return r.path === url;
|
|
566
|
-
});
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// If we get here and App was defined, it means the App render failed
|
|
601
|
+
// Return early to avoid duplicate rendering
|
|
602
|
+
if (App) return;
|
|
567
603
|
|
|
568
604
|
if (!route) {
|
|
569
605
|
if (notFoundTemplate) {
|