@weave-framework/router 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aidas Josas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * File-based routing — turn a directory of page files into a `Route[]` tree
3
+ * (the Next/Nuxt/SvelteKit convention). Pure + zero-dep so it is fully testable:
4
+ * {@link fileToRoutes} maps a list of file specifiers to a manifest, and
5
+ * {@link emitRoutesModule} serialises that manifest into an importable module.
6
+ * The CLI supplies the actual directory scan; everything here is string work.
7
+ *
8
+ * Convention (per directory level):
9
+ * - `index.*` → the index route (`path: ''`)
10
+ * - `about.*` → `path: 'about'`
11
+ * - `[id].*` → `path: ':id'` (dynamic segment)
12
+ * - `[...rest].*` → `path: '*'` (catch-all; honoured at the top level)
13
+ * - `_layout.*` → the folder's layout component; its folder becomes a nested
14
+ * route whose `children` are that folder's routes
15
+ * - a folder WITHOUT a `_layout` is flattened: its routes get the folder name
16
+ * prefixed onto their paths (no wrapper route)
17
+ */
18
+ /** A route in the generated manifest. `file` is the page's source specifier. */
19
+ export interface FileRoute {
20
+ path: string;
21
+ /** Page/layout source specifier (the emitter turns this into a component import). */
22
+ file?: string;
23
+ children?: FileRoute[];
24
+ }
25
+ /** Map a flat list of page-file specifiers to a nested {@link FileRoute} manifest. */
26
+ export declare function fileToRoutes(files: string[]): FileRoute[];
27
+ /** Options for {@link emitRoutesModule}. */
28
+ export interface EmitRoutesOptions {
29
+ /** Code-split every page via `lazy(() => import(...))` instead of a static import. */
30
+ lazy?: boolean;
31
+ /** Where `lazy` is imported from (default `@weave-framework/runtime/dom`). */
32
+ runtimeImport?: string;
33
+ /** Prefix prepended to each `file` to form the import specifier (default `./`). */
34
+ importPrefix?: string;
35
+ }
36
+ /** Serialise a manifest into an importable ES module exporting `const routes: Route[]`. */
37
+ export declare function emitRoutesModule(routes: FileRoute[], opts?: EmitRoutesOptions): string;
38
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;CACxB;AAoFD,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAYzD;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,sFAAsF;IACtF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,IAAI,GAAE,iBAAsB,GAAG,MAAM,CAqC1F"}
package/dist/files.js ADDED
@@ -0,0 +1,145 @@
1
+ /**
2
+ * File-based routing — turn a directory of page files into a `Route[]` tree
3
+ * (the Next/Nuxt/SvelteKit convention). Pure + zero-dep so it is fully testable:
4
+ * {@link fileToRoutes} maps a list of file specifiers to a manifest, and
5
+ * {@link emitRoutesModule} serialises that manifest into an importable module.
6
+ * The CLI supplies the actual directory scan; everything here is string work.
7
+ *
8
+ * Convention (per directory level):
9
+ * - `index.*` → the index route (`path: ''`)
10
+ * - `about.*` → `path: 'about'`
11
+ * - `[id].*` → `path: ':id'` (dynamic segment)
12
+ * - `[...rest].*` → `path: '*'` (catch-all; honoured at the top level)
13
+ * - `_layout.*` → the folder's layout component; its folder becomes a nested
14
+ * route whose `children` are that folder's routes
15
+ * - a folder WITHOUT a `_layout` is flattened: its routes get the folder name
16
+ * prefixed onto their paths (no wrapper route)
17
+ */
18
+ const EXT = /\.(weave|tsx?|jsx?)$/;
19
+ const baseName = (file) => {
20
+ const slash = file.lastIndexOf('/');
21
+ return file.slice(slash + 1).replace(EXT, '');
22
+ };
23
+ /** Filename (no extension) → a route path segment. */
24
+ function segment(name) {
25
+ if (name === 'index')
26
+ return '';
27
+ const catchAll = /^\[\.\.\.(.+)]$/.exec(name);
28
+ if (catchAll)
29
+ return '*';
30
+ const dynamic = /^\[(.+)]$/.exec(name);
31
+ if (dynamic)
32
+ return `:${dynamic[1]}`;
33
+ return name;
34
+ }
35
+ const emptyTree = () => ({ files: {}, dirs: {} });
36
+ /** Per-segment specificity: a static segment (0) wins over `:param` (1) over `*` (2). */
37
+ function segSpecificity(seg) {
38
+ if (seg === '*' || seg.startsWith('*'))
39
+ return 2;
40
+ if (seg.startsWith(':'))
41
+ return 1;
42
+ return 0;
43
+ }
44
+ /**
45
+ * Compare two route paths so the more specific one sorts first — segment by segment,
46
+ * static before dynamic before catch-all. This must be segment-aware (not first-char)
47
+ * because routes are flattened into one list with full multi-segment paths: e.g.
48
+ * `reference/config` must precede `reference/:pkg` even though ':' < 'c' lexically.
49
+ */
50
+ function compareRoutes(a, b) {
51
+ const as = a.split('/');
52
+ const bs = b.split('/');
53
+ const len = Math.max(as.length, bs.length);
54
+ for (let i = 0; i < len; i++) {
55
+ const sa = as[i] ?? '';
56
+ const sb = bs[i] ?? '';
57
+ const da = segSpecificity(sa);
58
+ const db = segSpecificity(sb);
59
+ if (da !== db)
60
+ return da - db; // more static segment wins at the first divergence
61
+ if (sa !== sb)
62
+ return sa.localeCompare(sb);
63
+ }
64
+ return 0;
65
+ }
66
+ function joinPath(dir, child) {
67
+ return child === '' ? dir : `${dir}/${child}`;
68
+ }
69
+ function convert(tree) {
70
+ const routes = [];
71
+ for (const [name, file] of Object.entries(tree.files)) {
72
+ if (name === '_layout')
73
+ continue; // a layout is consumed by its folder, not a route here
74
+ routes.push({ path: segment(name), file });
75
+ }
76
+ for (const [dir, sub] of Object.entries(tree.dirs)) {
77
+ const childRoutes = convert(sub);
78
+ const layout = sub.files['_layout'];
79
+ if (layout) {
80
+ routes.push({ path: dir, file: layout, children: sortRoutes(childRoutes) });
81
+ }
82
+ else {
83
+ // no layout → flatten, prefixing the folder name onto each child path
84
+ for (const r of childRoutes)
85
+ routes.push({ ...r, path: joinPath(dir, r.path) });
86
+ }
87
+ }
88
+ return sortRoutes(routes);
89
+ }
90
+ function sortRoutes(routes) {
91
+ return routes.sort((a, b) => compareRoutes(a.path, b.path));
92
+ }
93
+ /** Map a flat list of page-file specifiers to a nested {@link FileRoute} manifest. */
94
+ export function fileToRoutes(files) {
95
+ const root = emptyTree();
96
+ for (const file of files) {
97
+ const parts = file.split('/');
98
+ let node = root;
99
+ for (let i = 0; i < parts.length - 1; i++) {
100
+ const dir = parts[i];
101
+ node = node.dirs[dir] ??= emptyTree();
102
+ }
103
+ node.files[baseName(parts[parts.length - 1])] = file;
104
+ }
105
+ return convert(root);
106
+ }
107
+ /** Serialise a manifest into an importable ES module exporting `const routes: Route[]`. */
108
+ export function emitRoutesModule(routes, opts = {}) {
109
+ const imports = [];
110
+ let n = 0;
111
+ const prefix = opts.importPrefix ?? './';
112
+ const spec = (file) => {
113
+ const withPrefix = /^[./]/.test(file) ? file : prefix + file;
114
+ // Drop a TS/JS extension so the import resolves under both esbuild and `tsc`
115
+ // (importing a literal `.ts` errors without `allowImportingTsExtensions`).
116
+ // Keep `.weave` — the SFC loader needs the explicit extension to resolve.
117
+ return JSON.stringify(withPrefix.replace(/\.[mc]?[jt]sx?$/, ''));
118
+ };
119
+ const componentField = (file) => {
120
+ if (opts.lazy)
121
+ return `component: lazy(() => import(${spec(file)}))`;
122
+ const id = `Page${n++}`;
123
+ imports.push(`import ${id} from ${spec(file)};`);
124
+ return `component: ${id}`;
125
+ };
126
+ const serialize = (list, indent) => {
127
+ const inner = indent + ' ';
128
+ const items = list.map((r) => {
129
+ const fields = [`path: ${JSON.stringify(r.path)}`];
130
+ if (r.file)
131
+ fields.push(componentField(r.file));
132
+ if (r.children && r.children.length) {
133
+ fields.push(`children: ${serialize(r.children, inner)}`);
134
+ }
135
+ return `${inner}{ ${fields.join(', ')} }`;
136
+ });
137
+ return `[\n${items.join(',\n')}\n${indent}]`;
138
+ };
139
+ const body = serialize(routes, '');
140
+ const header = opts.lazy
141
+ ? `import { lazy } from ${JSON.stringify(opts.runtimeImport ?? '@weave-framework/runtime/dom')};\n`
142
+ : '';
143
+ return `${header}${imports.join('\n')}${imports.length ? '\n' : ''}\nexport const routes = ${body};\n`;
144
+ }
145
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * @weave-framework/router — the official client router. Built in, not a third-party bolt-on.
3
+ * Zero third-party dependencies (only `@weave-framework/runtime`).
4
+ *
5
+ * History-based and signal-driven: the current path and query are signals, so any
6
+ * view that reads them updates surgically on navigation. Routes are an ordered tree
7
+ * of `{ path, component?, guard?, redirect?, children? }` objects (`'*'` = catch-all
8
+ * fallback), supporting path params (`/user/:id`), query parsing (`?tab=x`), **sync
9
+ * guards** (read auth signals; return `true`/`false`/a redirect path), static
10
+ * `redirect`s, and **nested routes** (a parent layout renders a nested `<RouterView>`).
11
+ *
12
+ * Matching produces a *chain* of matches (layout → … → leaf). The top `<RouterView>`
13
+ * renders the chain's first component; each nested `<RouterView>` renders the next,
14
+ * discovering its depth + the router through **provide/inject** (no prop drilling).
15
+ *
16
+ * Guards are synchronous by design: they run inside the reactive resolution and read
17
+ * signals (e.g. `isAuthed()`), so a route re-resolves automatically when auth changes.
18
+ * Async data loading belongs in the component via `@weave-framework/data`.
19
+ */
20
+ import { type Component } from '@weave-framework/runtime/dom';
21
+ export type RouteParams = Record<string, string>;
22
+ /** Context handed to a guard: the resolved path, accumulated path params, and query. */
23
+ export interface RouteContext {
24
+ path: string;
25
+ params: RouteParams;
26
+ query: RouteParams;
27
+ }
28
+ /**
29
+ * A route guard. Runs synchronously during matching and may read signals.
30
+ * Return `true` to allow, `false` to block (→ fallback), or a path string to redirect.
31
+ */
32
+ export type Guard = (ctx: RouteContext) => boolean | string;
33
+ /** A single route definition. `path: '*'` is the catch-all (404) fallback. */
34
+ export interface Route {
35
+ /** Path pattern: `/`, `/users`, `/user/:id`, `''` (index child), or `'*'` (fallback). */
36
+ path: string;
37
+ /** Component to render when matched (a layout, if it has `children`). */
38
+ component?: Component;
39
+ /** Sync guard: `true` allows, `false` blocks (→ fallback), a string redirects. */
40
+ guard?: Guard;
41
+ /** Static redirect target (pathname). When matched, resolve to this path instead. */
42
+ redirect?: string;
43
+ /** Nested routes, matched against the path remainder under this route. */
44
+ children?: Route[];
45
+ }
46
+ /**
47
+ * Set the base path the app is served under (e.g. `/weave` for a project page at
48
+ * `user.github.io/weave/`). Call once before the first render. Default is root.
49
+ */
50
+ export declare function setBasename(base: string): void;
51
+ /** What a navigation was: a `navigate()` push, a back/forward `pop`, or a `replace`. */
52
+ export type NavType = 'push' | 'pop' | 'replace';
53
+ /** Payload handed to every {@link afterEach} hook after a navigation settles. */
54
+ export interface NavInfo {
55
+ path: string;
56
+ search: string;
57
+ hash: string;
58
+ type: NavType;
59
+ }
60
+ type AfterHook = (nav: NavInfo) => void;
61
+ /**
62
+ * Register a callback that runs after every navigation (push / pop / replace) —
63
+ * the place for document-title updates, analytics, focus management, etc. Returns
64
+ * an unsubscribe function.
65
+ */
66
+ export declare function afterEach(fn: AfterHook): () => void;
67
+ /** Toggle Weave's built-in scroll handling (top-on-push, `#fragment`, restore-on-pop). */
68
+ export declare function setScrollHandling(on: boolean): void;
69
+ /** The reactive current pathname (read-only). */
70
+ export declare const currentPath: () => string;
71
+ /** The reactive current query params (read-only). */
72
+ export declare const currentQuery: () => RouteParams;
73
+ /** Programmatic navigation (pushes history). Resilient if the env blocks pushState. */
74
+ export declare function navigate(to: string): void;
75
+ /** Go back one history entry (the `popstate` listener syncs the path). */
76
+ export declare function back(): void;
77
+ /** A reactive match: a component + the path params accumulated down to its depth. */
78
+ export interface Match {
79
+ view: Component;
80
+ params: RouteParams;
81
+ }
82
+ export interface Router {
83
+ /** The match at `depth` in the resolved chain (default 0 — the top component), or null. */
84
+ matched: (depth?: number) => Match | null;
85
+ /** The full resolved chain (layout → … → leaf). */
86
+ chain: () => Match[];
87
+ /** Accumulated path params at `depth` (default: the leaf — i.e. all params). */
88
+ params: (depth?: number) => RouteParams;
89
+ /** The current query params (reactive). */
90
+ query: () => RouteParams;
91
+ /** Canonical pathname the URL should sync to after a guard/redirect, or null. */
92
+ redirectTo: () => string | null;
93
+ /** Warm a path's lazy route chunk(s) ahead of navigation (Link prefetch). */
94
+ preload: (to: string) => void;
95
+ }
96
+ /**
97
+ * Build a router from an ordered `Route[]` tree (`path: '*'` = catch-all fallback).
98
+ * Resolution is a single reactive computation producing a match *chain*; redirect and
99
+ * guard-redirect hops are followed (capped at 16 to break loops). Place the output with
100
+ * a top `<RouterView router={r}/>` and a nested `<RouterView/>` inside each layout.
101
+ */
102
+ export declare function createRouter(routes: Route[], options?: {
103
+ basename?: string;
104
+ }): Router;
105
+ /** Warm a path's lazy route chunk(s) via the active router (no-op if none / not lazy). */
106
+ export declare function prefetch(to: string): void;
107
+ /**
108
+ * Router outlet: renders the matched component at its depth in the chain. The top
109
+ * outlet takes `router` as a prop and renders depth 0; a nested `<RouterView/>` written
110
+ * inside a layout discovers the router + its depth via context. A `display:contents`
111
+ * host keeps it layout-neutral. A stable render thunk per component means a param-only
112
+ * change updates `params` in place instead of remounting; switching routes swaps the
113
+ * component. The top outlet also syncs the address bar after a guard/redirect.
114
+ *
115
+ * Pass `transition` (a `TransitionFn`, e.g. `fade`) to animate route changes: the
116
+ * entering view is wrapped in a real host element that plays the intro on swap — so
117
+ * it works even for `lazy()` routes (whose own host is `display:contents`). Author a
118
+ * page-root `out:` if you also want a leave animation.
119
+ *
120
+ * Usage: `<RouterView router={r}/>` at the top, `<RouterView/>` inside each layout.
121
+ */
122
+ export declare const RouterView: Component;
123
+ /**
124
+ * Client-side anchor: navigates instead of reloading (plain clicks only — lets
125
+ * ctrl/cmd/middle-click open a new tab as usual).
126
+ *
127
+ * Active state (reactive on the current path): when the link's target matches the
128
+ * URL it gets `aria-current="page"` automatically, and — if you name one via
129
+ * `activeClass` — an active CSS class. Matching is prefix-by-segment so a parent
130
+ * link (`/users`) stays active on a child (`/users/42`); pass `exact` to require an
131
+ * exact match. A link to `/` is only ever active at exactly `/`.
132
+ *
133
+ * Usage in a template: `<Link to="/about" activeClass="active">About</Link>`.
134
+ */
135
+ export declare const Link: Component;
136
+ export { fileToRoutes, emitRoutesModule, type FileRoute, type EmitRoutesOptions, } from './files.js';
137
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,445 @@
1
+ /**
2
+ * @weave-framework/router — the official client router. Built in, not a third-party bolt-on.
3
+ * Zero third-party dependencies (only `@weave-framework/runtime`).
4
+ *
5
+ * History-based and signal-driven: the current path and query are signals, so any
6
+ * view that reads them updates surgically on navigation. Routes are an ordered tree
7
+ * of `{ path, component?, guard?, redirect?, children? }` objects (`'*'` = catch-all
8
+ * fallback), supporting path params (`/user/:id`), query parsing (`?tab=x`), **sync
9
+ * guards** (read auth signals; return `true`/`false`/a redirect path), static
10
+ * `redirect`s, and **nested routes** (a parent layout renders a nested `<RouterView>`).
11
+ *
12
+ * Matching produces a *chain* of matches (layout → … → leaf). The top `<RouterView>`
13
+ * renders the chain's first component; each nested `<RouterView>` renders the next,
14
+ * discovering its depth + the router through **provide/inject** (no prop drilling).
15
+ *
16
+ * Guards are synchronous by design: they run inside the reactive resolution and read
17
+ * signals (e.g. `isAuthed()`), so a route re-resolves automatically when auth changes.
18
+ * Async data loading belongs in the component via `@weave-framework/data`.
19
+ */
20
+ import { signal, computed, effect, batch, getOwner, createContext, provide, inject } from '@weave-framework/runtime';
21
+ import { ifBlock, transition } from '@weave-framework/runtime/dom';
22
+ /* ──────────── base path (for hosting under a sub-path, e.g. GitHub Pages) ──────────── */
23
+ // All public paths (route patterns, navigate(), Link `to`, currentPath()) are
24
+ // "internal" — written as if the app were at the origin root. `basename` is the
25
+ // prefix the app is actually served under (default '' = root). It's stripped when
26
+ // reading location and re-added when writing history, so nothing else changes.
27
+ let basename = '';
28
+ /** Normalize a base: strip a trailing slash; treat '' / '/' as "no base". */
29
+ function normalizeBase(b) {
30
+ const t = b.replace(/\/+$/, '');
31
+ return t === '' || t === '/' ? '' : t;
32
+ }
33
+ /** location.pathname → internal path (drop the basename prefix). */
34
+ function stripBase(pathname) {
35
+ if (basename && (pathname === basename || pathname.startsWith(basename + '/'))) {
36
+ const rest = pathname.slice(basename.length);
37
+ return rest === '' ? '/' : rest;
38
+ }
39
+ return pathname || '/';
40
+ }
41
+ /** Internal path → external URL path (prepend the basename). */
42
+ function withBase(p) {
43
+ if (!basename)
44
+ return p;
45
+ return basename + (p.startsWith('/') ? p : '/' + p);
46
+ }
47
+ /**
48
+ * Set the base path the app is served under (e.g. `/weave` for a project page at
49
+ * `user.github.io/weave/`). Call once before the first render. Default is root.
50
+ */
51
+ export function setBasename(base) {
52
+ basename = normalizeBase(base);
53
+ if (typeof location !== 'undefined')
54
+ path.set(stripBase(location.pathname));
55
+ }
56
+ const path = signal(typeof location !== 'undefined' ? stripBase(location.pathname) : '/');
57
+ const search = signal(typeof location !== 'undefined' ? location.search : '');
58
+ const afterHooks = new Set();
59
+ /**
60
+ * Register a callback that runs after every navigation (push / pop / replace) —
61
+ * the place for document-title updates, analytics, focus management, etc. Returns
62
+ * an unsubscribe function.
63
+ */
64
+ export function afterEach(fn) {
65
+ afterHooks.add(fn);
66
+ return () => void afterHooks.delete(fn);
67
+ }
68
+ // Built-in scroll handling (on by default in the browser): scroll to top on a new
69
+ // navigation, to a `#fragment` element if the URL has one, and restore the saved
70
+ // position on back/forward. Apps that manage scroll themselves opt out.
71
+ let scrollManaged = typeof window !== 'undefined';
72
+ /** Toggle Weave's built-in scroll handling (top-on-push, `#fragment`, restore-on-pop). */
73
+ export function setScrollHandling(on) {
74
+ scrollManaged = on;
75
+ }
76
+ const scrollPositions = new Map();
77
+ let posSeq = 0;
78
+ let curPos = 0;
79
+ // Own scroll restoration so the browser's native one doesn't fight ours.
80
+ if (typeof history !== 'undefined' && 'scrollRestoration' in history) {
81
+ try {
82
+ history.scrollRestoration = 'manual';
83
+ }
84
+ catch {
85
+ /* some embedded contexts disallow it */
86
+ }
87
+ }
88
+ /** Fire the after-hooks, then apply built-in scroll (in a microtask, after the swap). */
89
+ function runAfter(nav) {
90
+ for (const fn of afterHooks)
91
+ fn(nav);
92
+ if (!scrollManaged || typeof window === 'undefined')
93
+ return;
94
+ const { type, hash } = nav;
95
+ queueMicrotask(() => {
96
+ if (type === 'pop') {
97
+ window.scrollTo(0, scrollPositions.get(curPos) ?? 0);
98
+ return;
99
+ }
100
+ if (hash) {
101
+ const el = document.getElementById(hash.slice(1));
102
+ if (el) {
103
+ el.scrollIntoView();
104
+ return;
105
+ }
106
+ }
107
+ window.scrollTo(0, 0);
108
+ });
109
+ }
110
+ if (typeof window !== 'undefined') {
111
+ window.addEventListener('popstate', (e) => {
112
+ const st = e.state;
113
+ curPos = st && typeof st.__wpos === 'number' ? st.__wpos : 0;
114
+ const internal = stripBase(location.pathname);
115
+ batch(() => {
116
+ path.set(internal);
117
+ search.set(location.search);
118
+ });
119
+ runAfter({ path: internal, search: location.search, hash: location.hash, type: 'pop' });
120
+ });
121
+ }
122
+ /** The reactive current pathname (read-only). */
123
+ export const currentPath = () => path();
124
+ /** Parsed query string as a reactive `{ key: value }` map (last value wins on repeats). */
125
+ const queryMap = computed(() => {
126
+ const out = {};
127
+ const s = search();
128
+ if (s)
129
+ new URLSearchParams(s).forEach((v, k) => (out[k] = v));
130
+ return out;
131
+ });
132
+ /** The reactive current query params (read-only). */
133
+ export const currentQuery = () => queryMap();
134
+ /** Programmatic navigation (pushes history). Resilient if the env blocks pushState. */
135
+ export function navigate(to) {
136
+ const hash = to.includes('#') ? to.slice(to.indexOf('#')) : '';
137
+ const noHash = to.split('#')[0];
138
+ const qI = noHash.indexOf('?');
139
+ const nextPath = qI === -1 ? noHash : noHash.slice(0, qI);
140
+ const nextSearch = qI === -1 ? '' : noHash.slice(qI);
141
+ // A bare same-URL navigation is a no-op — unless there's a `#fragment` to scroll to.
142
+ if (nextPath === path.peek() && nextSearch === search.peek() && !hash)
143
+ return;
144
+ // Remember where we are before leaving, so back/forward can restore it.
145
+ if (typeof window !== 'undefined')
146
+ scrollPositions.set(curPos, window.scrollY);
147
+ const nextPos = ++posSeq;
148
+ try {
149
+ // Write the externally-visible URL (basename-prefixed); signals stay internal.
150
+ history.pushState({ __wpos: nextPos }, '', withBase(nextPath) + nextSearch + hash);
151
+ curPos = nextPos;
152
+ }
153
+ catch {
154
+ /* non-navigable environment (tests, sandboxes) — the signals stay authoritative */
155
+ }
156
+ batch(() => {
157
+ path.set(nextPath);
158
+ search.set(nextSearch);
159
+ });
160
+ runAfter({ path: nextPath, search: nextSearch, hash, type: 'push' });
161
+ }
162
+ /** Go back one history entry (the `popstate` listener syncs the path). */
163
+ export function back() {
164
+ history.back();
165
+ }
166
+ const splitSegs = (s) => s.split('/').filter(Boolean);
167
+ function parsePattern(pattern) {
168
+ return splitSegs(pattern).map((s) => s.startsWith(':') ? { param: s.slice(1) } : { literal: s });
169
+ }
170
+ function compileRoutes(routes) {
171
+ return routes
172
+ .filter((r) => r.path !== '*')
173
+ .map((r) => ({
174
+ route: r,
175
+ segs: parsePattern(r.path),
176
+ children: r.children ? compileRoutes(r.children) : [],
177
+ }));
178
+ }
179
+ /** Match a pattern's segments against a leading run of the path; return params + remainder. */
180
+ function matchPrefix(segs, pathSegs) {
181
+ if (segs.length > pathSegs.length)
182
+ return null;
183
+ const params = {};
184
+ for (let i = 0; i < segs.length; i++) {
185
+ const s = segs[i];
186
+ if ('param' in s)
187
+ params[s.param] = decodeURIComponent(pathSegs[i]);
188
+ else if (s.literal !== pathSegs[i])
189
+ return null;
190
+ }
191
+ return { params, rest: pathSegs.slice(segs.length) };
192
+ }
193
+ /** Resolve one sibling level: first matching route → redirect / guard / descend / leaf. */
194
+ function resolveLevel(routes, pathSegs, query, fullPath, inherited) {
195
+ for (const c of routes) {
196
+ const m = matchPrefix(c.segs, pathSegs);
197
+ if (!m)
198
+ continue;
199
+ const params = { ...inherited, ...m.params };
200
+ if (c.route.redirect)
201
+ return { redirect: c.route.redirect };
202
+ const verdict = c.route.guard ? c.route.guard({ path: fullPath, params, query }) : true;
203
+ if (verdict === false)
204
+ return null; // blocked → caller falls back
205
+ if (typeof verdict === 'string')
206
+ return { redirect: verdict };
207
+ const here = { view: c.route.component, params };
208
+ if (m.rest.length === 0) {
209
+ // Path fully consumed: include an index child (`path: ''`) if one matches.
210
+ if (c.children.length) {
211
+ const idx = resolveLevel(c.children, [], query, fullPath, params);
212
+ if (idx && 'redirect' in idx)
213
+ return idx;
214
+ if (idx && 'chain' in idx)
215
+ return { chain: [here, ...idx.chain] };
216
+ }
217
+ return { chain: [here] };
218
+ }
219
+ // Segments remain: this route only matches if a child consumes the rest.
220
+ if (c.children.length) {
221
+ const sub = resolveLevel(c.children, m.rest, query, fullPath, params);
222
+ if (sub && 'redirect' in sub)
223
+ return sub;
224
+ if (sub && 'chain' in sub)
225
+ return { chain: [here, ...sub.chain] };
226
+ }
227
+ // No child matched the remainder → not a full match; try the next sibling.
228
+ }
229
+ return null;
230
+ }
231
+ /** The most recently created router — the target of the module-level {@link prefetch}. */
232
+ let activeRouter = null;
233
+ /**
234
+ * Build a router from an ordered `Route[]` tree (`path: '*'` = catch-all fallback).
235
+ * Resolution is a single reactive computation producing a match *chain*; redirect and
236
+ * guard-redirect hops are followed (capped at 16 to break loops). Place the output with
237
+ * a top `<RouterView router={r}/>` and a nested `<RouterView/>` inside each layout.
238
+ */
239
+ export function createRouter(routes, options) {
240
+ if (options?.basename !== undefined)
241
+ setBasename(options.basename);
242
+ const compiled = compileRoutes(routes);
243
+ const fallback = routes.find((r) => r.path === '*');
244
+ const fallbackChain = () => fallback?.component ? [{ view: fallback.component, params: {} }] : [];
245
+ const resolution = computed(() => {
246
+ const q = queryMap();
247
+ const start = path();
248
+ let p = start;
249
+ for (let hops = 0; hops < 16; hops++) {
250
+ const res = resolveLevel(compiled, splitSegs(p), q, p, {});
251
+ const synced = p !== start ? p : null;
252
+ if (res && 'redirect' in res) {
253
+ p = res.redirect.split('#')[0].split('?')[0];
254
+ continue;
255
+ }
256
+ if (res && 'chain' in res)
257
+ return { chain: res.chain, redirectTo: synced };
258
+ return { chain: fallbackChain(), redirectTo: synced };
259
+ }
260
+ return { chain: [], redirectTo: null }; // redirect loop — give up rather than spin
261
+ });
262
+ const chain = () => resolution().chain;
263
+ /** Non-reactive resolve of an arbitrary path → preload each chunk in its chain. */
264
+ const preload = (to) => {
265
+ let p = to.split('#')[0].split('?')[0];
266
+ for (let hops = 0; hops < 16; hops++) {
267
+ const res = resolveLevel(compiled, splitSegs(p), {}, p, {});
268
+ if (res && 'redirect' in res) {
269
+ p = res.redirect.split('#')[0].split('?')[0];
270
+ continue;
271
+ }
272
+ const ch = res && 'chain' in res ? res.chain : fallbackChain();
273
+ for (const m of ch)
274
+ m.view.preload?.();
275
+ return;
276
+ }
277
+ };
278
+ const router = {
279
+ chain,
280
+ matched: (depth = 0) => chain()[depth] ?? null,
281
+ params: (depth) => {
282
+ const ch = chain();
283
+ const i = depth ?? ch.length - 1;
284
+ return ch[i]?.params ?? {};
285
+ },
286
+ query: () => queryMap(),
287
+ redirectTo: () => resolution().redirectTo,
288
+ preload,
289
+ };
290
+ activeRouter = router; // most-recent router answers the module-level prefetch()
291
+ return router;
292
+ }
293
+ /** Warm a path's lazy route chunk(s) via the active router (no-op if none / not lazy). */
294
+ export function prefetch(to) {
295
+ activeRouter?.preload(to);
296
+ }
297
+ /** Carries the router + the next outlet's depth down the tree (set by each RouterView). */
298
+ const OutletContext = createContext(null);
299
+ /**
300
+ * Router outlet: renders the matched component at its depth in the chain. The top
301
+ * outlet takes `router` as a prop and renders depth 0; a nested `<RouterView/>` written
302
+ * inside a layout discovers the router + its depth via context. A `display:contents`
303
+ * host keeps it layout-neutral. A stable render thunk per component means a param-only
304
+ * change updates `params` in place instead of remounting; switching routes swaps the
305
+ * component. The top outlet also syncs the address bar after a guard/redirect.
306
+ *
307
+ * Pass `transition` (a `TransitionFn`, e.g. `fade`) to animate route changes: the
308
+ * entering view is wrapped in a real host element that plays the intro on swap — so
309
+ * it works even for `lazy()` routes (whose own host is `display:contents`). Author a
310
+ * page-root `out:` if you also want a leave animation.
311
+ *
312
+ * Usage: `<RouterView router={r}/>` at the top, `<RouterView/>` inside each layout.
313
+ */
314
+ export const RouterView = (props = {}) => {
315
+ const parentCtx = inject(OutletContext);
316
+ const router = props.router ?? parentCtx?.router;
317
+ const depth = parentCtx ? parentCtx.depth : 0;
318
+ const txFn = props.transition;
319
+ const txParams = props.transitionParams;
320
+ // Hand the router + the next depth to any nested outlet below us. Only when an owner
321
+ // scope exists (a directly-invoked RouterView in a test has none — and won't nest).
322
+ if (router && getOwner())
323
+ provide(OutletContext, { router, depth: depth + 1 });
324
+ const host = document.createElement('div');
325
+ host.style.display = 'contents';
326
+ const anchorNode = document.createComment('router');
327
+ host.appendChild(anchorNode);
328
+ // Only the top outlet syncs the URL on a guard/redirect (redirects bubble to the
329
+ // chain root regardless of depth). Owned by this outlet's scope; converges after
330
+ // navigating (resolution then lands on the target → redirectTo() is null).
331
+ if (router && depth === 0) {
332
+ effect(() => {
333
+ const to = router.redirectTo();
334
+ if (to !== null && to !== path.peek())
335
+ navigate(to);
336
+ });
337
+ }
338
+ const thunks = new Map();
339
+ ifBlock(anchorNode, () => {
340
+ const m = router?.matched(depth) ?? null;
341
+ if (!m)
342
+ return null;
343
+ let thunk = thunks.get(m.view);
344
+ if (!thunk) {
345
+ const view = m.view;
346
+ thunk = () => {
347
+ const node = view({
348
+ get params() {
349
+ return router.params(depth);
350
+ },
351
+ });
352
+ if (!txFn)
353
+ return node;
354
+ // Wrap in a real element so the intro plays even when the view's own root is
355
+ // `display:contents` (lazy host) or a fragment (multi-root template).
356
+ const wrap = document.createElement('div');
357
+ wrap.appendChild(node);
358
+ transition(wrap, txFn, txParams, 'in');
359
+ return wrap;
360
+ };
361
+ thunks.set(view, thunk);
362
+ }
363
+ return thunk;
364
+ });
365
+ return host;
366
+ };
367
+ /**
368
+ * Client-side anchor: navigates instead of reloading (plain clicks only — lets
369
+ * ctrl/cmd/middle-click open a new tab as usual).
370
+ *
371
+ * Active state (reactive on the current path): when the link's target matches the
372
+ * URL it gets `aria-current="page"` automatically, and — if you name one via
373
+ * `activeClass` — an active CSS class. Matching is prefix-by-segment so a parent
374
+ * link (`/users`) stays active on a child (`/users/42`); pass `exact` to require an
375
+ * exact match. A link to `/` is only ever active at exactly `/`.
376
+ *
377
+ * Usage in a template: `<Link to="/about" activeClass="active">About</Link>`.
378
+ */
379
+ export const Link = (props = {}, slots = {}) => {
380
+ const to = String(props.to ?? '/');
381
+ // prefetch defaults on: warm the target's lazy chunk on first hover/focus.
382
+ const wantsPrefetch = props.prefetch !== false;
383
+ const exact = props.exact === true;
384
+ const activeClass = typeof props.activeClass === 'string'
385
+ ? props.activeClass
386
+ : null;
387
+ const a = document.createElement('a');
388
+ // The visible href is basename-prefixed (so middle/ctrl-click + SSR are correct);
389
+ // navigation + active-matching use the internal `to`.
390
+ a.setAttribute('href', withBase(to));
391
+ // Forward any other props (class, id, aria-*, title, …) to the anchor, so a
392
+ // `<Link class="nav" aria-label="Home">` actually styles/labels its <a>. The
393
+ // router-owned props and any function/event props are skipped; read once.
394
+ for (const key in props) {
395
+ if (key === 'to' || key === 'prefetch' || key === 'exact' || key === 'activeClass')
396
+ continue;
397
+ const val = props[key];
398
+ if (val == null || val === false || typeof val === 'function')
399
+ continue;
400
+ a.setAttribute(key, val === true ? '' : String(val));
401
+ }
402
+ const kids = slots.default?.();
403
+ if (kids)
404
+ a.appendChild(kids);
405
+ // Reactive active state. The target is compared without query/hash; `/` is
406
+ // exact-only (else its prefix would match every path).
407
+ const target = to.split('#')[0].split('?')[0];
408
+ const isActive = (cur) => {
409
+ if (exact || target === '/')
410
+ return cur === target;
411
+ if (cur === target)
412
+ return true;
413
+ return cur.startsWith(target.endsWith('/') ? target : target + '/');
414
+ };
415
+ effect(() => {
416
+ const on = isActive(currentPath());
417
+ if (on)
418
+ a.setAttribute('aria-current', 'page');
419
+ else
420
+ a.removeAttribute('aria-current');
421
+ if (activeClass)
422
+ a.classList.toggle(activeClass, on);
423
+ });
424
+ a.addEventListener('click', (e) => {
425
+ const me = e;
426
+ if (me.metaKey || me.ctrlKey || me.shiftKey || me.button !== 0)
427
+ return;
428
+ e.preventDefault();
429
+ navigate(to);
430
+ });
431
+ if (wantsPrefetch) {
432
+ let warmed = false;
433
+ const warm = () => {
434
+ if (warmed)
435
+ return;
436
+ warmed = true;
437
+ prefetch(to);
438
+ };
439
+ a.addEventListener('pointerenter', warm);
440
+ a.addEventListener('focusin', warm);
441
+ }
442
+ return a;
443
+ };
444
+ export { fileToRoutes, emitRoutesModule, } from './files.js';
445
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@weave-framework/router",
3
+ "version": "0.2.0",
4
+ "description": "Weave official client router — signal-driven, history-based, with RouterView + Link. Zero third-party deps.",
5
+ "type": "module",
6
+ "sideEffects": [
7
+ "./src/index.ts",
8
+ "./dist/index.js"
9
+ ],
10
+ "main": "./dist/index.js",
11
+ "module": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ },
18
+ "./files": {
19
+ "types": "./dist/files.d.ts",
20
+ "import": "./dist/files.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/weave-framework/weave.git",
32
+ "directory": "packages/router"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "dependencies": {
38
+ "@weave-framework/runtime": "0.2.0"
39
+ },
40
+ "license": "MIT"
41
+ }