@ilha/router 0.6.7 → 0.6.8
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/codegen.d.ts +21 -0
- package/dist/hash.d.ts +44 -0
- package/dist/index.d.ts +341 -2
- package/dist/index.js +1 -1
- package/dist/plugin-DBB11WaX.js +6 -0
- package/dist/plugin.d.ts +56 -0
- package/dist/rolldown.d.ts +4 -8
- package/dist/rolldown.js +1 -1
- package/dist/rspack.d.ts +4 -7
- package/dist/rspack.js +1 -1
- package/dist/src-Cgv3m3sQ.js +4 -0
- package/dist/ssr.d.ts +93 -0
- package/dist/ssr.js +15 -0
- package/dist/vite.d.ts +5 -9952
- package/dist/vite.js +1 -1
- package/package.json +11 -4
- package/dist/index-DZA_1KrG.d.ts +0 -299
- package/dist/plugin-OpojKenS.js +0 -6
- package/dist/plugin-rauddckN.d.ts +0 -28
- package/dist/src-Cprpo919.js +0 -3
package/dist/vite.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{E as e,T as t}from"./src-Cgv3m3sQ.js";import{t as n}from"./plugin-DBB11WaX.js";function r(e={}){return n.vite(e)}export{n as ilhaPages,r as pages,t as wrapError,e as wrapLayout};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ilha/router",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "A tiny SPA router for Ilha",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"frontend",
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"types": "./dist/index.d.ts",
|
|
40
40
|
"import": "./dist/index.js"
|
|
41
41
|
},
|
|
42
|
+
"./ssr": {
|
|
43
|
+
"types": "./dist/ssr.d.ts",
|
|
44
|
+
"import": "./dist/ssr.js"
|
|
45
|
+
},
|
|
42
46
|
"./vite": {
|
|
43
47
|
"types": "./dist/vite.d.ts",
|
|
44
48
|
"import": "./dist/vite.js"
|
|
@@ -56,15 +60,18 @@
|
|
|
56
60
|
"access": "public"
|
|
57
61
|
},
|
|
58
62
|
"scripts": {
|
|
59
|
-
"build": "
|
|
63
|
+
"build": "tsdown && tsc -p tsconfig.build.json",
|
|
60
64
|
"test": "bun test"
|
|
61
65
|
},
|
|
62
66
|
"dependencies": {
|
|
63
|
-
"ilha": "0.8.4",
|
|
64
67
|
"rou3": "0.8.1",
|
|
65
68
|
"unplugin": "3.0.0"
|
|
66
69
|
},
|
|
67
70
|
"devDependencies": {
|
|
68
|
-
"
|
|
71
|
+
"ilha": "0.8.5",
|
|
72
|
+
"vite": "^8.1.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"ilha": ">=0.8.5"
|
|
69
76
|
}
|
|
70
77
|
}
|
package/dist/index-DZA_1KrG.d.ts
DELETED
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
import { HydratableOptions, Island } from "ilha";
|
|
2
|
-
|
|
3
|
-
//#region src/hash.d.ts
|
|
4
|
-
type HistoryMode = "history" | "hash";
|
|
5
|
-
/**
|
|
6
|
-
* Set the router's history mode. Call this once at app entry, before
|
|
7
|
-
* mounting any router. Defaults to "history" (HTML5 History API).
|
|
8
|
-
*
|
|
9
|
-
* Use "hash" when the document is loaded over file:// (Electron, Tauri, etc.)
|
|
10
|
-
* or any time there's no server able to serve a SPA fallback at arbitrary
|
|
11
|
-
* pathnames.
|
|
12
|
-
*
|
|
13
|
-
* Switching modes mid-session is supported but not common — listeners
|
|
14
|
-
* registered before the switch will keep using their original adapter
|
|
15
|
-
* until they're re-attached (typically by unmounting and remounting
|
|
16
|
-
* the router).
|
|
17
|
-
*/
|
|
18
|
-
declare function setHistoryMode(mode: HistoryMode): void;
|
|
19
|
-
declare function getHistoryMode(): HistoryMode;
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/index.d.ts
|
|
22
|
-
interface RouteRecord {
|
|
23
|
-
pattern: string;
|
|
24
|
-
island: Island<any, any>;
|
|
25
|
-
/** Merged loader chain (layouts outer→inner, then page) — `undefined` if no loaders. */
|
|
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;
|
|
29
|
-
}
|
|
30
|
-
interface RouteSnapshot {
|
|
31
|
-
path: string;
|
|
32
|
-
params: Record<string, string>;
|
|
33
|
-
search: string;
|
|
34
|
-
hash: string;
|
|
35
|
-
}
|
|
36
|
-
interface AppError {
|
|
37
|
-
message: string;
|
|
38
|
-
status?: number;
|
|
39
|
-
stack?: string;
|
|
40
|
-
}
|
|
41
|
-
type LayoutHandler = (children: Island<any, any>) => Island<any, any>;
|
|
42
|
-
type ErrorHandler = (error: AppError, route: RouteSnapshot) => Island<any, any>;
|
|
43
|
-
interface LoaderContext {
|
|
44
|
-
params: Record<string, string>;
|
|
45
|
-
request: Request;
|
|
46
|
-
url: URL;
|
|
47
|
-
signal: AbortSignal;
|
|
48
|
-
}
|
|
49
|
-
type Loader<T> = (ctx: LoaderContext) => Promise<T> | T;
|
|
50
|
-
/**
|
|
51
|
-
* Identity function for declaring a loader. Exists purely as a type anchor and
|
|
52
|
-
* a marker for the Vite plugin to detect by export name.
|
|
53
|
-
*/
|
|
54
|
-
declare function loader<T>(fn: Loader<T>): Loader<T>;
|
|
55
|
-
/** Extract the return type of a loader. */
|
|
56
|
-
type InferLoader<L> = L extends Loader<infer T> ? Awaited<T> : never;
|
|
57
|
-
/**
|
|
58
|
-
* Merge multiple loader return types into a single object type.
|
|
59
|
-
* Later loaders override earlier ones on key collision — matching runtime merge.
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* type PageInput = MergeLoaders<[typeof rootLayoutLoad, typeof sectionLayoutLoad, typeof pageLoad]>;
|
|
63
|
-
*/
|
|
64
|
-
type MergeLoaders<Ls extends readonly Loader<any>[]> = Ls extends readonly [infer First extends Loader<any>, ...infer Rest extends readonly Loader<any>[]] ? Rest extends readonly [] ? InferLoader<First> : Omit<InferLoader<First>, keyof MergeLoaders<Rest>> & MergeLoaders<Rest> : {};
|
|
65
|
-
declare class Redirect {
|
|
66
|
-
readonly __ilhaRedirect: true;
|
|
67
|
-
readonly to: string;
|
|
68
|
-
readonly status: number;
|
|
69
|
-
constructor(to: string, status?: number);
|
|
70
|
-
}
|
|
71
|
-
declare class LoaderError {
|
|
72
|
-
readonly __ilhaLoaderError: true;
|
|
73
|
-
readonly status: number;
|
|
74
|
-
readonly message: string;
|
|
75
|
-
constructor(status: number, message: string);
|
|
76
|
-
}
|
|
77
|
-
declare function redirect(to: string, status?: number): never;
|
|
78
|
-
declare function error(status: number, message: string): never;
|
|
79
|
-
/**
|
|
80
|
-
* Compose a list of loaders into a single loader. Later loaders win on key
|
|
81
|
-
* collision (page loader overrides layout loader for the same key). All loaders
|
|
82
|
-
* run concurrently within a chain since they share the same abort signal and
|
|
83
|
-
* request — re-fetching is cheap with a request-scoped cache (future work).
|
|
84
|
-
*
|
|
85
|
-
* For v1 we run them in parallel via `Promise.all`. If a loader throws a
|
|
86
|
-
* `Redirect` or `LoaderError`, the composed loader re-throws it unchanged.
|
|
87
|
-
*/
|
|
88
|
-
declare function composeLoaders<Ls extends readonly Loader<any>[]>(loaders: Ls): Loader<MergeLoaders<Ls>>;
|
|
89
|
-
declare function wrapLayout(layout: LayoutHandler, page: Island<any, any>): Island<any, any>;
|
|
90
|
-
declare function wrapError(handler: ErrorHandler, page: Island<any, any>): Island<any, any>;
|
|
91
|
-
declare function defineLayout(layout: LayoutHandler): LayoutHandler;
|
|
92
|
-
interface NavigateOptions {
|
|
93
|
-
replace?: boolean;
|
|
94
|
-
}
|
|
95
|
-
type RouterMode = "spa" | "static";
|
|
96
|
-
interface RouterOptions {
|
|
97
|
-
/**
|
|
98
|
-
* Client navigation mode.
|
|
99
|
-
* - `spa` — full route graph, SSR/hydration, client-side navigation.
|
|
100
|
-
* - `static` — no route graph bundled; hydrate islands on the current
|
|
101
|
-
* pre-rendered page only.
|
|
102
|
-
* Default: `spa`.
|
|
103
|
-
*/
|
|
104
|
-
mode?: RouterMode;
|
|
105
|
-
/**
|
|
106
|
-
* When `true` (default), internal `<a>` clicks are intercepted and handled
|
|
107
|
-
* by the client router. Set to `false` for MPA-style behavior where links
|
|
108
|
-
* perform full document navigations.
|
|
109
|
-
* Only meaningful in `spa` mode; ignored in `static` mode.
|
|
110
|
-
* Default: `true`.
|
|
111
|
-
*/
|
|
112
|
-
interceptLinks?: boolean;
|
|
113
|
-
}
|
|
114
|
-
interface HydratableRenderOptions extends Partial<Omit<HydratableOptions, "name">> {}
|
|
115
|
-
interface HydrateOptions {
|
|
116
|
-
root?: Element;
|
|
117
|
-
target?: string | Element;
|
|
118
|
-
/**
|
|
119
|
-
* When `true` (default), internal `<a>` clicks are intercepted for
|
|
120
|
-
* client-side navigation. Set to `false` for MPA-style full-page navigations.
|
|
121
|
-
*/
|
|
122
|
-
interceptLinks?: boolean;
|
|
123
|
-
}
|
|
124
|
-
interface MountOptions {
|
|
125
|
-
hydrate?: boolean;
|
|
126
|
-
registry?: Record<string, Island<any, any>>;
|
|
127
|
-
/**
|
|
128
|
-
* When `true` (default), internal `<a>` clicks are intercepted for
|
|
129
|
-
* client-side navigation. Set to `false` for MPA-style full-page navigations.
|
|
130
|
-
*/
|
|
131
|
-
interceptLinks?: boolean;
|
|
132
|
-
}
|
|
133
|
-
/** Response envelope returned by `renderResponse` — lets the host app handle redirects. */
|
|
134
|
-
type RenderResponse = {
|
|
135
|
-
kind: "html";
|
|
136
|
-
html: string;
|
|
137
|
-
status?: number;
|
|
138
|
-
} | {
|
|
139
|
-
kind: "redirect";
|
|
140
|
-
to: string;
|
|
141
|
-
status: number;
|
|
142
|
-
} | {
|
|
143
|
-
kind: "error";
|
|
144
|
-
status: number;
|
|
145
|
-
message: string;
|
|
146
|
-
html: string;
|
|
147
|
-
};
|
|
148
|
-
interface RouterBuilder {
|
|
149
|
-
/**
|
|
150
|
-
* Register a route. The optional `loader` is the merged loader chain
|
|
151
|
-
* (layout loaders outer→inner followed by the page loader) produced by
|
|
152
|
-
* the FS-routing codegen.
|
|
153
|
-
*/
|
|
154
|
-
route(pattern: string, island: Island<any, any>, loader?: Loader<any>): RouterBuilder;
|
|
155
|
-
/**
|
|
156
|
-
* Attach (or replace) a loader on an already-registered route pattern.
|
|
157
|
-
* Used by the `ilha:loaders` virtual module to wire server-only loaders
|
|
158
|
-
* onto the client-safe `pageRouter` at SSR time. No-op if the pattern
|
|
159
|
-
* was never registered via `.route()`.
|
|
160
|
-
*/
|
|
161
|
-
attachLoader(pattern: string, loader: Loader<any>): RouterBuilder;
|
|
162
|
-
/**
|
|
163
|
-
* Mark an already-registered route as having a server-side loader without
|
|
164
|
-
* importing that loader into the client bundle. Used by FS-routing codegen
|
|
165
|
-
* so SPA navigation knows to call the loader endpoint.
|
|
166
|
-
*/
|
|
167
|
-
markLoader(pattern: string): RouterBuilder;
|
|
168
|
-
/**
|
|
169
|
-
* Return a snapshot of every registered route in match order. Useful for
|
|
170
|
-
* prerenderers that need to discover the filesystem routes exposed by
|
|
171
|
-
* `pageRouter` without reaching into router internals.
|
|
172
|
-
*/
|
|
173
|
-
routes(): RouteRecord[];
|
|
174
|
-
prime(): void;
|
|
175
|
-
mount(target: string | Element, options?: MountOptions): () => void;
|
|
176
|
-
render(url: string | URL): string;
|
|
177
|
-
renderHydratable(url: string | URL, registry: Record<string, Island<any, any>>, options?: HydratableRenderOptions, request?: Request): Promise<string>;
|
|
178
|
-
/**
|
|
179
|
-
* Like `renderHydratable` but surfaces loader redirects and errors as
|
|
180
|
-
* structured responses instead of baking them into HTML. Prefer this from
|
|
181
|
-
* host server code so you can emit proper 302 / 4xx responses.
|
|
182
|
-
*/
|
|
183
|
-
renderResponse(url: string | URL, registry: Record<string, Island<any, any>>, options?: HydratableRenderOptions, request?: Request): Promise<RenderResponse>;
|
|
184
|
-
/**
|
|
185
|
-
* Run the loader chain for a given URL without rendering. Used by the
|
|
186
|
-
* `/__ilha/loader` endpoint the Vite plugin exposes for client-side
|
|
187
|
-
* navigation. Returns the raw loader result, a redirect sentinel, or an
|
|
188
|
-
* error sentinel.
|
|
189
|
-
*/
|
|
190
|
-
runLoader(url: string | URL, request?: Request): Promise<{
|
|
191
|
-
kind: "data";
|
|
192
|
-
data: Record<string, unknown>;
|
|
193
|
-
} | {
|
|
194
|
-
kind: "redirect";
|
|
195
|
-
to: string;
|
|
196
|
-
status: number;
|
|
197
|
-
} | {
|
|
198
|
-
kind: "error";
|
|
199
|
-
status: number;
|
|
200
|
-
message: string;
|
|
201
|
-
} | {
|
|
202
|
-
kind: "not-found";
|
|
203
|
-
}>;
|
|
204
|
-
/**
|
|
205
|
-
* Hydrate the application - combines prime(), mount(), and router.mount() into one call.
|
|
206
|
-
* @param registry - The island registry from ilha:registry
|
|
207
|
-
* @param options - Optional root element (defaults to document.body) and router target (defaults to root)
|
|
208
|
-
* @returns Cleanup function
|
|
209
|
-
*/
|
|
210
|
-
hydrate(registry: Record<string, Island<any, any>>, options?: HydrateOptions): () => void;
|
|
211
|
-
/**
|
|
212
|
-
* Hydrate islands on the current pre-rendered page without mounting a route
|
|
213
|
-
* view or enabling client navigation. Intended for `static` mode: each page
|
|
214
|
-
* is a self-contained HTML file; only interactive islands need activation.
|
|
215
|
-
*/
|
|
216
|
-
hydrateStatic(registry: Record<string, Island<any, any>>, options?: {
|
|
217
|
-
root?: Element;
|
|
218
|
-
}): () => void;
|
|
219
|
-
}
|
|
220
|
-
/** Path of the loader endpoint served by the Vite plugin / production adapter. */
|
|
221
|
-
declare const LOADER_ENDPOINT = "/__ilha/loader";
|
|
222
|
-
/**
|
|
223
|
-
* Prefetch loader data for a given path. Safe to call repeatedly — a single
|
|
224
|
-
* inflight request is reused until it either resolves (and is consumed by
|
|
225
|
-
* navigation) or is superseded by another prefetch.
|
|
226
|
-
*/
|
|
227
|
-
declare function prefetch(pathWithSearch: string): void;
|
|
228
|
-
declare const routePath: {
|
|
229
|
-
(): string;
|
|
230
|
-
(value: string): void;
|
|
231
|
-
};
|
|
232
|
-
declare const routeParams: {
|
|
233
|
-
(): Record<string, string>;
|
|
234
|
-
(value: Record<string, string>): void;
|
|
235
|
-
};
|
|
236
|
-
declare const routeSearch: {
|
|
237
|
-
(): string;
|
|
238
|
-
(value: string): void;
|
|
239
|
-
};
|
|
240
|
-
declare const routeHash: {
|
|
241
|
-
(): string;
|
|
242
|
-
(value: string): void;
|
|
243
|
-
};
|
|
244
|
-
declare function useRoute(): {
|
|
245
|
-
path: {
|
|
246
|
-
(): string;
|
|
247
|
-
(value: string): void;
|
|
248
|
-
};
|
|
249
|
-
params: {
|
|
250
|
-
(): Record<string, string>;
|
|
251
|
-
(value: Record<string, string>): void;
|
|
252
|
-
};
|
|
253
|
-
search: {
|
|
254
|
-
(): string;
|
|
255
|
-
(value: string): void;
|
|
256
|
-
};
|
|
257
|
-
hash: {
|
|
258
|
-
(): string;
|
|
259
|
-
(value: string): void;
|
|
260
|
-
};
|
|
261
|
-
};
|
|
262
|
-
/**
|
|
263
|
-
* Prime route context signals from the current `location` so that islands
|
|
264
|
-
* hydrated by `ilha.mount()` see the correct route values on their first
|
|
265
|
-
* render — preventing a mismatch morph that would destroy hydrated bindings.
|
|
266
|
-
*/
|
|
267
|
-
declare function prime(): void;
|
|
268
|
-
declare function navigate(to: string, opts?: NavigateOptions): void;
|
|
269
|
-
interface LinkInterceptionOptions {
|
|
270
|
-
/**
|
|
271
|
-
* Prefetch loader data on `mouseenter` for eligible links. Links opt in via
|
|
272
|
-
* the `data-prefetch` attribute (set `data-prefetch="false"` to opt out a
|
|
273
|
-
* specific link even when the framework is configured to prefetch by default).
|
|
274
|
-
* Default: `true` — prefetches on hover for any link with `data-prefetch`.
|
|
275
|
-
*/
|
|
276
|
-
prefetch?: boolean;
|
|
277
|
-
}
|
|
278
|
-
declare function enableLinkInterception(root?: Element | Document, options?: LinkInterceptionOptions): () => void;
|
|
279
|
-
declare const RouterView: Island<Record<string, unknown>, Record<never, never>>;
|
|
280
|
-
declare const RouterLink: Island<Record<string, unknown>, Omit<Omit<Record<never, never>, K> & Record<"href", string>, "label"> & Record<"label", string>>;
|
|
281
|
-
declare function isActive(pattern: string): boolean;
|
|
282
|
-
declare function router(options?: RouterOptions): RouterBuilder;
|
|
283
|
-
declare const _default: {
|
|
284
|
-
router: typeof router;
|
|
285
|
-
navigate: typeof navigate;
|
|
286
|
-
useRoute: typeof useRoute;
|
|
287
|
-
isActive: typeof isActive;
|
|
288
|
-
enableLinkInterception: typeof enableLinkInterception;
|
|
289
|
-
prime: typeof prime;
|
|
290
|
-
prefetch: typeof prefetch;
|
|
291
|
-
RouterView: Island<Record<string, unknown>, Record<never, never>>;
|
|
292
|
-
RouterLink: Island<Record<string, unknown>, Omit<Omit<Record<never, never>, K> & Record<"href", string>, "label"> & Record<"label", string>>;
|
|
293
|
-
loader: typeof loader;
|
|
294
|
-
redirect: typeof redirect;
|
|
295
|
-
error: typeof error;
|
|
296
|
-
composeLoaders: typeof composeLoaders;
|
|
297
|
-
};
|
|
298
|
-
//#endregion
|
|
299
|
-
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/plugin-OpojKenS.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{watch as e}from"node:fs";import{basename as t,dirname as n,extname as r,join as i,relative as a,resolve as o,sep as s}from"node:path";import{createUnplugin as c}from"unplugin";import{mkdir as l,readFile as u,readdir as d,writeFile as f}from"node:fs/promises";function p(e){return e.replace(/\\/g,`/`)}const m=/\.(test|spec|d)\.(ts|tsx)$/,h=/^\s*export\s+(?:const|let|var|async\s+function|function)\s+load\b/m;async function g(e){try{let t=(await u(e,`utf8`)).replace(/^\s*\/\/.*$/gm,``);return h.test(t)}catch{return!1}}function _(e){return e.startsWith(`[...`)&&e.endsWith(`]`)?`**:${e.slice(4,-1)}`:e.startsWith(`[`)&&e.endsWith(`]`)?`:${e.slice(1,-1)}`:e}function v(e){return e.startsWith(`(`)&&e.endsWith(`)`)?``:_(e)}function y(e,t){let n=p(a(e,t)),i=n.slice(0,-r(n).length).split(`/`),o=[...i.slice(0,-1).map(v),_(i.at(-1))];return o.at(-1)===`index`&&o.pop(),`/`+o.filter(Boolean).join(`/`)||`/`}function b(e){return e===`/`?`index`:e.replace(/^\//,``).replace(/\*\*:[^/]*/g,e=>e.length>3?e.slice(3):`wildcard`).replace(/:/g,``).replace(/\*\*/g,`wildcard`).replace(/\//g,`-`).replace(/[^a-zA-Z0-9-]/g,``)||`page`}function x(e){return e===`/`?3:e.includes(`**`)?0:e.includes(`:`)?1:2}function S(e){return[...e].sort((e,t)=>{let n=x(t.pattern)-x(e.pattern);if(n!==0)return n;let r=t.pattern.split(`/`).length-e.pattern.split(`/`).length;return r===0?e.pattern.localeCompare(t.pattern):r})}function C(e,t,r,o){let s=p(a(e,n(t))),c=s===``?[]:s.split(`/`);return[e,...c.map((t,n)=>i(e,...c.slice(0,n+1)))].flatMap(e=>{let t=`${i(e,o)}.tsx`;if(r.has(t))return[t];let n=`${i(e,o)}.ts`;return r.has(n)?[n]:[]})}async function w(e,t=0){if(t>20)return console.warn(`[ilha:pages] Max scan depth (20) reached at ${e} — skipping`),[];let n=[];try{for(let r of await d(e,{withFileTypes:!0})){let a=i(e,r.name);r.isDirectory()?n.push(...await w(a,t+1)):r.isFile()&&/\.(ts|tsx)$/.test(r.name)&&!m.test(r.name)&&n.push(a)}}catch(e){if(e.code===`ENOENT`)return[];throw e}return n}async function T(e){let n=await w(e),r=new Set(n),i=n.filter(e=>!t(e).startsWith(`+`)),a=new Map,o=e=>{let t=a.get(e);return t||(t=g(e),a.set(e,t)),t};return Promise.all(i.map(async t=>{let n=y(e,t),i=C(e,t,r,`+layout`),a=C(e,t,r,`+error`),[s,...c]=await Promise.all([g(t),...i.map(o)]),l=i.filter((e,t)=>c[t]);return{file:t,pattern:n,name:b(n),layouts:i,errors:a,hasLoader:s,loaderLayouts:l}}))}function E(e,t){if(e.length===0){console.warn(`[ilha:pages] No pages found in ${t}`);return}let n=new Map,r=new Map;for(let t of e){let e=n.get(t.pattern);e?console.warn(`[ilha:pages] Duplicate route pattern "${t.pattern}"\n first: ${e}\n second: ${t.file}\n The first match wins — the second page will never be reached.`):n.set(t.pattern,t.file);let i=r.get(t.name);i?console.warn(`[ilha:pages] Registry name collision: "${t.name}" is used by both\n ${i}\n ${t.file}\n Hydration may not work correctly for one of these routes.`):r.set(t.name,t.file)}}function D(e){return{serverFile:i(e,`pages.server.ts`),clientFile:i(e,`pages.client.ts`),loadersFile:i(e,`loaders.ts`)}}async function O(e,t,n={}){let r=n.mode??`spa`,i=n.interceptLinks,a=r===`static`,o=S(await T(e));E(o,e),await l(t,{recursive:!0});let{serverFile:s,clientFile:c,loadersFile:u}=D(t),d=await M(s,k(o,s)),f=await M(c,A(o,c,{isStatic:a,interceptLinks:i}));a||await M(u,j(o,u,s)),(d||f)&&await N(t)}function k(e,t){let r=e=>{let r=p(a(n(t),e));return r.startsWith(`.`)?r:`./${r}`},i=[`import { router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`],o=[],s=[],c=[];for(let[t,n]of e.entries()){i.push(`import { default as _page${t} } from ${JSON.stringify(r(n.file))};`);for(let[e,a]of n.layouts.entries())i.push(`import { default as _layout${t}_${e} } from ${JSON.stringify(r(a))};`);for(let[e,a]of n.errors.entries())i.push(`import { default as _error${t}_${e} } from ${JSON.stringify(r(a))};`);let a=`_page${t}`;for(let e=n.errors.length-1;e>=0;e--)a=`wrapError(_error${t}_${e}, ${a})`;for(let e=n.layouts.length-1;e>=0;e--)a=`wrapLayout(_layout${t}_${e}, ${a})`;let l=`_wrapped${t}`;o.push(`const ${l} = ${a};`),s.push(` ${JSON.stringify(n.name)}: ${l}`+(t<e.length-1?`,`:``)),c.push(` .route(${JSON.stringify(n.pattern)}, ${l})`+(n.hasLoader||n.loaderLayouts.length>0?`.markLoader(${JSON.stringify(n.pattern)})`:``))}return[`// @generated by @ilha/router — do not edit`,`// Server module. Use for SSR and SSG/prerender.`,`// Import via: import { pageRouter, registry } from "ilha:pages/server";`,``,...i,``,...o,``,`export const registry: Record<string, Island<any, any>> = {`,...s,`};`,``,`export const pageRouter = router()`,...c,` ;`].join(`
|
|
2
|
-
`)}function A(e,t,r){let{isStatic:i,interceptLinks:o}=r,s=e=>{let r=p(a(n(t),e));return r.startsWith(`.`)?r:`./${r}`},c=e=>`${s(e)}?client`,l=i?[`import { router as _router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`]:[`import { router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`],u=[],d=[],f=[];for(let[t,n]of e.entries()){l.push(`import { default as _page${t} } from ${JSON.stringify(c(n.file))};`);for(let[e,r]of n.layouts.entries())l.push(`import { default as _layout${t}_${e} } from ${JSON.stringify(c(r))};`);for(let[e,r]of n.errors.entries())l.push(`import { default as _error${t}_${e} } from ${JSON.stringify(c(r))};`);let r=`_page${t}`;for(let e=n.errors.length-1;e>=0;e--)r=`wrapError(_error${t}_${e}, ${r})`;for(let e=n.layouts.length-1;e>=0;e--)r=`wrapLayout(_layout${t}_${e}, ${r})`;let a=`_wrapped${t}`;u.push(`const ${a} = ${r};`),d.push(` ${JSON.stringify(n.name)}: ${a}`+(t<e.length-1?`,`:``)),i||f.push(` .route(${JSON.stringify(n.pattern)}, ${a})`+(n.hasLoader||n.loaderLayouts.length>0?`.markLoader(${JSON.stringify(n.pattern)})`:``))}let m=i?`_router({ mode: "static" })`:`router(${o===!1?`{ interceptLinks: false }`:``})`,h=[`// @generated by @ilha/router — do not edit`,`// Client module. Use for browser hydration.`,`// Import via: import { pageRouter, registry } from "ilha:pages/client";`,``,...l,``,...u,``,`export const registry: Record<string, Island<any, any>> = {`,...d,`};`,``];return i?h.push(`export const pageRouter = ${m};`):h.push(`export const pageRouter = ${m}`,...f,` ;`),h.join(`
|
|
3
|
-
`)}function j(e,t,r){let i=e=>{let r=p(a(n(t),e));return r.startsWith(`.`)?r:`./${r}`},o=e.filter(e=>e.hasLoader||e.loaderLayouts.length>0);if(o.length===0)return[`// @generated by @ilha/router — do not edit`,`// This project has no loader exports; this file is intentionally empty.`,``,`export {};`,``].join(`
|
|
4
|
-
`);let s=i(r).replace(/\.tsx?$/,``),c=[`import { pageRouter } from ${JSON.stringify(s)};`],l=!1,u=[];for(let[e,t]of o.entries()){let n=[];for(let[r,a]of t.loaderLayouts.entries()){let t=`_p${e}_l${r}`;c.push(`import { load as ${t} } from ${JSON.stringify(i(a))};`),n.push(t)}if(t.hasLoader){let r=`_p${e}`;c.push(`import { load as ${r} } from ${JSON.stringify(i(t.file))};`),n.push(r)}let r=n.length===1?n[0]:`composeLoaders([${n.join(`, `)}])`;n.length>1&&(l=!0),u.push(`pageRouter.attachLoader(${JSON.stringify(t.pattern)}, ${r});`)}return l&&c.unshift(`import { composeLoaders } from "@ilha/router";`),[`// @generated by @ilha/router — do not edit`,`// Server-only. Import this module from your SSR entry to wire loaders`,`// onto pageRouter. Importing it from the client is a no-op but wastes`,`// bundle size — rely on the default build pipeline to keep it out.`,``,...c,``,...u,``].join(`
|
|
5
|
-
`)}async function M(e,t){try{if(await u(e,`utf8`)===t)return!1}catch{}return await f(e,t,`utf8`),!0}async function N(e){await M(i(e,`pages.d.ts`),[`// @generated by @ilha/router — do not edit`,``,`declare module "ilha:pages/server" {`,` import type { RouterBuilder } from "@ilha/router";`,` import type { Island } from "ilha";`,` export const pageRouter: RouterBuilder;`,` export const registry: Record<string, Island<any, any>>;`,`}`,``,`declare module "ilha:pages/client" {`,` import type { RouterBuilder } from "@ilha/router";`,` import type { Island } from "ilha";`,` export const pageRouter: RouterBuilder;`,` export const registry: Record<string, Island<any, any>>;`,`}`,``,`declare module "ilha:loaders" {`,` // Side-effect-only module. Importing it attaches loaders to pageRouter.`,`}`,``].join(`
|
|
6
|
-
`))}const P=`\0ilha:pages/server`,F=`\0ilha:pages/client`,I=`\0ilha:loaders`,L=[P,F,I];function R(e,t){let n=o(e,t.dir??`src/pages`),r=o(e,t.outDir??`.ilha`),{serverFile:i,clientFile:a,loadersFile:s}=D(r);return{pagesDir:n,outDir:r,serverFile:i,clientFile:a,loadersFile:s}}function z(e){let n,r,i,a,o,c=t=>{({pagesDir:n,outDir:r,serverFile:i,clientFile:a,loadersFile:o}=R(t,e))},l=async()=>{try{await O(n,r,{mode:e.mode,interceptLinks:e.interceptLinks})}catch(e){console.error(`[ilha:pages] codegen failed:`,e)}},u=e=>e===n||e.startsWith(n+s);return{get pagesDir(){return n},get outDir(){return r},get serverFile(){return i},get clientFile(){return a},get loadersFile(){return o},setPaths:c,regen:l,shouldRegenOnChange:e=>{if(!u(e))return!1;let n=t(e);return n.startsWith(`+`)||/\.(ts|tsx)$/.test(n)},isUnderPagesDir:u}}async function B(e,t,n){n(t)&&await e.regen()}function V(e,t,n){if(t===`ilha:pages/server`)return P;if(t===`ilha:pages/client`)return F;if(t===`ilha:loaders`)return I;if(t.endsWith(`?client`)){let e=t.slice(0,-7);return(n?o(n.replace(/\?.*$/,``),`..`,e):o(e))+`?client`}}function H(e,t){if(t===`\0ilha:pages/server`){let t=e.serverFile.replace(/\.tsx?$/,``);return`export { pageRouter, registry } from ${JSON.stringify(t)};`}if(t===`\0ilha:pages/client`){let t=e.clientFile.replace(/\.tsx?$/,``);return`export { pageRouter, registry } from ${JSON.stringify(t)};`}if(t===`\0ilha:loaders`){let t=e.loadersFile.replace(/\.tsx?$/,``);return`import ${JSON.stringify(t)};`}if(t.endsWith(`?client`)){let e=t.slice(0,-7);return`export { default } from ${JSON.stringify(e)};`}}function U(e,t){return async n=>{e.isUnderPagesDir(n)&&(await e.regen(),await t())}}function W(t,n){let r=e(t.pagesDir,{recursive:!0},(e,r)=>{r&&n(i(t.pagesDir,r))});return()=>r.close()}const G=c((e={})=>{let t=z(e);return{name:`ilha:pages`,async buildStart(){t.pagesDir||t.setPaths(process.cwd()),this.addWatchFile?.(t.pagesDir),await t.regen()},async watchChange(e){await B(t,e,e=>t.shouldRegenOnChange(e))},resolveId(e,n){return V(t,e,n)},load(e){return H(t,e)},vite:{configResolved(e){t.setPaths(e.root)},configureServer(e){e.watcher.add(t.pagesDir);let n=U(t,async()=>{for(let t of L){let n=e.moduleGraph.getModuleById(t);n&&e.moduleGraph.invalidateModule(n)}e.hot.send({type:`full-reload`})});e.watcher.on(`add`,n),e.watcher.on(`addDir`,n),e.watcher.on(`unlink`,n),e.watcher.on(`change`,async e=>{t.shouldRegenOnChange(e)&&await n(e)})}},rspack(e){t.setPaths(e.options.context??process.cwd());let n=U(t,()=>{e.watching&&e.invalidate()}),r;e.hooks.watchRun.tap(`ilha:pages`,()=>{r?.(),r=W(t,n)}),e.hooks.shutdown.tap(`ilha:pages`,()=>r?.())}}});export{G as t};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as _$unplugin from "unplugin";
|
|
2
|
-
|
|
3
|
-
//#region src/codegen.d.ts
|
|
4
|
-
type PagesMode = "spa" | "static";
|
|
5
|
-
//#endregion
|
|
6
|
-
//#region src/plugin.d.ts
|
|
7
|
-
interface IlhaPagesOptions {
|
|
8
|
-
/** Directory containing page files. Default: `src/pages` */
|
|
9
|
-
dir?: string;
|
|
10
|
-
/** Output directory for generated files. Default: `.ilha` */
|
|
11
|
-
outDir?: string;
|
|
12
|
-
/**
|
|
13
|
-
* File-system router navigation mode.
|
|
14
|
-
* - `spa` — full client route graph with SSR/hydration and client navigation.
|
|
15
|
-
* - `static` — island registry only; no route graph bundled into the client.
|
|
16
|
-
* Default: `spa`.
|
|
17
|
-
*/
|
|
18
|
-
mode?: PagesMode;
|
|
19
|
-
/**
|
|
20
|
-
* When `false`, internal `<a>` clicks are not intercepted — browser performs
|
|
21
|
-
* full document navigations. Only meaningful in `spa` mode.
|
|
22
|
-
* Default: `true`.
|
|
23
|
-
*/
|
|
24
|
-
interceptLinks?: boolean;
|
|
25
|
-
}
|
|
26
|
-
declare const ilhaPages: _$unplugin.UnpluginInstance<IlhaPagesOptions | undefined, boolean>;
|
|
27
|
-
//#endregion
|
|
28
|
-
export { ilhaPages as n, IlhaPagesOptions as t };
|
package/dist/src-Cprpo919.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import e,{ISLAND_MOUNT_INTERNAL as t,context as n,html as r,mount as i}from"ilha";import{addRoute as a,createRouter as o,findRoute as s}from"rou3";const c=typeof window<`u`&&typeof document<`u`,l={readLocation(){return c?{pathname:location.pathname,search:location.search,hash:location.hash}:{pathname:`/`,search:``,hash:``}},push(e){c&&history.pushState(null,``,e)},replace(e){c&&history.replaceState(null,``,e)},onChange(e){return c?(window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)):()=>{}},toLinkHref(e){return e},extractLogicalPath(e){let t=e.getAttribute(`href`);return!t||e.protocol&&!/^(http:|https:)$/.test(e.protocol)||t.startsWith(`#`)||e.hostname&&(e.hostname!==location.hostname||e.protocol!==location.protocol)?null:e.pathname+e.search+e.hash}};function u(e){let t=e.startsWith(`#`)?e.slice(1):e,n=t===``?`/`:t.startsWith(`/`)?t:`/`+t,r=new URL(n,`http://_`);return{pathname:r.pathname,search:r.search,hash:r.hash}}const d={readLocation(){return c?u(location.hash):{pathname:`/`,search:``,hash:``}},push(e){c&&history.pushState(null,``,e.startsWith(`#`)?e:`#`+e)},replace(e){c&&history.replaceState(null,``,e.startsWith(`#`)?e:`#`+e)},onChange(e){return c?(window.addEventListener(`popstate`,e),window.addEventListener(`hashchange`,e),()=>{window.removeEventListener(`popstate`,e),window.removeEventListener(`hashchange`,e)}):()=>{}},toLinkHref(e){return e.startsWith(`#`)?e:`#`+e},extractLogicalPath(e){let t=e.getAttribute(`href`);if(!t||e.protocol&&!/^(http:|https:)$/.test(e.protocol))return null;if(t.startsWith(`#`)){let e=t.slice(1);return e===``||!e.startsWith(`/`)?null:e}if(/^https?:\/\//i.test(t))try{let e=new URL(t);if(e.origin!==location.origin||!e.hash||e.hash===`#`)return null;let n=e.hash.slice(1);return n.startsWith(`/`)?n:null}catch{return null}return t}};let f=`history`,p=l;function m(e){f=e,p=e===`hash`?d:l}function h(){return f}function g(){return p}const _=typeof window<`u`&&typeof document<`u`;function v(e){return e}var y=class{__ilhaRedirect=!0;to;status;constructor(e,t=302){this.to=e,this.status=t}},b=class{__ilhaLoaderError=!0;status;message;constructor(e,t){this.status=e,this.message=t}};function x(e,t=302){throw new y(e,t)}function S(e,t){throw new b(e,t)}function C(e){return e.length===0?async()=>({}):e.length===1?e[0]:async t=>{let n=await Promise.all(e.map(e=>e(t)));return Object.assign({},...n)}}const w=Symbol.for(`ilha.router.wrapLayout.leaf`),T=Symbol.for(`ilha.router.wrapLayout.handler`);function E(e){let t=e.match(/^<([a-zA-Z][\w-]*)\s[^>]*>([\s\S]*)<\/\1>\s*$/);return t?t[2]:e}function ee(e){let t=e.match(/^<([a-zA-Z][\w-]*)\s([^>]*)>/);return t?{tag:t[1],attrs:t[2]}:null}const te=/<(pre|script|style|textarea)\b/i;function ne(e,t,n){let r=RegExp(`<${n}\\b`,`gi`),i=RegExp(`</${n}>`,`gi`),a=1,o=t;for(;a>0&&o<e.length;){r.lastIndex=o,i.lastIndex=o;let t=r.exec(e),n=i.exec(e);if(!n)return null;if(t&&t.index<n.index)a+=1,o=t.index+t[0].length;else{if(--a,a===0)return n.index+n[0].length;o=n.index+n[0].length}}return o}function re(e,t){let n=t;for(;n<e.length;){if(e.startsWith(`<!--`,n)){let t=e.indexOf(`-->`,n);if(t===-1)return null;n=t+3;continue}let t=e.slice(n).match(te);if(t&&t.index!=null&&t.index>=0){let r=n+t.index,i=!1,a=n;for(;a<r;){let t=e.indexOf(`<!--`,a);if(t===-1||t>=r)break;let o=e.indexOf(`-->`,t);if(o===-1)return null;if(r<o+3){n=o+3,i=!0;break}a=o+3}if(i)continue;let o=e.indexOf(`<div`,n),s=e.indexOf(`</div>`,n);if(!(o!==-1&&o<r||s!==-1&&s<r)){let i=t[1].toLowerCase(),a=ne(e,r+t[0].length,i);if(a===null)return null;n=a;continue}}let r=e.indexOf(`<div`,n),i=e.indexOf(`</div>`,n);return i===-1&&r===-1?null:r===-1||i!==-1&&i<r?{kind:`close`,index:i}:{kind:`open`,index:r}}return null}function ie(e){let t=[];for(let n of e.matchAll(/<div\s[^>]*data-ilha-slot="k:page"[^>]*>/g)){let r=n.index+n[0].length,i=1,a=r;for(;i>0;){let n=re(e,a);if(!n)break;if(n.kind===`open`)i+=1,a=n.index+4;else{if(--i,i===0){t.push({openEnd:r,closeStart:n.index});break}a=n.index+6}}}return t}function D(e,t,n){let r=ie(e);if(r.length===0)return e;let i=n===`innermost`?r[r.length-1]:r[0];return e.slice(0,i.openEnd)+t+e.slice(i.closeStart)}function O(e,t){let n=e[T];if(!n)return e.toString(t);let r=e[w]??e;return n(Object.assign(r.key(`page`),{toString:()=>``})).toString(t)}async function ae(e,t,n,r){let i=E(await t.hydratable(n,r));return D(D(O(e,n),``,`innermost`),i,`innermost`)}function oe(e,n){let r=n[w]??n,i=r===n?null:n,a=e(Object.assign(n.key(`page`),{toString:n.toString.bind(n)}));a[w]=r,a[T]=e;function o(e){let t=[...e.querySelectorAll(`[data-ilha-slot="k:page"]`)].filter(t=>{let n=t.closest(`[data-ilha]`);return n===null||n===e});return t.length===0?e:t[t.length-1]}function s(e,t){if(t.hasAttribute(`data-ilha-state`))return;let n=e.getAttribute(`data-ilha-state`);if(n)try{let e=JSON.parse(n);delete e._skipOnMount,t.setAttribute(`data-ilha-state`,JSON.stringify(e))}catch{}}let c=a.mount.bind(a),l=a[t];function u(e){s(e,o(e)),e.removeAttribute(`data-ilha-state`)}return a.mount=(e,t)=>(u(e),c(e,t)),a[t]=(e,t)=>(u(e),typeof l==`function`?l(e,t):{unmount:c(e,t),updateProps:()=>{}}),a.hydratable=async(e,t)=>{if(!t?.name)throw Error(`wrapLayout: hydratable requires options.name`);let n=e??{},o=await r.hydratable(n,t),s=ee(o);if(!s)return o;let c=E(o);i&&(c=await ae(i,r,n,t));let l=D(D(O(a,n),``,`first`),c,`first`);return`<${s.tag} ${s.attrs}>${l}</${s.tag}>`},a}function se(n,r){let i=e.render(()=>{try{return r.toString()}catch(e){let t={path:P(),params:F(),search:I(),hash:L()};return n({message:e.message,status:e.status,stack:e.stack},t).toString()}});return i.mount=(e,t)=>{try{return r.mount(e,t)}catch(r){let i={path:P(),params:F(),search:I(),hash:L()},a=n({message:r.message,status:r.status,stack:r.stack},i);return e.innerHTML=a.toString(),a.mount(e,t)}},i[t]=(e,i)=>{try{let n=r[t];return typeof n==`function`?n(e,i):{unmount:r.mount(e,i),updateProps:()=>{}}}catch(t){let r={path:P(),params:F(),search:I(),hash:L()},a=n({message:t.message,status:t.status,stack:t.stack},r);return e.innerHTML=a.toString(),{unmount:a.mount(e,i),updateProps:()=>{}}}},i.hydratable=async(e,t)=>{if(!t?.name)throw Error(`wrapError: hydratable requires options.name`);return r.hydratable(e??{},t)},i}function ce(e){return e}function k(e){let t=new Map;for(let[n,r]of Object.entries(e))t.has(r)||t.set(r,n);return t}const A=`/__ilha/loader`,j=new Map;async function M(e,t){let n=j.get(e);if(n){j.delete(e);try{return await n}catch{}}let r=`${A}?path=${encodeURIComponent(e)}`;try{let e=await fetch(r,{signal:t,headers:{accept:`application/json`}});if(!e.ok){try{let t=await e.json();if(t&&typeof t==`object`&&`kind`in t)return t}catch{}return{kind:`error`,status:e.status,message:e.statusText}}return await e.json()}catch(e){if(e?.name===`AbortError`)throw e;return{kind:`error`,status:0,message:e?.message??`network error`}}}function N(e){if(!_||j.has(e))return;let t=e.split(`?`)[0]??``;if(!s(V,`GET`,t)?.data?.hasLoader)return;let n=M(e).catch(e=>({kind:`error`,status:0,message:e?.message??`prefetch failed`}));j.set(e,n)}async function le(e,t,n,r,i,a){if(!e)return t.innerHTML=`<div data-router-empty></div>`,()=>{};let o=!!s(V,`GET`,n.split(`?`)[0]??``)?.data?.hasLoader,c={},l=o?await M(n,r):{kind:`data`,data:{}};if(l.kind===`redirect`)return J(l.to,{replace:!0}),()=>{};if(l.kind===`error`){let e=String(l.message).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`);return t.innerHTML=`<div data-router-view data-router-error="${l.status}">${e}</div>`,()=>{}}if(l.kind===`not-found`)return t.innerHTML=`<div data-router-empty></div>`,()=>{};if(c=l.data,!i)return console.warn(`[ilha-router] No registry provided for client-side navigation. Island will not be interactive.`),t.innerHTML=`<div data-router-view>${e.toString(c)}</div>`,()=>{};let u=a?.get(e)??Object.entries(i).find(([,t])=>t===e)?.[0];if(!u)return console.warn(`[ilha-router] Island not found in registry for client-side navigation.`),t.innerHTML=`<div data-router-view>${e.toString(c)}</div>`,()=>{};t.innerHTML=`<div data-router-view>${await e.hydratable(c,{name:u,as:`div`,snapshot:!0})}</div>`;let d=t.querySelector(`[data-ilha="${u}"]`);return d?e.mount(d):()=>{}}const P=n(`router.path`,``),F=n(`router.params`,{}),I=n(`router.search`,``),L=n(`router.hash`,``);function R(){return{path:P,params:F,search:I,hash:L}}const z=n(`router.active`,null);let B=[],V=o(),H=new Map,U=new Map;function W(e){let t={};if(e)for(let[n,r]of Object.entries(e))t[n]=decodeURIComponent(r);return t}function G(e){let t=typeof e==`string`?new URL(e,`http://localhost`):e,n=s(V,`GET`,t.pathname);P(t.pathname),F(W(n?.params)),I(t.search),L(t.hash),z(n?.data?.island??null)}function K(){let e=g().readLocation(),t=s(V,`GET`,e.pathname);P(e.pathname),F(W(t?.params)),I(e.search),L(e.hash),z(t?.data?.island??null)}function q(){_&&K()}function J(e,t={}){if(!_)return;let n=g(),r=n.readLocation();e!==r.pathname+r.search+r.hash&&(t.replace?n.replace(e):n.push(e),K())}function Y(e=document,t={}){if(!_)return()=>{};let n=t.prefetch!==!1;function r(e,t){let n=e.getAttribute(`target`)===`_blank`,r=!!t&&(t.ctrlKey||t.metaKey||t.shiftKey),i=e.hasAttribute(`data-no-intercept`);return n||r||i?null:g().extractLogicalPath(e)}let i=e=>{if(e.defaultPrevented)return;let t=e.target.closest(`a`);if(!t)return;let n=r(t,e);n!==null&&(e.preventDefault(),J(n))},a=e=>{let t=e.target.closest(`a`);if(!t)return;let n=t.getAttribute(`data-prefetch`);if(n===null||n===`false`)return;let i=r(t);i!==null&&N(i.split(`#`)[0]??i)};return e.addEventListener(`click`,i),n&&e.addEventListener(`mouseover`,a,{passive:!0}),()=>{e.removeEventListener(`click`,i),n&&e.removeEventListener(`mouseover`,a)}}const X=e.render(()=>{let e=z();return e?`<div data-router-view>${e.toString()}</div>`:`<div data-router-empty></div>`}),Z=e.state(`href`,``).state(`label`,``).on(`[data-link]@click`,({state:e,event:t})=>{t.preventDefault(),J(e.href())}).on(`[data-link]@mouseenter`,({state:e})=>{let t=e.href();if(t){if(/^https?:\/\//i.test(t))try{let e=new URL(t);if(e.origin!==location.origin)return;N(e.pathname+e.search);return}catch{return}N(t)}}).render(({state:e})=>r`<a data-link data-prefetch href="${()=>g().toLinkHref(e.href())}"
|
|
2
|
-
>${e.label}</a
|
|
3
|
-
>`);function Q(e){let t=s(V,`GET`,P());return t?H.get(t.data.island)===e:!1}function ue(e){return typeof e==`string`?new URL(e,`http://localhost`):e}function de(e){try{return new Request(e.toString())}catch{return{url:e.toString(),headers:new Headers}}}async function fe(e,t,n,r,i){try{return{kind:`data`,data:await e({params:n,request:r,url:t,signal:i})??{}}}catch(e){return e instanceof y?{kind:`redirect`,to:e.to,status:e.status}:e instanceof b?{kind:`error`,status:e.status,message:e.message}:{kind:`error`,status:e?.status??500,message:e?.message??`Loader failed`}}}function $(t={}){let n=t.mode??`spa`,r=t.interceptLinks!==!1;B=[],V=o(),H=new Map,U=new Map;let c=null,l=null,u={route(e,t,n){let r=!!n,i={island:t,loader:n,hasLoader:r};return B.push({pattern:e,island:t,loader:n,hasLoader:r}),a(V,`GET`,e,i),U.set(e,i),H.has(t)||H.set(t,e),u},attachLoader(e,t){let n=U.get(e);if(!n)return console.warn(`[ilha-router] attachLoader("${e}", …): pattern was never registered via .route(). The loader will be ignored.`),u;n.loader=t,n.hasLoader=!0;let r=B.find(t=>t.pattern===e);return r&&(r.loader=t,r.hasLoader=!0),u},markLoader(e){let t=U.get(e);if(!t)return console.warn(`[ilha-router] markLoader("${e}"): pattern was never registered via .route(). The loader marker will be ignored.`),u;t.hasLoader=!0;let n=B.find(t=>t.pattern===e);return n&&(n.hasLoader=!0),u},routes(){return B.map(e=>({...e}))},prime:q,hydrateStatic(e,t={}){if(!_)return()=>{};let n=t.root??document.body;q();let{unmount:r}=i(e,{root:n});return r},mount(t,{hydrate:i=!1,registry:a,interceptLinks:o}={}){if(!_)return console.warn(`[ilha-router] mount() called in a non-browser environment`),()=>{};let u=typeof t==`string`?document.querySelector(t):t;if(!u)return console.warn(`[ilha-router] No element found for selector "${t}"`),()=>{};if(K(),n===`static`)return console.warn(`[ilha-router] router.mount() called in static mode. Use router.hydrateStatic(registry) instead.`),()=>{};c=g().onChange(()=>K()),l=o??r?Y(document):null;let d=null,f=null;if(i){h()===`hash`&&console.warn("[ilha-router] mount({ hydrate: true }) was called in hash mode. SSR + hydration assumes the server can render the active route, but in hash mode the server only ever sees the document URL. Use plain SPA mode (`mount(target)` without `hydrate: true`) for hash-mode apps.");let t=u.querySelector(`[data-router-view]`)??u,n=z(),r=a?k(a):void 0,i=0,o=e.render(()=>{let e=z();if(e!==n){let o=++i;f?.abort(),f=new AbortController;let s=f.signal;queueMicrotask(async()=>{if(o===i){d?.();try{let n=g().readLocation();d=await le(e,t,n.pathname+n.search,s,a,r)}catch(e){if(e?.name===`AbortError`)return;throw e}n=e}})}return``}),s=document.createElement(`div`);s.style.display=`none`,u.appendChild(s);let p=o.mount(s);return()=>{++i,f?.abort(),p(),s.remove(),d?.(),c?.(),l?.(),c=null,l=null}}let p=null,m=null,v=0;d=X.mount(u);async function y(e,t){if(p?.(),p=null,m=e,!e)return;let n=u?.querySelector(`[data-router-view]`);if(!n)return;let r=g().readLocation(),i=s(V,`GET`,r.pathname)?.data?.hasLoader?await M(r.pathname+r.search,t):{kind:`data`,data:{}};if(t.aborted)return;if(i.kind===`redirect`){J(i.to,{replace:!0});return}let a=i.kind===`data`?i.data:{};n.innerHTML=e.toString(a),p=e.mount(n,a)}f=new AbortController,y(z(),f.signal);let b=e.render(()=>{let e=z();if(e!==m){let t=++v;f?.abort(),f=new AbortController;let n=f.signal;queueMicrotask(()=>{t===v&&y(e,n)})}return``}),x=document.createElement(`div`);x.style.display=`none`,u.appendChild(x);let S=b.mount(x);return()=>{++v,f?.abort(),p?.(),S(),x.remove(),d?.(),c?.(),l?.(),c=null,l=null}},render(e){return G(e),X.toString()},async renderHydratable(e,t,n={},r){let i=await this.renderResponse(e,t,n,r);return i.kind===`html`||i.kind===`error`?i.html:`<meta http-equiv="refresh" content="0; url=${i.to}">`},async renderResponse(e,t,n={},r){let i=ue(e);G(i);let a=s(V,`GET`,i.pathname),o=a?.data?.island??null;if(!o)return{kind:`html`,html:`<div data-router-empty></div>`,status:404};let c={};if(a?.data?.loader){let e=r??de(i),t=new AbortController,n=await fe(a.data.loader,i,F(),e,t.signal);if(n.kind===`redirect`)return{kind:`redirect`,to:n.to,status:n.status};if(n.kind===`error`){let e=String(n.message).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`),t=`<div data-router-view data-router-error="${n.status}">${e}</div>`;return{kind:`error`,status:n.status,message:n.message,html:t}}c=n.data}let l=k(t).get(o);return l?{kind:`html`,html:`<div data-router-view>${await o.hydratable(c,{name:l,as:`div`,snapshot:!0,...n})}</div>`}:(console.warn(`[ilha-router] renderHydratable: active island for "${P()}" is not in the registry. Falling back to plain SSR — the island will not be interactive on the client.`),{kind:`html`,html:`<div data-router-view>${o.toString(c)}</div>`})},async runLoader(e,t){let n=ue(e),r=s(V,`GET`,n.pathname);if(!r?.data?.island)return{kind:`not-found`};if(!r.data.loader)return{kind:`data`,data:{}};let i=W(r.params),a=t??de(n),o=new AbortController;return fe(r.data.loader,n,i,a,o.signal)},hydrate(e,t={}){if(!_)return console.warn(`[ilha-router] hydrate() called in a non-browser environment`),()=>{};let n=t.root??document.body,r=t.target??n;q();let{unmount:a}=i(e,{root:n}),o=this.mount(r,{hydrate:!0,registry:e,interceptLinks:t.interceptLinks});return()=>{a(),o()}}};return u}var pe={router:$,navigate:J,useRoute:R,isActive:Q,enableLinkInterception:Y,prime:q,prefetch:N,RouterView:X,RouterLink:Z,loader:v,redirect:x,error:S,composeLoaders:C};export{se as C,m as E,R as S,h as T,F as _,X as a,$ as b,Y as c,v as d,J as f,L as g,x as h,Z as i,S as l,q as m,b as n,C as o,N as p,y as r,ce as s,A as t,Q as u,P as v,oe as w,pe as x,I as y};
|