@ilha/router 0.4.1 → 0.4.3
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/README.md +17 -0
- package/dist/{index-CFBKeDvv.d.ts → index-CJT0h4u4.d.ts} +20 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{plugin-DAk44om0.js → plugin-BMk5Ey9X.js} +5 -4
- package/dist/{plugin-CnS_mBUE.d.ts → plugin-Diq1AxSa.d.ts} +9 -0
- package/dist/rolldown.d.ts +2 -2
- package/dist/rolldown.js +2 -2
- package/dist/rspack.d.ts +2 -2
- package/dist/rspack.js +2 -2
- package/dist/{src-0cb8FYdn.js → src-BqAWh426.js} +27 -8
- package/dist/vite.d.ts +2 -2
- package/dist/vite.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -666,6 +666,11 @@ interface HydrateOptions {
|
|
|
666
666
|
}
|
|
667
667
|
|
|
668
668
|
type HistoryMode = "history" | "hash";
|
|
669
|
+
type RouterMode = "spa" | "mpa";
|
|
670
|
+
|
|
671
|
+
interface RouterOptions {
|
|
672
|
+
mode?: RouterMode; // default: "spa"
|
|
673
|
+
}
|
|
669
674
|
|
|
670
675
|
// Helper — returns fn as-is with LayoutHandler type enforced
|
|
671
676
|
function defineLayout(fn: LayoutHandler): LayoutHandler;
|
|
@@ -927,6 +932,18 @@ pageRouter.hydrate(registry);
|
|
|
927
932
|
pages({
|
|
928
933
|
dir: "src/pages", // pages directory (default: "src/pages")
|
|
929
934
|
generated: ".ilha/routes.ts", // generated file output (default: ".ilha/routes.ts")
|
|
935
|
+
mode: "spa", // "spa" | "mpa" (default: "spa")
|
|
936
|
+
});
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
Use `mode: "mpa"` when you want filesystem-routed pages to behave like a multi-page app: the current page can still be SSR-rendered and hydrated, but in-app links are not intercepted by the router, so navigation is handled by the browser as a full document request.
|
|
940
|
+
|
|
941
|
+
```ts
|
|
942
|
+
// vite.config.ts
|
|
943
|
+
import { pages } from "@ilha/router/vite";
|
|
944
|
+
|
|
945
|
+
export default defineConfig({
|
|
946
|
+
plugins: [pages({ mode: "mpa" })],
|
|
930
947
|
});
|
|
931
948
|
```
|
|
932
949
|
|
|
@@ -24,6 +24,8 @@ interface RouteRecord {
|
|
|
24
24
|
island: Island<any, any>;
|
|
25
25
|
/** Merged loader chain (layouts outer→inner, then page) — `undefined` if no loaders. */
|
|
26
26
|
loader?: Loader<any>;
|
|
27
|
+
/** True when the route has a server-side loader, even if the client only has a marker. */
|
|
28
|
+
hasLoader?: boolean;
|
|
27
29
|
}
|
|
28
30
|
interface RouteSnapshot {
|
|
29
31
|
path: string;
|
|
@@ -90,6 +92,16 @@ declare function defineLayout(layout: LayoutHandler): LayoutHandler;
|
|
|
90
92
|
interface NavigateOptions {
|
|
91
93
|
replace?: boolean;
|
|
92
94
|
}
|
|
95
|
+
type RouterMode = "spa" | "mpa";
|
|
96
|
+
interface RouterOptions {
|
|
97
|
+
/**
|
|
98
|
+
* Client navigation mode. `spa` intercepts in-app links and renders routes
|
|
99
|
+
* in-place. `mpa` leaves links to the browser, so each page navigation is a
|
|
100
|
+
* full document request while still allowing hydration of the current route.
|
|
101
|
+
* Default: `spa`.
|
|
102
|
+
*/
|
|
103
|
+
mode?: RouterMode;
|
|
104
|
+
}
|
|
93
105
|
interface HydratableRenderOptions extends Partial<Omit<HydratableOptions, "name">> {}
|
|
94
106
|
interface HydrateOptions {
|
|
95
107
|
root?: Element;
|
|
@@ -128,6 +140,12 @@ interface RouterBuilder {
|
|
|
128
140
|
* was never registered via `.route()`.
|
|
129
141
|
*/
|
|
130
142
|
attachLoader(pattern: string, loader: Loader<any>): RouterBuilder;
|
|
143
|
+
/**
|
|
144
|
+
* Mark an already-registered route as having a server-side loader without
|
|
145
|
+
* importing that loader into the client bundle. Used by FS-routing codegen
|
|
146
|
+
* so SPA navigation knows to call the loader endpoint.
|
|
147
|
+
*/
|
|
148
|
+
markLoader(pattern: string): RouterBuilder;
|
|
131
149
|
/**
|
|
132
150
|
* Return a snapshot of every registered route in match order. Useful for
|
|
133
151
|
* prerenderers that need to discover the filesystem routes exposed by
|
|
@@ -234,7 +252,7 @@ declare function enableLinkInterception(root?: Element | Document, options?: Lin
|
|
|
234
252
|
declare const RouterView: Island<Record<string, unknown>, Record<never, never>>;
|
|
235
253
|
declare const RouterLink: Island<Record<string, unknown>, Omit<Omit<Record<never, never>, K> & Record<"href", string>, "label"> & Record<"label", string>>;
|
|
236
254
|
declare function isActive(pattern: string): boolean;
|
|
237
|
-
declare function router(): RouterBuilder;
|
|
255
|
+
declare function router(options?: RouterOptions): RouterBuilder;
|
|
238
256
|
declare const _default: {
|
|
239
257
|
router: typeof router;
|
|
240
258
|
navigate: typeof navigate;
|
|
@@ -251,4 +269,4 @@ declare const _default: {
|
|
|
251
269
|
composeLoaders: typeof composeLoaders;
|
|
252
270
|
};
|
|
253
271
|
//#endregion
|
|
254
|
-
export {
|
|
272
|
+
export { loader as A, useRoute as B, RouterView as C, enableLinkInterception as D, defineLayout as E, routeHash as F, setHistoryMode as G, wrapLayout as H, routeParams as I, routePath as L, prefetch as M, prime as N, error as O, redirect as P, routeSearch as R, RouterOptions as S, composeLoaders as T, HistoryMode as U, wrapError as V, getHistoryMode as W, RouteRecord as _, InferLoader as a, RouterLink as b, LinkInterceptionOptions as c, LoaderError as d, MergeLoaders as f, RenderResponse as g, Redirect as h, HydrateOptions as i, navigate as j, isActive as k, Loader as l, NavigateOptions as m, ErrorHandler as n, LOADER_ENDPOINT as o, MountOptions as p, HydratableRenderOptions as r, LayoutHandler as s, AppError as t, LoaderContext as u, RouteSnapshot as v, _default as w, RouterMode as x, RouterBuilder as y, router as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { AppError, ErrorHandler, HistoryMode, HydratableRenderOptions, HydrateOptions, InferLoader, LOADER_ENDPOINT, LayoutHandler, LinkInterceptionOptions, Loader, LoaderContext, LoaderError, MergeLoaders, MountOptions, NavigateOptions, Redirect, RenderResponse, RouteRecord, RouteSnapshot, RouterBuilder, RouterLink, RouterView, composeLoaders, _default as default, defineLayout, enableLinkInterception, error, getHistoryMode, isActive, loader, navigate, prefetch, prime, redirect, routeHash, routeParams, routePath, routeSearch, router, setHistoryMode, useRoute, wrapError, wrapLayout };
|
|
1
|
+
import { A as loader, B as useRoute, C as RouterView, D as enableLinkInterception, E as defineLayout, F as routeHash, G as setHistoryMode, H as wrapLayout, I as routeParams, L as routePath, M as prefetch, N as prime, O as error, P as redirect, R as routeSearch, S as RouterOptions, T as composeLoaders, U as HistoryMode, V as wrapError, W as getHistoryMode, _ as RouteRecord, a as InferLoader, b as RouterLink, c as LinkInterceptionOptions, d as LoaderError, f as MergeLoaders, g as RenderResponse, h as Redirect, i as HydrateOptions, j as navigate, k as isActive, l as Loader, m as NavigateOptions, n as ErrorHandler, o as LOADER_ENDPOINT, p as MountOptions, r as HydratableRenderOptions, s as LayoutHandler, t as AppError, u as LoaderContext, v as RouteSnapshot, w as _default, x as RouterMode, y as RouterBuilder, z as router } from "./index-CJT0h4u4.js";
|
|
2
|
+
export { AppError, ErrorHandler, HistoryMode, HydratableRenderOptions, HydrateOptions, InferLoader, LOADER_ENDPOINT, LayoutHandler, LinkInterceptionOptions, Loader, LoaderContext, LoaderError, MergeLoaders, MountOptions, NavigateOptions, Redirect, RenderResponse, RouteRecord, RouteSnapshot, RouterBuilder, RouterLink, RouterMode, RouterOptions, RouterView, composeLoaders, _default as default, defineLayout, enableLinkInterception, error, getHistoryMode, isActive, loader, navigate, prefetch, prime, redirect, routeHash, routeParams, routePath, routeSearch, router, setHistoryMode, useRoute, wrapError, wrapLayout };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as wrapError, E as setHistoryMode, S as useRoute, T as getHistoryMode, _ as routeParams, a as RouterView, b as router, c as enableLinkInterception, d as loader, f as navigate, g as routeHash, h as redirect, i as RouterLink, l as error, m as prime, n as LoaderError, o as composeLoaders, p as prefetch, r as Redirect, s as defineLayout, t as LOADER_ENDPOINT, u as isActive, v as routePath, w as wrapLayout, x as src_default, y as routeSearch } from "./src-
|
|
1
|
+
import { C as wrapError, E as setHistoryMode, S as useRoute, T as getHistoryMode, _ as routeParams, a as RouterView, b as router, c as enableLinkInterception, d as loader, f as navigate, g as routeHash, h as redirect, i as RouterLink, l as error, m as prime, n as LoaderError, o as composeLoaders, p as prefetch, r as Redirect, s as defineLayout, t as LOADER_ENDPOINT, u as isActive, v as routePath, w as wrapLayout, x as src_default, y as routeSearch } from "./src-BqAWh426.js";
|
|
2
2
|
export { LOADER_ENDPOINT, LoaderError, Redirect, RouterLink, RouterView, composeLoaders, src_default as default, defineLayout, enableLinkInterception, error, getHistoryMode, isActive, loader, navigate, prefetch, prime, redirect, routeHash, routeParams, routePath, routeSearch, router, setHistoryMode, useRoute, wrapError, wrapLayout };
|
|
@@ -138,7 +138,8 @@ function validateEntries(entries, pagesDir) {
|
|
|
138
138
|
else seenNames.set(entry.name, entry.file);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
-
async function generate(pagesDir, outFile) {
|
|
141
|
+
async function generate(pagesDir, outFile, options = {}) {
|
|
142
|
+
const mode = options.mode ?? "spa";
|
|
142
143
|
const entries = sortEntries(await scanPages(pagesDir));
|
|
143
144
|
validateEntries(entries, pagesDir);
|
|
144
145
|
const rel = (abs) => {
|
|
@@ -161,7 +162,7 @@ async function generate(pagesDir, outFile) {
|
|
|
161
162
|
const wrappedId = `_wrapped${i}`;
|
|
162
163
|
wrappedIslandLines.push(`const ${wrappedId} = ${expr};`);
|
|
163
164
|
registryLines.push(` ${JSON.stringify(entry.name)}: ${wrappedId}` + (i < entries.length - 1 ? "," : ""));
|
|
164
|
-
routeLines.push(` .route(${JSON.stringify(entry.pattern)}, ${wrappedId})`);
|
|
165
|
+
routeLines.push(` .route(${JSON.stringify(entry.pattern)}, ${wrappedId})` + (entry.hasLoader || entry.loaderLayouts.length > 0 ? `.markLoader(${JSON.stringify(entry.pattern)})` : ""));
|
|
165
166
|
}
|
|
166
167
|
const code = [
|
|
167
168
|
`// @generated by @ilha/router — do not edit`,
|
|
@@ -174,7 +175,7 @@ async function generate(pagesDir, outFile) {
|
|
|
174
175
|
...registryLines,
|
|
175
176
|
`};`,
|
|
176
177
|
``,
|
|
177
|
-
`export const pageRouter = router()`,
|
|
178
|
+
`export const pageRouter = router(${mode === "mpa" ? `{ mode: "mpa" }` : ""})`,
|
|
178
179
|
...routeLines,
|
|
179
180
|
` ;`
|
|
180
181
|
].join("\n");
|
|
@@ -285,7 +286,7 @@ function createPagesPluginState(options) {
|
|
|
285
286
|
};
|
|
286
287
|
const regen = async () => {
|
|
287
288
|
try {
|
|
288
|
-
await generate(pagesDir, outFile);
|
|
289
|
+
await generate(pagesDir, outFile, { mode: options.mode });
|
|
289
290
|
} catch (e) {
|
|
290
291
|
console.error("[ilha:pages] codegen failed:", e);
|
|
291
292
|
}
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import * as _$unplugin from "unplugin";
|
|
2
2
|
|
|
3
|
+
//#region src/codegen.d.ts
|
|
4
|
+
type PagesMode = "spa" | "mpa";
|
|
5
|
+
//#endregion
|
|
3
6
|
//#region src/plugin.d.ts
|
|
4
7
|
interface IlhaPagesOptions {
|
|
5
8
|
/** Directory containing page files. Default: `src/pages` */
|
|
6
9
|
dir?: string;
|
|
7
10
|
/** Output path for the generated routes + registry file. Default: `.ilha/routes.ts` */
|
|
8
11
|
generated?: string;
|
|
12
|
+
/**
|
|
13
|
+
* File-system router navigation mode. `spa` intercepts in-app links and
|
|
14
|
+
* renders routes client-side; `mpa` leaves links to the browser for full
|
|
15
|
+
* document navigations. Default: `spa`.
|
|
16
|
+
*/
|
|
17
|
+
mode?: PagesMode;
|
|
9
18
|
}
|
|
10
19
|
declare const ilhaPages: _$unplugin.UnpluginInstance<IlhaPagesOptions | undefined, boolean>;
|
|
11
20
|
//#endregion
|
package/dist/rolldown.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as ilhaPages, t as IlhaPagesOptions } from "./plugin-
|
|
1
|
+
import { H as wrapLayout, V as wrapError, n as ErrorHandler, s as LayoutHandler, t as AppError, v as RouteSnapshot } from "./index-CJT0h4u4.js";
|
|
2
|
+
import { n as ilhaPages, t as IlhaPagesOptions } from "./plugin-Diq1AxSa.js";
|
|
3
3
|
import * as _$unplugin from "unplugin";
|
|
4
4
|
|
|
5
5
|
//#region src/rolldown.d.ts
|
package/dist/rolldown.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as wrapError, w as wrapLayout } from "./src-
|
|
2
|
-
import { t as ilhaPages } from "./plugin-
|
|
1
|
+
import { C as wrapError, w as wrapLayout } from "./src-BqAWh426.js";
|
|
2
|
+
import { t as ilhaPages } from "./plugin-BMk5Ey9X.js";
|
|
3
3
|
//#region src/rolldown.ts
|
|
4
4
|
/** Rolldown plugin — use via `@ilha/router/rolldown`. */
|
|
5
5
|
function pages(options = {}) {
|
package/dist/rspack.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as ilhaPages, t as IlhaPagesOptions } from "./plugin-
|
|
1
|
+
import { H as wrapLayout, V as wrapError, n as ErrorHandler, s as LayoutHandler, t as AppError, v as RouteSnapshot } from "./index-CJT0h4u4.js";
|
|
2
|
+
import { n as ilhaPages, t as IlhaPagesOptions } from "./plugin-Diq1AxSa.js";
|
|
3
3
|
import * as _$unplugin from "unplugin";
|
|
4
4
|
|
|
5
5
|
//#region src/rspack.d.ts
|
package/dist/rspack.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as wrapError, w as wrapLayout } from "./src-
|
|
2
|
-
import { t as ilhaPages } from "./plugin-
|
|
1
|
+
import { C as wrapError, w as wrapLayout } from "./src-BqAWh426.js";
|
|
2
|
+
import { t as ilhaPages } from "./plugin-BMk5Ey9X.js";
|
|
3
3
|
//#region src/rspack.ts
|
|
4
4
|
/** Rspack plugin — use via `@ilha/router/rspack`. */
|
|
5
5
|
function pages(options = {}) {
|
|
@@ -349,7 +349,7 @@ function prefetch(pathWithSearch) {
|
|
|
349
349
|
if (!isBrowser) return;
|
|
350
350
|
if (prefetchCache.has(pathWithSearch)) return;
|
|
351
351
|
const pathOnly = pathWithSearch.split("?")[0] ?? "";
|
|
352
|
-
if (!findRoute(_rou3, "GET", pathOnly)?.data?.
|
|
352
|
+
if (!findRoute(_rou3, "GET", pathOnly)?.data?.hasLoader) return;
|
|
353
353
|
const promise = fetchLoaderData(pathWithSearch).catch((e) => {
|
|
354
354
|
return {
|
|
355
355
|
kind: "error",
|
|
@@ -369,7 +369,7 @@ async function mountRouteWithHydration(island, host, pathWithSearch, signal, reg
|
|
|
369
369
|
host.innerHTML = `<div data-router-empty></div>`;
|
|
370
370
|
return () => {};
|
|
371
371
|
}
|
|
372
|
-
const hasLoader = !!findRoute(_rou3, "GET", pathWithSearch.split("?")[0] ?? "")?.data?.
|
|
372
|
+
const hasLoader = !!findRoute(_rou3, "GET", pathWithSearch.split("?")[0] ?? "")?.data?.hasLoader;
|
|
373
373
|
let props = {};
|
|
374
374
|
const loaderResult = hasLoader ? await fetchLoaderData(pathWithSearch, signal) : {
|
|
375
375
|
kind: "data",
|
|
@@ -577,7 +577,8 @@ async function executeLoader(loader, url, params, request, signal) {
|
|
|
577
577
|
};
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
|
-
function router() {
|
|
580
|
+
function router(options = {}) {
|
|
581
|
+
const mode = options.mode ?? "spa";
|
|
581
582
|
_records = [];
|
|
582
583
|
_rou3 = createRouter();
|
|
583
584
|
_islandToPattern = /* @__PURE__ */ new Map();
|
|
@@ -586,14 +587,17 @@ function router() {
|
|
|
586
587
|
let _linkCleanup = null;
|
|
587
588
|
const builder = {
|
|
588
589
|
route(pattern, island, loader) {
|
|
590
|
+
const hasLoader = !!loader;
|
|
589
591
|
const data = {
|
|
590
592
|
island,
|
|
591
|
-
loader
|
|
593
|
+
loader,
|
|
594
|
+
hasLoader
|
|
592
595
|
};
|
|
593
596
|
_records.push({
|
|
594
597
|
pattern,
|
|
595
598
|
island,
|
|
596
|
-
loader
|
|
599
|
+
loader,
|
|
600
|
+
hasLoader
|
|
597
601
|
});
|
|
598
602
|
addRoute(_rou3, "GET", pattern, data);
|
|
599
603
|
_patternToData.set(pattern, data);
|
|
@@ -607,8 +611,23 @@ function router() {
|
|
|
607
611
|
return builder;
|
|
608
612
|
}
|
|
609
613
|
data.loader = loader;
|
|
614
|
+
data.hasLoader = true;
|
|
610
615
|
const rec = _records.find((r) => r.pattern === pattern);
|
|
611
|
-
if (rec)
|
|
616
|
+
if (rec) {
|
|
617
|
+
rec.loader = loader;
|
|
618
|
+
rec.hasLoader = true;
|
|
619
|
+
}
|
|
620
|
+
return builder;
|
|
621
|
+
},
|
|
622
|
+
markLoader(pattern) {
|
|
623
|
+
const data = _patternToData.get(pattern);
|
|
624
|
+
if (!data) {
|
|
625
|
+
console.warn(`[ilha-router] markLoader("${pattern}"): pattern was never registered via .route(). The loader marker will be ignored.`);
|
|
626
|
+
return builder;
|
|
627
|
+
}
|
|
628
|
+
data.hasLoader = true;
|
|
629
|
+
const rec = _records.find((r) => r.pattern === pattern);
|
|
630
|
+
if (rec) rec.hasLoader = true;
|
|
612
631
|
return builder;
|
|
613
632
|
},
|
|
614
633
|
routes() {
|
|
@@ -628,7 +647,7 @@ function router() {
|
|
|
628
647
|
syncRouteFromLocation();
|
|
629
648
|
const popHandler = () => syncRouteFromLocation();
|
|
630
649
|
_navChangeCleanup = getAdapter().onChange(popHandler);
|
|
631
|
-
_linkCleanup = enableLinkInterception(document);
|
|
650
|
+
_linkCleanup = mode === "spa" ? enableLinkInterception(document) : null;
|
|
632
651
|
let unmountView = null;
|
|
633
652
|
let navAbort = null;
|
|
634
653
|
if (hydrate) {
|
|
@@ -692,7 +711,7 @@ function router() {
|
|
|
692
711
|
const viewHost = host?.querySelector("[data-router-view]");
|
|
693
712
|
if (!viewHost) return;
|
|
694
713
|
const loc = getAdapter().readLocation();
|
|
695
|
-
const result = !!findRoute(_rou3, "GET", loc.pathname)?.data?.
|
|
714
|
+
const result = !!findRoute(_rou3, "GET", loc.pathname)?.data?.hasLoader ? await fetchLoaderData(loc.pathname + loc.search, signal) : {
|
|
696
715
|
kind: "data",
|
|
697
716
|
data: {}
|
|
698
717
|
};
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import {
|
|
3
|
-
import { n as ilhaPages, t as IlhaPagesOptions } from "./plugin-
|
|
2
|
+
import { H as wrapLayout, V as wrapError, n as ErrorHandler, s as LayoutHandler, t as AppError, v as RouteSnapshot } from "./index-CJT0h4u4.js";
|
|
3
|
+
import { n as ilhaPages, t as IlhaPagesOptions } from "./plugin-Diq1AxSa.js";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as http from "node:http";
|
|
6
6
|
import { Agent, ClientRequest, ClientRequestArgs, OutgoingHttpHeaders } from "node:http";
|
package/dist/vite.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as wrapError, w as wrapLayout } from "./src-
|
|
2
|
-
import { t as ilhaPages } from "./plugin-
|
|
1
|
+
import { C as wrapError, w as wrapLayout } from "./src-BqAWh426.js";
|
|
2
|
+
import { t as ilhaPages } from "./plugin-BMk5Ey9X.js";
|
|
3
3
|
//#region src/vite.ts
|
|
4
4
|
/** Vite plugin — use via `@ilha/router/vite`. */
|
|
5
5
|
function pages(options = {}) {
|