modernjs-typed-routes 0.0.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 Giancarlos Isasi
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.
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # modernjs-typed-routes
2
+
3
+ [![npm version](https://img.shields.io/npm/v/modernjs-typed-routes)](https://www.npmjs.com/package/modernjs-typed-routes)
4
+ [![CI](https://github.com/giancarlosisasi/modernjs-typed-routes/actions/workflows/ci.yml/badge.svg)](https://github.com/giancarlosisasi/modernjs-typed-routes/actions/workflows/ci.yml)
5
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
6
+
7
+ Type-safe routes for [Modern.js](https://modernjs.dev): a zero-config CLI plugin that generates
8
+ TypeScript types from your file-based `routes/` folders, plus typed `Link`, `Navigate` and
9
+ `useNavigate` wrappers — so path typos and missing route params become **compile errors** instead
10
+ of runtime 404s.
11
+
12
+ > **Status: pre-release.** Not yet published to npm — v0.1.0 is on its way.
13
+
14
+ ![Demo: route autocomplete, typo'd path error, required params](./docs/public/demo.gif)
15
+
16
+ ```tsx
17
+ import { Link, useNavigate } from 'modernjs-typed-routes';
18
+
19
+ <Link to="/about" /> // ✅ autocompleted route union
20
+ <Link to="/blog/[id]" params={{ id: post.id }} /> // ✅ params required & typed
21
+ <Link to="/blgo/[id]" params={{ id: post.id }} /> // ❌ error: Did you mean '"/blog/[id]"'?
22
+
23
+ const { navigateTo } = useNavigate();
24
+ navigateTo('/blog/[id]'); // ❌ error: `params` is required here
25
+ ```
26
+
27
+ ## Quickstart
28
+
29
+ **1. Install** (one package: build-time plugin + runtime wrappers):
30
+
31
+ ```bash
32
+ pnpm add modernjs-typed-routes
33
+ ```
34
+
35
+ **2. Register the plugin:**
36
+
37
+ ```ts
38
+ // modern.config.ts
39
+ import { appTools, defineConfig } from '@modern-js/app-tools';
40
+ import { routeTypesPlugin } from 'modernjs-typed-routes/plugin';
41
+
42
+ export default defineConfig({
43
+ plugins: [appTools(), routeTypesPlugin()],
44
+ });
45
+ ```
46
+
47
+ **3. Run `modern dev`** (or `npx modern typegen` to generate without starting anything). A single
48
+ declaration file appears at `src/routes.gen.d.ts` and regenerates automatically whenever your
49
+ routes change.
50
+
51
+ **4. Import navigation from the package** instead of `@modern-js/runtime/router` — it re-exports
52
+ the full router surface with `Link`, `Navigate` and `useNavigate` swapped for typed versions:
53
+
54
+ ```tsx
55
+ import { Link } from 'modernjs-typed-routes';
56
+
57
+ <Link to="/blog/[id]" params={{ id: 42 }}>Read post 42</Link>
58
+ ```
59
+
60
+ That's the whole setup. See the docs for the full API: `buildPath`, `useTypedParams`,
61
+ `createUrl`, search params, hashes, multi-entry apps and more.
62
+
63
+ ## Why this plugin
64
+
65
+ - **Derived from Modern.js's own route parser** — not a re-implementation of its conventions, so
66
+ the types can't drift from what the framework serves. Covers dynamic `[id]`, optional `[id$]`,
67
+ splat `$`, pathless `__auth` layouts, flat `a.b.c` segments, `modern.routes.ts` config routes
68
+ and multi-entry apps.
69
+ - **Types only, zero bundle cost** — the generated file is a `.d.ts`; declaration merging does the
70
+ rest. The wrappers are tiny static components.
71
+ - **Params are objects, required per route** — `<Link to="/blog/[id]" params={{ id }}>`
72
+ autocompletes the path *and* the param names.
73
+ - **CI friendly** — `npx modern typegen && tsc --noEmit` catches broken links in pull requests.
74
+
75
+ ## Comparison
76
+
77
+ | | modernjs-typed-routes | Next.js `typedRoutes` | React Router `typegen` | TanStack Router |
78
+ |---|---|---|---|---|
79
+ | Framework | Modern.js | Next.js | RR framework mode | TanStack |
80
+ | Typed path union | ✅ | ✅ | ✅ (`href()`) | ✅ |
81
+ | Params required per route | ✅ objects | template strings only | ✅ (`href()`) | ✅ |
82
+ | Typed navigation components | ✅ ships wrappers | `next/link` built-in | partial | ✅ built-in |
83
+ | Search params | basic (validated planned) | ❌ | ❌ | ✅ validated |
84
+ | Bundle cost | none (types only) | none | none | runtime router |
85
+
86
+ ## Requirements
87
+
88
+ | | Supported |
89
+ |---|---|
90
+ | Modern.js | v3 — `@modern-js/app-tools` / `@modern-js/runtime` `^3.6.0` |
91
+ | Node.js | ≥ 20 |
92
+ | TypeScript | ≥ 5.3 |
93
+ | React | ≥ 18 |
94
+
95
+ Routing must use conventional `routes/` entries. Modern.js v2, the legacy `pages/` convention and
96
+ self-controlled (`App.tsx`) entries are out of scope — there's no conventional route tree to type.
97
+
98
+ ## Documentation
99
+
100
+ Full docs live in [`docs/`](./docs) (Rspress site — hosted version coming with the first release):
101
+ guides for [getting started](./docs/guide/getting-started.md),
102
+ [navigation](./docs/guide/navigation.md),
103
+ [route conventions](./docs/guide/route-conventions.md) and
104
+ [troubleshooting](./docs/guide/troubleshooting.md), plus the
105
+ [plugin options](./docs/api/plugin-options.md) and [runtime API](./docs/api/runtime.md) reference.
106
+
107
+ Related: [web-infra-dev/modern.js#6218](https://github.com/web-infra-dev/modern.js/issues/6218) —
108
+ the upstream feature request this plugin answers.
109
+
110
+ ## Contributing
111
+
112
+ Issues and PRs welcome — see [CONTRIBUTING.md](./CONTRIBUTING.md). Development at a glance:
113
+
114
+ ```bash
115
+ pnpm install
116
+ pnpm build # build the library (rslib)
117
+ pnpm test # unit + type-level + docs-snippet + playground checks
118
+ pnpm test:e2e # E2E against the real Modern.js CLI (examples/playground)
119
+ pnpm check # biome
120
+ pnpm docs:dev # rspress docs site
121
+ ```
122
+
123
+ ## License
124
+
125
+ [MIT](./LICENSE) © Giancarlos Isasi
package/dist/index.cjs ADDED
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __webpack_modules__ = {
3
+ "./src/runtime/basename.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
4
+ var _modern_js_runtime_router__rspack_import_0 = __webpack_require__("@modern-js/runtime/router");
5
+ const splitAtQueryOrHash = (url)=>{
6
+ const at = url.search(/[?#]/);
7
+ return -1 === at ? [
8
+ url,
9
+ ''
10
+ ] : [
11
+ url.slice(0, at),
12
+ url.slice(at)
13
+ ];
14
+ };
15
+ const normalizeBasename = (basename)=>{
16
+ const trimmed = basename.replace(/\/+$/, '');
17
+ return '' === trimmed ? '/' : trimmed;
18
+ };
19
+ function isWithinBasename(url, basename) {
20
+ const base = normalizeBasename(basename);
21
+ if ('/' === base) return true;
22
+ const [path] = splitAtQueryOrHash(url);
23
+ return path === base || path.startsWith(`${base}/`);
24
+ }
25
+ function toRouterPath(url, basename) {
26
+ const base = normalizeBasename(basename);
27
+ if ('/' === base || !isWithinBasename(url, base)) return url;
28
+ const [path, suffix] = splitAtQueryOrHash(url);
29
+ return (path.slice(base.length) || '/') + suffix;
30
+ }
31
+ function toRealUrl(url, basename) {
32
+ const base = normalizeBasename(basename);
33
+ if ('/' === base || isWithinBasename(url, base)) return url;
34
+ const [path, suffix] = splitAtQueryOrHash(url);
35
+ return ('/' === path ? base : `${base}${path}`) + suffix;
36
+ }
37
+ function useBasename() {
38
+ return normalizeBasename((0, _modern_js_runtime_router__rspack_import_0.useHref)('/'));
39
+ }
40
+ __webpack_require__.d(__webpack_exports__, {
41
+ Kh: ()=>useBasename,
42
+ Tz: ()=>toRealUrl,
43
+ aT: ()=>toRouterPath
44
+ });
45
+ },
46
+ "./src/runtime/build-path.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
47
+ const OPTIONAL_TOKEN = /^\[(.+)\$\]$/;
48
+ const REQUIRED_TOKEN = /^\[(.+)\]$/;
49
+ const isAbsent = (value)=>null == value || '' === value;
50
+ function substituteSegment(segment, params, path) {
51
+ if ('$' === segment) {
52
+ const splat = params['*'];
53
+ if (isAbsent(splat)) return null;
54
+ const pieces = String(splat).split('/').filter(Boolean);
55
+ return 0 === pieces.length ? null : pieces.map(encodeURIComponent).join('/');
56
+ }
57
+ const optional = OPTIONAL_TOKEN.exec(segment);
58
+ if (optional) {
59
+ const value = params[optional[1]];
60
+ if (isAbsent(value)) return null;
61
+ return encodeURIComponent(String(value));
62
+ }
63
+ const required = REQUIRED_TOKEN.exec(segment);
64
+ if (required) {
65
+ const name = required[1];
66
+ const value = params[name];
67
+ if (isAbsent(value)) throw new Error(`[modernjs-typed-routes] Missing required param "${name}" for path "${path}".`);
68
+ return encodeURIComponent(String(value));
69
+ }
70
+ return segment;
71
+ }
72
+ function serializeSearchParams(searchParams) {
73
+ const query = new URLSearchParams();
74
+ for (const [key, value] of Object.entries(searchParams))if (null != value) query.append(key, String(value));
75
+ return query.toString();
76
+ }
77
+ function buildPath(path, ...args) {
78
+ const { params = {}, searchParams, hash } = args[0] ?? {};
79
+ const wantsTrailingSlash = path.length > 1 && path.endsWith('/');
80
+ const base = wantsTrailingSlash ? path.slice(0, -1) : path;
81
+ const built = base.split('/').filter((segment)=>'' !== segment).map((segment)=>substituteSegment(segment, params, path)).filter((segment)=>null !== segment).join('/');
82
+ let url = `/${built}`;
83
+ if (wantsTrailingSlash && '/' !== url) url += '/';
84
+ if (searchParams) {
85
+ const query = serializeSearchParams(searchParams);
86
+ if ('' !== query) url += `?${query}`;
87
+ }
88
+ if (void 0 !== hash && '' !== hash && '#' !== hash) url += hash.startsWith('#') ? hash : `#${hash}`;
89
+ return url;
90
+ }
91
+ __webpack_require__.d(__webpack_exports__, {
92
+ U: ()=>buildPath
93
+ });
94
+ },
95
+ "./src/runtime/link.tsx" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
96
+ var react_jsx_runtime__rspack_import_0 = __webpack_require__("react/jsx-runtime");
97
+ var _modern_js_runtime_router__rspack_import_1 = __webpack_require__("@modern-js/runtime/router");
98
+ var react__rspack_import_2 = __webpack_require__("react");
99
+ var _basename__rspack_import_3 = __webpack_require__("./src/runtime/basename.ts");
100
+ var _build_path__rspack_import_4 = __webpack_require__("./src/runtime/build-path.ts");
101
+ function LinkImpl(props, ref) {
102
+ const { to, params, searchParams, hash, ...rest } = props;
103
+ const basename = (0, _basename__rspack_import_3.Kh)();
104
+ const url = (0, _build_path__rspack_import_4.U)(to, {
105
+ params,
106
+ searchParams,
107
+ hash
108
+ });
109
+ return /*#__PURE__*/ (0, react_jsx_runtime__rspack_import_0.jsx)(_modern_js_runtime_router__rspack_import_1.Link, {
110
+ ...rest,
111
+ ref: ref,
112
+ to: (0, _basename__rspack_import_3.aT)(url, basename)
113
+ });
114
+ }
115
+ LinkImpl.displayName = 'TypedLink';
116
+ const Link = /*#__PURE__*/ (0, react__rspack_import_2.forwardRef)(LinkImpl);
117
+ __webpack_require__.d(__webpack_exports__, {}, {
118
+ N: Link
119
+ });
120
+ },
121
+ "./src/runtime/navigate.tsx" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
122
+ var react_jsx_runtime__rspack_import_0 = __webpack_require__("react/jsx-runtime");
123
+ var _modern_js_runtime_router__rspack_import_1 = __webpack_require__("@modern-js/runtime/router");
124
+ var _basename__rspack_import_2 = __webpack_require__("./src/runtime/basename.ts");
125
+ var _build_path__rspack_import_3 = __webpack_require__("./src/runtime/build-path.ts");
126
+ function Navigate(props) {
127
+ const { to, params, searchParams, hash, ...rest } = props;
128
+ const basename = (0, _basename__rspack_import_2.Kh)();
129
+ const url = (0, _build_path__rspack_import_3.U)(to, {
130
+ params,
131
+ searchParams,
132
+ hash
133
+ });
134
+ return /*#__PURE__*/ (0, react_jsx_runtime__rspack_import_0.jsx)(_modern_js_runtime_router__rspack_import_1.Navigate, {
135
+ ...rest,
136
+ to: (0, _basename__rspack_import_2.aT)(url, basename)
137
+ });
138
+ }
139
+ Navigate.displayName = 'TypedNavigate';
140
+ __webpack_require__.d(__webpack_exports__, {
141
+ C: ()=>Navigate
142
+ });
143
+ },
144
+ "./src/runtime/reexports.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
145
+ __webpack_require__.r(__webpack_exports__);
146
+ var _modern_js_runtime_router__rspack_import_0 = __webpack_require__("@modern-js/runtime/router");
147
+ var __rspack_reexport = {};
148
+ for(const __rspack_import_key in _modern_js_runtime_router__rspack_import_0)if ([
149
+ "useTypedParams",
150
+ "buildPath",
151
+ "default",
152
+ "Navigate",
153
+ "useNavigate",
154
+ "Link"
155
+ ].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_modern_js_runtime_router__rspack_import_0[__rspack_import_key];
156
+ __webpack_require__.d(__webpack_exports__, __rspack_reexport);
157
+ var _build_path__rspack_import_1 = __webpack_require__("./src/runtime/build-path.ts");
158
+ var _link__rspack_import_2 = __webpack_require__("./src/runtime/link.tsx");
159
+ var _navigate__rspack_import_3 = __webpack_require__("./src/runtime/navigate.tsx");
160
+ var _use_navigate__rspack_import_4 = __webpack_require__("./src/runtime/use-navigate.ts");
161
+ var _use_typed_params__rspack_import_5 = __webpack_require__("./src/runtime/use-typed-params.ts");
162
+ __webpack_require__.d(__webpack_exports__, {
163
+ Link: ()=>_link__rspack_import_2.N,
164
+ Navigate: ()=>_navigate__rspack_import_3.C,
165
+ buildPath: ()=>_build_path__rspack_import_1.U,
166
+ useNavigate: ()=>_use_navigate__rspack_import_4.Z,
167
+ useTypedParams: ()=>_use_typed_params__rspack_import_5.C
168
+ });
169
+ },
170
+ "./src/runtime/use-navigate.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
171
+ var _modern_js_runtime_router__rspack_import_0 = __webpack_require__("@modern-js/runtime/router");
172
+ var react__rspack_import_1 = __webpack_require__("react");
173
+ var _basename__rspack_import_2 = __webpack_require__("./src/runtime/basename.ts");
174
+ var _build_path__rspack_import_3 = __webpack_require__("./src/runtime/build-path.ts");
175
+ function useNavigate() {
176
+ const originalNavigate = (0, _modern_js_runtime_router__rspack_import_0.useNavigate)();
177
+ const basename = (0, _basename__rspack_import_2.Kh)();
178
+ const navigateTo = (0, react__rspack_import_1.useCallback)((to, ...args)=>{
179
+ const { replace, state, ...build } = args[0] ?? {};
180
+ const url = (0, _build_path__rspack_import_3.U)(to, build);
181
+ originalNavigate((0, _basename__rspack_import_2.aT)(url, basename), {
182
+ replace,
183
+ state
184
+ });
185
+ }, [
186
+ originalNavigate,
187
+ basename
188
+ ]);
189
+ const createUrl = (0, react__rspack_import_1.useCallback)((to, ...args)=>(0, _basename__rspack_import_2.Tz)((0, _build_path__rspack_import_3.U)(to, ...args), basename), [
190
+ basename
191
+ ]);
192
+ const goBack = (0, react__rspack_import_1.useCallback)(()=>{
193
+ originalNavigate(-1);
194
+ }, [
195
+ originalNavigate
196
+ ]);
197
+ return (0, react__rspack_import_1.useMemo)(()=>({
198
+ navigateTo,
199
+ createUrl,
200
+ goBack,
201
+ originalNavigate
202
+ }), [
203
+ navigateTo,
204
+ createUrl,
205
+ goBack,
206
+ originalNavigate
207
+ ]);
208
+ }
209
+ __webpack_require__.d(__webpack_exports__, {
210
+ Z: ()=>useNavigate
211
+ });
212
+ },
213
+ "./src/runtime/use-typed-params.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
214
+ var _modern_js_runtime_router__rspack_import_0 = __webpack_require__("@modern-js/runtime/router");
215
+ function useTypedParams(_path) {
216
+ return (0, _modern_js_runtime_router__rspack_import_0.useParams)();
217
+ }
218
+ __webpack_require__.d(__webpack_exports__, {
219
+ C: ()=>useTypedParams
220
+ });
221
+ },
222
+ "@modern-js/runtime/router" (module) {
223
+ module.exports = require("@modern-js/runtime/router");
224
+ },
225
+ react (module) {
226
+ module.exports = require("react");
227
+ },
228
+ "react/jsx-runtime" (module) {
229
+ module.exports = require("react/jsx-runtime");
230
+ }
231
+ };
232
+ var __webpack_module_cache__ = {};
233
+ function __webpack_require__(moduleId) {
234
+ var cachedModule = __webpack_module_cache__[moduleId];
235
+ if (void 0 !== cachedModule) return cachedModule.exports;
236
+ var module = __webpack_module_cache__[moduleId] = {
237
+ exports: {}
238
+ };
239
+ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
240
+ return module.exports;
241
+ }
242
+ (()=>{
243
+ __webpack_require__.n = (module)=>{
244
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
245
+ __webpack_require__.d(getter, {
246
+ a: getter
247
+ });
248
+ return getter;
249
+ };
250
+ })();
251
+ (()=>{
252
+ __webpack_require__.d = (exports1, getters, values)=>{
253
+ var define = (defs, kind)=>{
254
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
255
+ enumerable: true,
256
+ [kind]: defs[key]
257
+ });
258
+ };
259
+ define(getters, "get");
260
+ define(values, "value");
261
+ };
262
+ })();
263
+ (()=>{
264
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
265
+ })();
266
+ (()=>{
267
+ __webpack_require__.r = (exports1)=>{
268
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
269
+ value: 'Module'
270
+ });
271
+ Object.defineProperty(exports1, '__esModule', {
272
+ value: true
273
+ });
274
+ };
275
+ })();
276
+ var __webpack_exports__ = {};
277
+ (()=>{
278
+ __webpack_require__.r(__webpack_exports__);
279
+ var _runtime_reexports__rspack_import_0 = __webpack_require__("./src/runtime/reexports.ts");
280
+ var __rspack_reexport = {};
281
+ for(const __rspack_import_key in _runtime_reexports__rspack_import_0)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_runtime_reexports__rspack_import_0[__rspack_import_key];
282
+ __webpack_require__.d(__webpack_exports__, __rspack_reexport);
283
+ })();
284
+ exports.Link = __webpack_exports__.Link;
285
+ exports.Navigate = __webpack_exports__.Navigate;
286
+ exports.buildPath = __webpack_exports__.buildPath;
287
+ exports.useNavigate = __webpack_exports__.useNavigate;
288
+ exports.useTypedParams = __webpack_exports__.useTypedParams;
289
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
290
+ "Link",
291
+ "Navigate",
292
+ "buildPath",
293
+ "useNavigate",
294
+ "useTypedParams"
295
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
296
+ Object.defineProperty(exports, '__esModule', {
297
+ value: true
298
+ });
@@ -0,0 +1,176 @@
1
+ import { ComponentProps } from 'react';
2
+ import { Link as Link_2 } from '@modern-js/runtime/router';
3
+ import { Navigate as Navigate_2 } from '@modern-js/runtime/router';
4
+ import { NavigateFunction } from '@modern-js/runtime/router';
5
+ import { ReactElement } from 'react';
6
+ import { Ref } from 'react';
7
+
8
+ declare type AllEntryRouteKeys = {
9
+ [E in RegisterEntryName]: keyof RoutesOf<E> & string;
10
+ }[RegisterEntryName];
11
+
12
+ export declare type BaseBuildOptions = {
13
+ searchParams?: SearchParamsInit;
14
+ hash?: string;
15
+ };
16
+
17
+ /** Same conditional tuple for `buildPath` / `createUrl`. */
18
+ export declare type BuildArgs<P extends RoutePath> = Record<string, never> extends RouteParams<P> ? [options?: BuildOptions<P>] : [options: BuildOptions<P>];
19
+
20
+ /** Options for `buildPath` / `createUrl` — `params` conditionally required (D8). */
21
+ export declare type BuildOptions<P extends RoutePath> = BaseBuildOptions & WithParams<P>;
22
+
23
+ /**
24
+ * `buildPath('/blog/[id]', { params: { id: 42 }, searchParams: { ref: 'rss' }, hash: 'top' })`
25
+ * → `'/blog/42?ref=rss#top'`
26
+ */
27
+ export declare function buildPath<P extends RoutePath>(path: P, ...args: BuildArgs<P>): string;
28
+
29
+ declare type EntriesMap = Register extends {
30
+ entries: infer E extends EntriesShape;
31
+ } ? E : never;
32
+
33
+ declare type EntriesShape = Record<string, {
34
+ basename: string;
35
+ routes: RoutesShape;
36
+ }>;
37
+
38
+ /** Write-side params scoped to one entry. */
39
+ export declare type EntryRouteParams<E extends RegisterEntryName, P extends EntryRoutePath<E>> = P extends keyof RoutesOf<E> ? ParamsLookup<RoutesOf<E>[P]> : never;
40
+
41
+ /** Route-path literals belonging to ONE entry of a multi-entry app. */
42
+ export declare type EntryRoutePath<E extends RegisterEntryName> = keyof RoutesOf<E> & string;
43
+
44
+ /** Fallback write-side params when nothing is generated yet. */
45
+ declare type FallbackParams = Record<string, string | number>;
46
+
47
+ declare type HasEntries = [EntriesMap] extends [never] ? false : true;
48
+
49
+ declare type HasSingle = [SingleRoutes] extends [never] ? false : true;
50
+
51
+ export declare const Link: <P extends RoutePath>(props: LinkProps<P> & {
52
+ ref?: Ref<HTMLAnchorElement>;
53
+ }) => ReactElement;
54
+
55
+ export declare type LinkProps<P extends RoutePath> = Omit<RouterLinkProps, 'to'> & {
56
+ to: P;
57
+ } & BaseBuildOptions & WithParams<P>;
58
+
59
+ export declare function Navigate<P extends RoutePath>(props: NavigateProps<P>): ReactElement;
60
+
61
+ export declare namespace Navigate {
62
+ var displayName: string;
63
+ }
64
+
65
+ /**
66
+ * Argument tuple making the whole options object required exactly when the
67
+ * route has required params: `navigateTo('/blog/[id]')` must not compile.
68
+ */
69
+ export declare type NavigateArgs<P extends RoutePath> = Record<string, never> extends RouteParams<P> ? [options?: NavigateToOptions<P>] : [options: NavigateToOptions<P>];
70
+
71
+ export declare type NavigateProps<P extends RoutePath> = Omit<RouterNavigateProps, 'to'> & {
72
+ to: P;
73
+ } & BaseBuildOptions & WithParams<P>;
74
+
75
+ /** Options for `navigateTo` / `<Navigate>`. */
76
+ export declare type NavigateToOptions<P extends RoutePath> = BuildOptions<P> & {
77
+ replace?: boolean;
78
+ state?: unknown;
79
+ };
80
+
81
+ /** `infer`-based lookup — TS cannot index `['params']` on a generic entry lookup directly. */
82
+ declare type ParamsLookup<R> = R extends {
83
+ params: infer P extends ParamsShape;
84
+ } ? P : never;
85
+
86
+ declare type ParamsShape = Record<string, unknown>;
87
+
88
+ /**
89
+ * Runtime entry (`modernjs-typed-routes`): the full router surface with the
90
+ * typed wrappers shadowing `Link`/`Navigate`/`useNavigate` (docs contract).
91
+ *
92
+ * `Register` MUST be an interface (never a `type`): the generated
93
+ * `routes.gen.d.ts` fills it via declaration merging (D5), which only
94
+ * works on interfaces. It must also stay DECLARED here — module
95
+ * augmentation does not merge through re-exports.
96
+ */
97
+ export declare interface Register {
98
+ }
99
+
100
+ /**
101
+ * Entry names of a multi-entry Register (`never` for single-entry/empty).
102
+ * The `HasEntries` guard is required: with `EntriesMap = never`,
103
+ * `keyof never & string` collapses to `string`, not `never`.
104
+ */
105
+ export declare type RegisterEntryName = HasEntries extends true ? keyof EntriesMap & string : never;
106
+
107
+ /** Write-side params for route `P` (values `string | number`, splat `string`). */
108
+ export declare type RouteParams<P extends RoutePath> = HasSingle extends true ? P extends keyof SingleRoutes ? SingleRoutes[P]['params'] : never : HasEntries extends true ? {
109
+ [E in RegisterEntryName]: P extends keyof RoutesOf<E> ? ParamsLookup<RoutesOf<E>[P]> : never;
110
+ }[RegisterEntryName] : FallbackParams;
111
+
112
+ /** Union of all route-path literals; `string` before the first generation. */
113
+ export declare type RoutePath = HasSingle extends true ? keyof SingleRoutes & string : HasEntries extends true ? AllEntryRouteKeys : string;
114
+
115
+ /** Alias of {@link RoutePath} for app-level props (`RoutePathname | (string & {})`). */
116
+ export declare type RoutePathname = RoutePath;
117
+
118
+ /**
119
+ * Read-side params for `useTypedParams` — values are always `string`
120
+ * (that's what the URL contains); optionality is preserved.
121
+ */
122
+ export declare type RouteReadParams<P extends RoutePath> = {
123
+ [K in keyof RouteParams<P>]: string;
124
+ };
125
+
126
+ declare type RouterLinkProps = ComponentProps<typeof Link_2>;
127
+
128
+ declare type RouterNavigateProps = ComponentProps<typeof Navigate_2>;
129
+
130
+ declare type RoutesOf<E extends RegisterEntryName> = EntriesMap[E]['routes'];
131
+
132
+ declare type RoutesShape = Record<string, {
133
+ params: ParamsShape;
134
+ }>;
135
+
136
+ export declare type SearchParamsInit = Record<string, string | number | boolean>;
137
+
138
+ declare type SingleRoutes = Register extends {
139
+ routes: infer R extends RoutesShape;
140
+ } ? R : never;
141
+
142
+ declare type StaticKeysOf<R extends RoutesShape> = {
143
+ [K in keyof R]: Record<string, never> extends R[K]['params'] ? K : never;
144
+ }[keyof R] & string;
145
+
146
+ /** Only the routes callable without a `params` argument (optional-only included). */
147
+ export declare type StaticRoutePath = HasSingle extends true ? StaticKeysOf<SingleRoutes> : HasEntries extends true ? {
148
+ [E in RegisterEntryName]: StaticKeysOf<RoutesOf<E>>;
149
+ }[RegisterEntryName] : string;
150
+
151
+ export declare function useNavigate(): {
152
+ navigateTo: <P extends RoutePath>(to: P, options?: NavigateToOptions<P> | undefined) => void;
153
+ createUrl: <P extends RoutePath>(to: P, options?: BuildOptions<P> | undefined) => string;
154
+ goBack: () => void;
155
+ originalNavigate: NavigateFunction;
156
+ };
157
+
158
+ export declare function useTypedParams<P extends RoutePath>(_path: P): RouteReadParams<P>;
159
+
160
+ /**
161
+ * `params` prop/option: required iff the route has required params (D8).
162
+ * Keyless params (`{}`) are tightened to `Record<string, never>` so passing
163
+ * params to a static route is a type error (TS never excess-checks against
164
+ * `{}`); the empty-Register fallback keeps `keyof = string` and stays
165
+ * permissive.
166
+ */
167
+ export declare type WithParams<P extends RoutePath> = Record<string, never> extends RouteParams<P> ? {
168
+ params?: [keyof RouteParams<P>] extends [never] ? Record<string, never> : RouteParams<P>;
169
+ } : {
170
+ params: RouteParams<P>;
171
+ };
172
+
173
+
174
+ export * from "@modern-js/runtime/router";
175
+
176
+ export { }