@zenithbuild/router 0.5.0-beta.2.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 +130 -0
- package/dist/ZenLink.zen +7 -0
- package/dist/events.js +53 -0
- package/dist/history.js +84 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +32 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +180 -0
- package/dist/manifest.js.map +1 -0
- package/dist/match.js +191 -0
- package/dist/navigate.js +67 -0
- package/dist/navigation/client-router.d.ts +59 -0
- package/dist/navigation/client-router.d.ts.map +1 -0
- package/dist/navigation/client-router.js +373 -0
- package/dist/navigation/client-router.js.map +1 -0
- package/dist/navigation/index.d.ts +30 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +44 -0
- package/dist/navigation/index.js.map +1 -0
- package/dist/navigation/zen-link.d.ts +234 -0
- package/dist/navigation/zen-link.d.ts.map +1 -0
- package/dist/navigation/zen-link.js +437 -0
- package/dist/navigation/zen-link.js.map +1 -0
- package/dist/router.js +111 -0
- package/dist/runtime.d.ts +33 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +157 -0
- package/dist/runtime.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
- package/template.js +260 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith Runtime Router (Native Bridge)
|
|
3
|
+
*
|
|
4
|
+
* SPA-style client-side router with SSR support via native resolution.
|
|
5
|
+
*/
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
import native from "../index.js";
|
|
8
|
+
import { state } from "@zenithbuild/runtime";
|
|
9
|
+
const { resolveRouteNative, generateRuntimeRouterNative } = native;
|
|
10
|
+
// Zenith strict signal integration: use reactive state
|
|
11
|
+
const currentRoute = state({
|
|
12
|
+
path: "/",
|
|
13
|
+
params: {},
|
|
14
|
+
query: {},
|
|
15
|
+
matched: undefined
|
|
16
|
+
});
|
|
17
|
+
const routeListeners = new Set();
|
|
18
|
+
let routeManifest = [];
|
|
19
|
+
let routerOutlet = null;
|
|
20
|
+
export function initRouter(manifest, outlet) {
|
|
21
|
+
routeManifest = manifest;
|
|
22
|
+
if (outlet) {
|
|
23
|
+
routerOutlet = typeof outlet === "string" ? document.querySelector(outlet) : outlet;
|
|
24
|
+
}
|
|
25
|
+
window.addEventListener("popstate", handlePopState);
|
|
26
|
+
resolveAndRender(window.location.pathname, parseQueryString(window.location.search), false);
|
|
27
|
+
}
|
|
28
|
+
function parseQueryString(search) {
|
|
29
|
+
const query = {};
|
|
30
|
+
if (!search || search === "?")
|
|
31
|
+
return query;
|
|
32
|
+
const params = new URLSearchParams(search);
|
|
33
|
+
params.forEach((value, key) => { query[key] = value; });
|
|
34
|
+
return query;
|
|
35
|
+
}
|
|
36
|
+
function handlePopState(_event) {
|
|
37
|
+
resolveAndRender(window.location.pathname, parseQueryString(window.location.search), false, false);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve route - uses native implementation if available (Node.js/SSR)
|
|
41
|
+
* or falls back to JS implementation for the browser.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveRoute(pathname) {
|
|
44
|
+
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
|
45
|
+
// SSR / Node environment
|
|
46
|
+
const resolved = resolveRouteNative(pathname, routeManifest);
|
|
47
|
+
if (!resolved)
|
|
48
|
+
return null;
|
|
49
|
+
return {
|
|
50
|
+
record: resolved.matched,
|
|
51
|
+
params: resolved.params
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Client-side fallback implementation
|
|
55
|
+
const normalizedPath = pathname === "" ? "/" : pathname;
|
|
56
|
+
for (const route of routeManifest) {
|
|
57
|
+
const regex = route.regex instanceof RegExp ? route.regex : new RegExp(route.regex.source, route.regex.flags);
|
|
58
|
+
const match = regex.exec(normalizedPath);
|
|
59
|
+
if (match) {
|
|
60
|
+
const params = {};
|
|
61
|
+
for (let i = 0; i < route.paramNames.length; i++) {
|
|
62
|
+
const paramName = route.paramNames[i];
|
|
63
|
+
const paramValue = match[i + 1];
|
|
64
|
+
if (paramName && paramValue !== undefined) {
|
|
65
|
+
params[paramName] = decodeURIComponent(paramValue);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { record: route, params };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
async function resolveAndRender(path, query, updateHistory = true, replace = false) {
|
|
74
|
+
const prevRoute = { ...currentRoute };
|
|
75
|
+
const resolved = resolveRoute(path);
|
|
76
|
+
if (resolved) {
|
|
77
|
+
// Update reactive state properties instead of replacing object
|
|
78
|
+
currentRoute.path = path;
|
|
79
|
+
currentRoute.params = resolved.params;
|
|
80
|
+
currentRoute.query = query;
|
|
81
|
+
currentRoute.matched = resolved.record;
|
|
82
|
+
const pageModule = resolved.record.module || (resolved.record.load ? await resolved.record.load() : null);
|
|
83
|
+
if (pageModule)
|
|
84
|
+
await renderPage(pageModule);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
currentRoute.path = path;
|
|
88
|
+
currentRoute.params = {};
|
|
89
|
+
currentRoute.query = query;
|
|
90
|
+
currentRoute.matched = undefined;
|
|
91
|
+
}
|
|
92
|
+
if (updateHistory) {
|
|
93
|
+
const url = path + (Object.keys(query).length > 0 ? "?" + new URLSearchParams(query).toString() : "");
|
|
94
|
+
if (replace)
|
|
95
|
+
window.history.replaceState(null, "", url);
|
|
96
|
+
else
|
|
97
|
+
window.history.pushState(null, "", url);
|
|
98
|
+
}
|
|
99
|
+
notifyListeners(currentRoute, prevRoute);
|
|
100
|
+
window.__zenith_route = currentRoute;
|
|
101
|
+
}
|
|
102
|
+
async function renderPage(pageModule) {
|
|
103
|
+
if (!routerOutlet)
|
|
104
|
+
return;
|
|
105
|
+
routerOutlet.innerHTML = pageModule.html;
|
|
106
|
+
// ... logic for styles and scripts
|
|
107
|
+
}
|
|
108
|
+
function notifyListeners(route, prevRoute) {
|
|
109
|
+
routeListeners.forEach(listener => { listener(route, prevRoute); });
|
|
110
|
+
}
|
|
111
|
+
export async function navigate(to, options = {}) {
|
|
112
|
+
let path, query = {};
|
|
113
|
+
if (to.includes("?")) {
|
|
114
|
+
const [pathname, search] = to.split("?");
|
|
115
|
+
path = pathname || "/";
|
|
116
|
+
query = parseQueryString("?" + (search || ""));
|
|
117
|
+
}
|
|
118
|
+
else
|
|
119
|
+
path = to;
|
|
120
|
+
await resolveAndRender(path, query, true, options.replace || false);
|
|
121
|
+
}
|
|
122
|
+
// Return the reactive proxy directly
|
|
123
|
+
export function getRoute() { return currentRoute; }
|
|
124
|
+
/**
|
|
125
|
+
* zenRoute() - Zenith Law: Environment Resolution
|
|
126
|
+
*
|
|
127
|
+
* Canonical primitive for resolving the current route environment.
|
|
128
|
+
* Must be resolved once and execute before state initialization.
|
|
129
|
+
*/
|
|
130
|
+
export function zenRoute() {
|
|
131
|
+
const path = typeof window !== 'undefined' ? window.location.pathname : currentRoute.path;
|
|
132
|
+
return {
|
|
133
|
+
path,
|
|
134
|
+
slugs: getSlugs(path)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function getSlugs(path) {
|
|
138
|
+
return path.split('/').filter(Boolean);
|
|
139
|
+
}
|
|
140
|
+
export function onRouteChange(listener) {
|
|
141
|
+
routeListeners.add(listener);
|
|
142
|
+
return () => { routeListeners.delete(listener); };
|
|
143
|
+
}
|
|
144
|
+
export function isActive(path, exact = false) {
|
|
145
|
+
return exact ? currentRoute.path === path : currentRoute.path.startsWith(path);
|
|
146
|
+
}
|
|
147
|
+
export async function prefetch(path) {
|
|
148
|
+
const resolved = resolveRoute(path);
|
|
149
|
+
if (resolved && resolved.record.load && !resolved.record.module) {
|
|
150
|
+
resolved.record.module = await resolved.record.load();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export function isPrefetched(_path) { return false; }
|
|
154
|
+
export function generateRuntimeRouterCode() {
|
|
155
|
+
return generateRuntimeRouterNative();
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zenith Router Types
|
|
3
|
+
*
|
|
4
|
+
* Re-exports types from the native Rust implementation.
|
|
5
|
+
*/
|
|
6
|
+
import { RouteRecord as NativeRouteRecord, RouteState as NativeRouteState, RouteManifest as NativeRouteManifest, SegmentType as NativeSegmentType, ParsedSegment as NativeParsedSegment } from "../index.js";
|
|
7
|
+
export { NativeSegmentType as SegmentType };
|
|
8
|
+
export interface RouteRecord extends Omit<NativeRouteRecord, 'regex'> {
|
|
9
|
+
regex: RegExp;
|
|
10
|
+
}
|
|
11
|
+
export interface RouteState extends Omit<NativeRouteState, 'matched'> {
|
|
12
|
+
matched?: RouteRecord;
|
|
13
|
+
}
|
|
14
|
+
export interface RouteManifest extends Omit<NativeRouteManifest, 'routes'> {
|
|
15
|
+
routes: RouteRecord[];
|
|
16
|
+
}
|
|
17
|
+
export interface ParsedSegment extends NativeParsedSegment {
|
|
18
|
+
}
|
|
19
|
+
export interface PageModule {
|
|
20
|
+
html: string;
|
|
21
|
+
scripts: string[];
|
|
22
|
+
styles: string[];
|
|
23
|
+
meta?: PageMeta;
|
|
24
|
+
}
|
|
25
|
+
export interface PageMeta {
|
|
26
|
+
title?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
[key: string]: string | undefined;
|
|
29
|
+
}
|
|
30
|
+
export interface NavigateOptions {
|
|
31
|
+
replace?: boolean;
|
|
32
|
+
scrollToTop?: boolean;
|
|
33
|
+
state?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
export interface RouteDefinition extends RouteRecord {
|
|
36
|
+
}
|
|
37
|
+
export interface RuntimeRouteRecord extends RouteRecord {
|
|
38
|
+
module?: PageModule | null;
|
|
39
|
+
load?: () => Promise<PageModule>;
|
|
40
|
+
}
|
|
41
|
+
export type RouteListener = (route: RouteState, prevRoute: RouteState) => void;
|
|
42
|
+
export interface Router {
|
|
43
|
+
readonly route: RouteState;
|
|
44
|
+
navigate(to: string, options?: NavigateOptions): Promise<void>;
|
|
45
|
+
resolve(path: string): {
|
|
46
|
+
record: RouteRecord;
|
|
47
|
+
params: Record<string, string>;
|
|
48
|
+
} | null;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACH,WAAW,IAAI,iBAAiB,EAChC,UAAU,IAAI,gBAAgB,EAC9B,aAAa,IAAI,mBAAmB,EACpC,WAAW,IAAI,iBAAiB,EAChC,aAAa,IAAI,mBAAmB,EACvC,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,iBAAiB,IAAI,WAAW,EAAE,CAAA;AAE3C,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACjE,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,gBAAgB,EAAE,SAAS,CAAC;IACjE,OAAO,CAAC,EAAE,WAAW,CAAA;CACxB;AAED,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,mBAAmB,EAAE,QAAQ,CAAC;IACtE,MAAM,EAAE,WAAW,EAAE,CAAA;CACxB;AAED,MAAM,WAAW,aAAc,SAAQ,mBAAmB;CAAI;AAE9D,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,IAAI,CAAC,EAAE,QAAQ,CAAA;CAClB;AAED,MAAM,WAAW,QAAQ;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CACpC;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;CAAI;AAExD,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACnD,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;CACpC;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,KAAK,IAAI,CAAC;AAE/E,MAAM,WAAW,MAAM;IACnB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI,CAAA;CACxF"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zenithbuild/router",
|
|
3
|
+
"version": "0.5.0-beta.2.3",
|
|
4
|
+
"description": "File-based SPA router for Zenith framework with deterministic, compile-time route resolution",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"template.js",
|
|
11
|
+
"index.js",
|
|
12
|
+
"index.d.ts",
|
|
13
|
+
"README.md",
|
|
14
|
+
"dist/**",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"package.json"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./dist/index.js",
|
|
20
|
+
"./template": "./template.js",
|
|
21
|
+
"./ZenLink.zen": "./dist/ZenLink.zen"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"zenith",
|
|
25
|
+
"router",
|
|
26
|
+
"spa",
|
|
27
|
+
"file-based-routing",
|
|
28
|
+
"compile-time"
|
|
29
|
+
],
|
|
30
|
+
"author": "Zenith Team",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+ssh://git@github.com/zenithbuild/zenith-router.git"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"format": "prettier --write \"**/*.ts\"",
|
|
37
|
+
"format:check": "prettier --check \"**/*.ts\"",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"build": "mkdir -p dist && cp -a src/* dist/ && cp src/ZenLink.zen dist/ZenLink.zen",
|
|
40
|
+
"release": "bun run scripts/release.ts",
|
|
41
|
+
"release:dry": "bun run scripts/release.ts --dry-run",
|
|
42
|
+
"release:patch": "bun run scripts/release.ts --bump=patch",
|
|
43
|
+
"release:minor": "bun run scripts/release.ts --bump=minor",
|
|
44
|
+
"release:major": "bun run scripts/release.ts --bump=major",
|
|
45
|
+
"contract:deps": "node dependency_contract.spec.js",
|
|
46
|
+
"contract:scan": "node contract-scan.mjs",
|
|
47
|
+
"contract:template": "node template-contract.spec.js",
|
|
48
|
+
"test": "npm run contract:deps && npm run contract:scan && npm run contract:template",
|
|
49
|
+
"test:legacy": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.js",
|
|
50
|
+
"prepublishOnly": "npm run build"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"private": false,
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@napi-rs/cli": "^2.18.4",
|
|
58
|
+
"@types/bun": "latest",
|
|
59
|
+
"@types/node": "latest",
|
|
60
|
+
"prettier": "^3.7.4"
|
|
61
|
+
}
|
|
62
|
+
}
|
package/template.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
function normalizeManifestJson(manifestJson) {
|
|
2
|
+
return manifestJson.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function sanitizeImportSpecifier(specifier) {
|
|
6
|
+
return specifier
|
|
7
|
+
.replace(/\\/g, '\\\\')
|
|
8
|
+
.replace(/'/g, "\\'")
|
|
9
|
+
.replace(/\r\n/g, '')
|
|
10
|
+
.replace(/[\r\n]/g, '');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function renderRouterModule(opts) {
|
|
14
|
+
if (!opts || typeof opts !== 'object') {
|
|
15
|
+
throw new Error('renderRouterModule(opts) requires an options object');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { manifestJson, runtimeImport, coreImport } = opts;
|
|
19
|
+
|
|
20
|
+
if (typeof manifestJson !== 'string' || manifestJson.length === 0) {
|
|
21
|
+
throw new Error('renderRouterModule(opts) requires opts.manifestJson string');
|
|
22
|
+
}
|
|
23
|
+
if (typeof runtimeImport !== 'string' || runtimeImport.length === 0) {
|
|
24
|
+
throw new Error('renderRouterModule(opts) requires opts.runtimeImport string');
|
|
25
|
+
}
|
|
26
|
+
if (typeof coreImport !== 'string' || coreImport.length === 0) {
|
|
27
|
+
throw new Error('renderRouterModule(opts) requires opts.coreImport string');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const manifest = normalizeManifestJson(manifestJson);
|
|
31
|
+
const runtimeSpec = sanitizeImportSpecifier(runtimeImport);
|
|
32
|
+
const coreSpec = sanitizeImportSpecifier(coreImport);
|
|
33
|
+
|
|
34
|
+
const lines = [
|
|
35
|
+
`import { hydrate as __zenithHydrate } from '${runtimeSpec}';`,
|
|
36
|
+
`import { zenOnMount as __zenithOnMount } from '${coreSpec}';`,
|
|
37
|
+
'',
|
|
38
|
+
'void __zenithHydrate;',
|
|
39
|
+
'void __zenithOnMount;',
|
|
40
|
+
'',
|
|
41
|
+
`const __ZENITH_MANIFEST__ = ${manifest};`,
|
|
42
|
+
'',
|
|
43
|
+
'let activeCleanup = null;',
|
|
44
|
+
'let navigationToken = 0;',
|
|
45
|
+
'',
|
|
46
|
+
'function splitPath(path) {',
|
|
47
|
+
" return path.split('/').filter(Boolean);",
|
|
48
|
+
'}',
|
|
49
|
+
'',
|
|
50
|
+
'function normalizeCatchAll(segments) {',
|
|
51
|
+
" return segments.filter(Boolean).join('/');",
|
|
52
|
+
'}',
|
|
53
|
+
'',
|
|
54
|
+
'function requiresServerReload(route) {',
|
|
55
|
+
' const routes = __ZENITH_MANIFEST__.server_routes || __ZENITH_MANIFEST__.serverRoutes || [];',
|
|
56
|
+
" return Array.isArray(routes) && routes.includes(route);",
|
|
57
|
+
'}',
|
|
58
|
+
'',
|
|
59
|
+
'function segmentWeight(segment) {',
|
|
60
|
+
' if (!segment) return 0;',
|
|
61
|
+
" if (segment.startsWith('*')) return 1;",
|
|
62
|
+
" if (segment.startsWith(':')) return 2;",
|
|
63
|
+
' return 3;',
|
|
64
|
+
'}',
|
|
65
|
+
'',
|
|
66
|
+
'function routeClass(segments) {',
|
|
67
|
+
' let hasParam = false;',
|
|
68
|
+
' let hasCatchAll = false;',
|
|
69
|
+
' for (let i = 0; i < segments.length; i++) {',
|
|
70
|
+
' const segment = segments[i];',
|
|
71
|
+
" if (segment.startsWith('*')) {",
|
|
72
|
+
' hasCatchAll = true;',
|
|
73
|
+
" } else if (segment.startsWith(':')) {",
|
|
74
|
+
' hasParam = true;',
|
|
75
|
+
' }',
|
|
76
|
+
' }',
|
|
77
|
+
' if (!hasParam && !hasCatchAll) return 3;',
|
|
78
|
+
' if (hasCatchAll) return 1;',
|
|
79
|
+
' return 2;',
|
|
80
|
+
'}',
|
|
81
|
+
'',
|
|
82
|
+
'function compareRouteSpecificity(a, b) {',
|
|
83
|
+
" if (a === '/' && b !== '/') return -1;",
|
|
84
|
+
" if (b === '/' && a !== '/') return 1;",
|
|
85
|
+
' const aSegs = splitPath(a);',
|
|
86
|
+
' const bSegs = splitPath(b);',
|
|
87
|
+
' const aClass = routeClass(aSegs);',
|
|
88
|
+
' const bClass = routeClass(bSegs);',
|
|
89
|
+
' if (aClass !== bClass) {',
|
|
90
|
+
' return bClass - aClass;',
|
|
91
|
+
' }',
|
|
92
|
+
' const max = Math.min(aSegs.length, bSegs.length);',
|
|
93
|
+
' for (let i = 0; i < max; i++) {',
|
|
94
|
+
' const aWeight = segmentWeight(aSegs[i]);',
|
|
95
|
+
' const bWeight = segmentWeight(bSegs[i]);',
|
|
96
|
+
' if (aWeight !== bWeight) {',
|
|
97
|
+
' return bWeight - aWeight;',
|
|
98
|
+
' }',
|
|
99
|
+
' }',
|
|
100
|
+
' if (aSegs.length !== bSegs.length) {',
|
|
101
|
+
' return bSegs.length - aSegs.length;',
|
|
102
|
+
' }',
|
|
103
|
+
' return a.localeCompare(b);',
|
|
104
|
+
'}',
|
|
105
|
+
'',
|
|
106
|
+
'function resolveRoute(pathname) {',
|
|
107
|
+
' if (__ZENITH_MANIFEST__.chunks[pathname]) {',
|
|
108
|
+
' return { route: pathname, params: {} };',
|
|
109
|
+
' }',
|
|
110
|
+
'',
|
|
111
|
+
' const pathnameSegments = splitPath(pathname);',
|
|
112
|
+
' const routes = Object.keys(__ZENITH_MANIFEST__.chunks).sort(compareRouteSpecificity);',
|
|
113
|
+
'',
|
|
114
|
+
' for (let i = 0; i < routes.length; i++) {',
|
|
115
|
+
' const route = routes[i];',
|
|
116
|
+
' const routeSegments = splitPath(route);',
|
|
117
|
+
' const params = Object.create(null);',
|
|
118
|
+
' let routeIndex = 0;',
|
|
119
|
+
' let pathIndex = 0;',
|
|
120
|
+
' let matched = true;',
|
|
121
|
+
'',
|
|
122
|
+
' while (routeIndex < routeSegments.length) {',
|
|
123
|
+
' const routeSegment = routeSegments[routeIndex];',
|
|
124
|
+
" if (routeSegment.startsWith('*')) {",
|
|
125
|
+
" const optionalCatchAll = routeSegment.endsWith('?');",
|
|
126
|
+
" const key = optionalCatchAll ? routeSegment.slice(1, -1) : routeSegment.slice(1);",
|
|
127
|
+
' if (routeIndex !== routeSegments.length - 1) {',
|
|
128
|
+
' matched = false;',
|
|
129
|
+
' break;',
|
|
130
|
+
' }',
|
|
131
|
+
' const rest = pathnameSegments.slice(pathIndex);',
|
|
132
|
+
' const rootRequiredCatchAll = !optionalCatchAll && routeSegments.length === 1;',
|
|
133
|
+
' if (rest.length === 0 && !optionalCatchAll && !rootRequiredCatchAll) {',
|
|
134
|
+
' matched = false;',
|
|
135
|
+
' break;',
|
|
136
|
+
' }',
|
|
137
|
+
' params[key] = normalizeCatchAll(rest);',
|
|
138
|
+
' pathIndex = pathnameSegments.length;',
|
|
139
|
+
' routeIndex = routeSegments.length;',
|
|
140
|
+
' continue;',
|
|
141
|
+
' }',
|
|
142
|
+
' if (pathIndex >= pathnameSegments.length) {',
|
|
143
|
+
' matched = false;',
|
|
144
|
+
' break;',
|
|
145
|
+
' }',
|
|
146
|
+
' const pathnameSegment = pathnameSegments[pathIndex];',
|
|
147
|
+
" if (routeSegment.startsWith(':')) {",
|
|
148
|
+
' params[routeSegment.slice(1)] = pathnameSegment;',
|
|
149
|
+
' } else if (routeSegment !== pathnameSegment) {',
|
|
150
|
+
' matched = false;',
|
|
151
|
+
' break;',
|
|
152
|
+
' }',
|
|
153
|
+
' routeIndex += 1;',
|
|
154
|
+
' pathIndex += 1;',
|
|
155
|
+
' }',
|
|
156
|
+
'',
|
|
157
|
+
' if (matched && routeIndex === routeSegments.length && pathIndex === pathnameSegments.length) {',
|
|
158
|
+
' return { route, params: { ...params } };',
|
|
159
|
+
' }',
|
|
160
|
+
' }',
|
|
161
|
+
'',
|
|
162
|
+
' return null;',
|
|
163
|
+
'}',
|
|
164
|
+
'',
|
|
165
|
+
'function teardownRuntime() {',
|
|
166
|
+
" if (typeof activeCleanup === 'function') {",
|
|
167
|
+
' activeCleanup();',
|
|
168
|
+
' activeCleanup = null;',
|
|
169
|
+
' }',
|
|
170
|
+
'}',
|
|
171
|
+
'',
|
|
172
|
+
'async function mountRoute(route, params, token) {',
|
|
173
|
+
' if (token !== navigationToken) return;',
|
|
174
|
+
' teardownRuntime();',
|
|
175
|
+
' if (token !== navigationToken) return;',
|
|
176
|
+
'',
|
|
177
|
+
' const pageModule = await import(__ZENITH_MANIFEST__.chunks[route]);',
|
|
178
|
+
' if (token !== navigationToken) return;',
|
|
179
|
+
'',
|
|
180
|
+
' const mountFn = pageModule.__zenith_mount || pageModule.default;',
|
|
181
|
+
" if (typeof mountFn === 'function') {",
|
|
182
|
+
' const cleanup = mountFn(document, params);',
|
|
183
|
+
" activeCleanup = typeof cleanup === 'function' ? cleanup : null;",
|
|
184
|
+
' }',
|
|
185
|
+
'}',
|
|
186
|
+
'',
|
|
187
|
+
'async function navigate(pathname, url) {',
|
|
188
|
+
' const next = resolveRoute(pathname);',
|
|
189
|
+
' if (!next) return false;',
|
|
190
|
+
'',
|
|
191
|
+
' navigationToken += 1;',
|
|
192
|
+
' const token = navigationToken;',
|
|
193
|
+
'',
|
|
194
|
+
' await mountRoute(next.route, next.params, token);',
|
|
195
|
+
' if (token !== navigationToken) return false;',
|
|
196
|
+
" if (typeof window.scrollTo === 'function') {",
|
|
197
|
+
' window.scrollTo(0, 0);',
|
|
198
|
+
' }',
|
|
199
|
+
' return true;',
|
|
200
|
+
'}',
|
|
201
|
+
'',
|
|
202
|
+
'function handleNavigationFailure(error, url) {',
|
|
203
|
+
" console.error('[Zenith Router] navigation failed', error);",
|
|
204
|
+
" if (url && typeof url.href === 'string') {",
|
|
205
|
+
' window.location.assign(url.href);',
|
|
206
|
+
' }',
|
|
207
|
+
'}',
|
|
208
|
+
'',
|
|
209
|
+
'function isInternalLink(anchor) {',
|
|
210
|
+
" if (!anchor || anchor.target || anchor.hasAttribute('download')) return false;",
|
|
211
|
+
" const href = anchor.getAttribute('href');",
|
|
212
|
+
" if (!href || href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:')) return false;",
|
|
213
|
+
' const url = new URL(anchor.href, window.location.href);',
|
|
214
|
+
' return url.origin === window.location.origin;',
|
|
215
|
+
'}',
|
|
216
|
+
'',
|
|
217
|
+
'function start() {',
|
|
218
|
+
" if (typeof history === 'object' && history && 'scrollRestoration' in history) {",
|
|
219
|
+
" history.scrollRestoration = 'manual';",
|
|
220
|
+
' }',
|
|
221
|
+
'',
|
|
222
|
+
" document.addEventListener('click', function (event) {",
|
|
223
|
+
" if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;",
|
|
224
|
+
" const target = event.target && event.target.closest ? event.target.closest('a[data-zen-link]') : null;",
|
|
225
|
+
' if (!isInternalLink(target)) return;',
|
|
226
|
+
'',
|
|
227
|
+
' const url = new URL(target.href, window.location.href);',
|
|
228
|
+
' const hashOnlyNavigation =',
|
|
229
|
+
' url.hash &&',
|
|
230
|
+
' url.pathname === window.location.pathname &&',
|
|
231
|
+
' url.search === window.location.search;',
|
|
232
|
+
' if (hashOnlyNavigation) return;',
|
|
233
|
+
' const nextPath = url.pathname;',
|
|
234
|
+
" if (nextPath === window.location.pathname && url.search === window.location.search && url.hash === window.location.hash) return;",
|
|
235
|
+
' const resolved = resolveRoute(nextPath);',
|
|
236
|
+
' if (!resolved) return;',
|
|
237
|
+
' if (requiresServerReload(resolved.route)) return;',
|
|
238
|
+
'',
|
|
239
|
+
' try {',
|
|
240
|
+
' window.location.assign(url.href);',
|
|
241
|
+
' } catch (e) {',
|
|
242
|
+
" console.error('[Zenith Router] click navigation failed, falling back to browser default', e);",
|
|
243
|
+
' }',
|
|
244
|
+
' });',
|
|
245
|
+
'',
|
|
246
|
+
'',
|
|
247
|
+
' navigate(window.location.pathname, null).catch(function (error) {',
|
|
248
|
+
" console.error('[Zenith Router] initial navigation failed', error);",
|
|
249
|
+
' });',
|
|
250
|
+
'}',
|
|
251
|
+
'',
|
|
252
|
+
"if (document.readyState === 'loading') {",
|
|
253
|
+
" document.addEventListener('DOMContentLoaded', start, { once: true });",
|
|
254
|
+
'} else {',
|
|
255
|
+
' start();',
|
|
256
|
+
'}'
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
return `${lines.join('\n')}\n`;
|
|
260
|
+
}
|