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 +21 -0
- package/README.md +125 -0
- package/dist/index.cjs +298 -0
- package/dist/index.d.cts +176 -0
- package/dist/index.d.ts +176 -0
- package/dist/index.js +152 -0
- package/dist/plugin/index.cjs +322 -0
- package/dist/plugin/index.d.cts +35 -0
- package/dist/plugin/index.d.ts +35 -0
- package/dist/plugin/index.js +271 -0
- package/package.json +119 -0
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
|
+
[](https://www.npmjs.com/package/modernjs-typed-routes)
|
|
4
|
+
[](https://github.com/giancarlosisasi/modernjs-typed-routes/actions/workflows/ci.yml)
|
|
5
|
+
[](./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
|
+

|
|
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
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -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 { }
|