@troyio/vite-file-router 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -6
- package/devtools/app.js +961 -143
- package/devtools/index.html +147 -33
- package/devtools/style.css +1136 -231
- package/dist/declarations.d.ts +9 -0
- package/dist/devtools-app.d.ts +59 -0
- package/dist/devtools.d.ts +50 -0
- package/dist/generator.d.ts +14 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +573 -33
- package/dist/scanner.d.ts +6 -1
- package/package.json +1 -1
- package/types/index.d.ts +143 -60
- package/types/virtual-routes.d.ts +46 -19
package/README.md
CHANGED
|
@@ -249,6 +249,10 @@ export default function DashboardPage() {
|
|
|
249
249
|
}
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
+
To get the type from the loader module instead of writing it by hand, use
|
|
253
|
+
`useRouteData` from `virtual:route-helpers`. See
|
|
254
|
+
[Typed params and loader data](#typed-params-and-loader-data).
|
|
255
|
+
|
|
252
256
|
The authentication and mutation functions above are application code. A
|
|
253
257
|
client-side route guard controls navigation and presentation; it is not a
|
|
254
258
|
security boundary. Protect data and mutations in the server/API layer as well,
|
|
@@ -322,10 +326,53 @@ export default defineConfig({
|
|
|
322
326
|
});
|
|
323
327
|
```
|
|
324
328
|
|
|
325
|
-
Open the **Routes** dock to
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
+
Open the **Routes** dock to use the panel. It's development-only and needs
|
|
330
|
+
Vite DevTools enabled. In the panel you can:
|
|
331
|
+
|
|
332
|
+
- **Browse the route tree.** Each route shows its URL segment, full path, and
|
|
333
|
+
badges for its files: page, layout, loading, error, not-found (`404`),
|
|
334
|
+
loader, and action. You can filter by path, param, or file, collapse
|
|
335
|
+
branches, and move between routes with the arrow keys.
|
|
336
|
+
- **See route details.** Select a route to see its params and files.
|
|
337
|
+
- **Open files in your editor.** Click any file path. This uses Vite's built-in
|
|
338
|
+
`/__open-in-editor` endpoint, so it opens the editor Vite detects or the one
|
|
339
|
+
named in `LAUNCH_EDITOR`.
|
|
340
|
+
- **Test a URL.** Enter a path such as `/users/42?tab=posts`. The panel shows
|
|
341
|
+
the matched routes, the params taken from the URL, and the page or
|
|
342
|
+
not-found page that renders. It also lists the layouts, loaders, loading
|
|
343
|
+
fallback, and error boundary that apply. Matching uses React Router's
|
|
344
|
+
`matchRoutes`, so the result agrees with the app. The matched routes are
|
|
345
|
+
highlighted in the tree.
|
|
346
|
+
- **Spot route conflicts.** Pages that resolve to the same URL pattern are
|
|
347
|
+
listed at the top, with links to their files.
|
|
348
|
+
- **Follow the current page.** The **Current page** card shows the URL your
|
|
349
|
+
app is on, its params, and the page that renders it. The matched routes get
|
|
350
|
+
a green dot in the tree, and the card updates as the app navigates. With
|
|
351
|
+
several app tabs open, pick which one to follow. When a tab closes, it drops
|
|
352
|
+
out of the list.
|
|
353
|
+
- **See loader and action calls.** The **Timeline** tab lists each loader and
|
|
354
|
+
action call your app makes, newest first. Each entry shows the route, URL,
|
|
355
|
+
start time, duration, and outcome: returned data, a redirect with its status
|
|
356
|
+
and location, a returned response, or a thrown error. Calls of 500 ms or
|
|
357
|
+
more are highlighted. It also shows each `shouldRevalidate` decision, so
|
|
358
|
+
you can see when a loader was skipped. You can filter by call type or text,
|
|
359
|
+
and expand an entry to see a short preview of returned data and a link to
|
|
360
|
+
the loader or action file. The dev server keeps the last 200 calls, so a
|
|
361
|
+
panel opened later still sees recent history.
|
|
362
|
+
|
|
363
|
+
The panel updates on its own when route files are added, removed, or renamed.
|
|
364
|
+
The dev server pushes changes over a Server-Sent Events stream instead of the
|
|
365
|
+
panel polling. If the dev server restarts, the panel reconnects by itself. The
|
|
366
|
+
refresh button reloads the data by hand.
|
|
367
|
+
|
|
368
|
+
The current page and the timeline come from a small client that
|
|
369
|
+
`virtual:routes` includes on the dev server only. It reports the app's URL and
|
|
370
|
+
wraps the generated loader, action, and `shouldRevalidate` handlers. It sends
|
|
371
|
+
these over Vite's HMR connection. Handler results pass through unchanged, and
|
|
372
|
+
only a short text preview of returned data is sent. It tracks navigation by
|
|
373
|
+
wrapping `history.pushState` and `history.replaceState` and listening for
|
|
374
|
+
`popstate`, and it also supports hash URLs (`#/path`). Production builds
|
|
375
|
+
include none of this code.
|
|
329
376
|
|
|
330
377
|
### Typed route helpers
|
|
331
378
|
|
|
@@ -360,6 +407,106 @@ parameters are rejected at runtime. Routes whose optional segment overlaps
|
|
|
360
407
|
another page's URL pattern are reported as conflicts. The generated
|
|
361
408
|
`routePatterns` export contains the discovered page paths.
|
|
362
409
|
|
|
410
|
+
### Generated route types
|
|
411
|
+
|
|
412
|
+
In TypeScript projects, the plugin writes `src/route-types.d.ts` when the dev
|
|
413
|
+
server starts and at the start of each build. It counts a project as
|
|
414
|
+
TypeScript when the Vite project root has a `tsconfig.json` or
|
|
415
|
+
`jsconfig.json`. JavaScript projects without either file get no extra file.
|
|
416
|
+
|
|
417
|
+
The plugin rewrites the file whenever route files are added, removed, or
|
|
418
|
+
renamed, and leaves it alone when nothing changed. The file lists every
|
|
419
|
+
discovered page pattern and loader module. With it in your TypeScript project,
|
|
420
|
+
`routePath`, `routePatterns`, and the hooks below only accept routes that
|
|
421
|
+
exist, and your editor autocompletes them:
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
routePath("/users/:userId", { userId }); // OK
|
|
425
|
+
routePath("/usres/:userId", { userId }); // Type error: unknown route pattern
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
The `dts` option controls this. It defaults to `"auto"`, which is the behavior
|
|
429
|
+
above. A path, relative to the Vite project root and ending in `.d.ts`, always
|
|
430
|
+
writes the file there, even without a TypeScript config. `false` never writes
|
|
431
|
+
it:
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
plugins: [routes({ dts: "types/route-types.d.ts" })];
|
|
435
|
+
plugins: [routes({ dts: false })];
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
If your `tsconfig.json` isn't in the Vite project root, for example when Vite's
|
|
439
|
+
`root` points at a subfolder, set an explicit path.
|
|
440
|
+
|
|
441
|
+
Without the file, route patterns are typed as `string` and loader data as
|
|
442
|
+
`unknown`, the same as before. You can commit the file so types work before
|
|
443
|
+
the first dev run, or add it to `.gitignore`.
|
|
444
|
+
|
|
445
|
+
### Typed params and loader data
|
|
446
|
+
|
|
447
|
+
`virtual:route-helpers` also exports two hooks that wrap React Router's hooks
|
|
448
|
+
with types taken from the route tree:
|
|
449
|
+
|
|
450
|
+
- `useRouteParams(pattern)` returns `useParams()`, typed for that pattern.
|
|
451
|
+
Required params are `string`, optional params are `string | undefined`, and
|
|
452
|
+
a splat is a `string` under the `"*"` key.
|
|
453
|
+
- `useRouteData(key)` returns `useRouteLoaderData()`, typed from the loader
|
|
454
|
+
module. The key is either a page pattern, which reads the loader in that
|
|
455
|
+
page's directory, or a loader route ID such as
|
|
456
|
+
`"file-route:src/pages/users/[userId]"`, which can read any matched loader,
|
|
457
|
+
including a parent's. It returns `undefined` when that route isn't matched,
|
|
458
|
+
just like React Router's hook.
|
|
459
|
+
|
|
460
|
+
```tsx
|
|
461
|
+
// src/pages/users/[userId]/loader.ts
|
|
462
|
+
import type { LoaderFunctionArgs } from "react-router";
|
|
463
|
+
|
|
464
|
+
export async function loader({ params }: LoaderFunctionArgs) {
|
|
465
|
+
return { name: await getUserName(params.userId!) };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/pages/users/[userId]/page.tsx
|
|
469
|
+
import { useRouteData, useRouteParams } from "virtual:route-helpers";
|
|
470
|
+
|
|
471
|
+
export default function UserPage() {
|
|
472
|
+
const { userId } = useRouteParams("/users/:userId"); // { userId: string }
|
|
473
|
+
const data = useRouteData("/users/:userId"); // { name: string } | undefined
|
|
474
|
+
return <h1>{data?.name ?? userId}</h1>;
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The `RouteParams<"/users/:userId">`, `RouteLoaderData<key>`, `RoutePattern`,
|
|
479
|
+
and `RouteLoaderKey` types are exported from `@troyio/vite-file-router` for
|
|
480
|
+
your own helpers. Loader data is typed the same way as React Router's
|
|
481
|
+
`useRouteLoaderData<typeof loader>()`. JavaScript loader modules are typed as
|
|
482
|
+
`any`. Both hooks throw if they're passed a pattern or key the plugin didn't
|
|
483
|
+
discover.
|
|
484
|
+
|
|
485
|
+
### Route preloading
|
|
486
|
+
|
|
487
|
+
`virtual:routes` exports `preloadRoute(to, basename?)`. It matches a URL
|
|
488
|
+
against the route tree and imports the page, layout, and loader modules of
|
|
489
|
+
every matched route, so the next navigation there doesn't wait for those
|
|
490
|
+
downloads. Error and action modules are left out, since they're only needed
|
|
491
|
+
after an error or a form submission. Importing modules doesn't run loaders.
|
|
492
|
+
Each module downloads once, however often you call it. The promise resolves to
|
|
493
|
+
`false` when no route matches the URL.
|
|
494
|
+
|
|
495
|
+
Call it when a user is likely to navigate, such as on link hover or focus:
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
import { Link, type LinkProps } from "react-router";
|
|
499
|
+
import { preloadRoute } from "virtual:routes";
|
|
500
|
+
|
|
501
|
+
export function PreloadLink(props: LinkProps & { to: string }) {
|
|
502
|
+
const preload = () => void preloadRoute(props.to);
|
|
503
|
+
return <Link {...props} onMouseEnter={preload} onFocus={preload} />;
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
If the router uses a `basename`, pass it as the second argument. `to` also
|
|
508
|
+
accepts a partial location such as `{ pathname: "/users/42" }`.
|
|
509
|
+
|
|
363
510
|
### TypeScript setup
|
|
364
511
|
|
|
365
512
|
Add Vite's client types and the package's virtual-module declaration to your
|
|
@@ -372,8 +519,9 @@ application TypeScript project. For example, create `src/vite-env.d.ts`:
|
|
|
372
519
|
|
|
373
520
|
If `tsconfig.json` sets `compilerOptions.types`, include both `"vite/client"`
|
|
374
521
|
and `"@troyio/vite-file-router"` in that list. This lets TypeScript resolve
|
|
375
|
-
`virtual:routes`.
|
|
376
|
-
|
|
522
|
+
`virtual:routes`. Make sure the generated `src/route-types.d.ts`, or the path
|
|
523
|
+
you set with `dts`, is included by your `tsconfig.json`. Restart your editor's
|
|
524
|
+
TypeScript server if the declarations aren't picked up immediately.
|
|
377
525
|
|
|
378
526
|
## Build
|
|
379
527
|
|