@troyio/vite-file-router 0.0.0-stage → 0.1.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/README.md +74 -3
- package/dist/generator.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +86 -0
- package/dist/scanner.d.ts +8 -0
- package/package.json +41 -4
- package/types/index.d.ts +3 -0
- package/types/virtual-routes.d.ts +4 -0
package/README.md
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# vite-file-router
|
|
2
|
+
|
|
3
|
+
A Vite plugin that generates a route tree from a file-based page directory.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
The package is published to GitHub Packages on every push to `main`, under
|
|
8
|
+
`@OWNER/vite-file-router`. Configure npm to use GitHub Packages for that scope
|
|
9
|
+
and authenticate with a GitHub token that has `read:packages` access:
|
|
10
|
+
|
|
11
|
+
```ini
|
|
12
|
+
@OWNER:registry=https://npm.pkg.github.com
|
|
13
|
+
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then install it with:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install @OWNER/vite-file-router
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each main-branch build is published with a unique prerelease version.
|
|
23
|
+
|
|
24
|
+
Vite is a peer dependency and must also be installed in your project.
|
|
25
|
+
|
|
26
|
+
## Configure
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { defineConfig } from "vite";
|
|
30
|
+
import { routes } from "@OWNER/vite-file-router";
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
plugins: [routes()],
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
By default, the plugin scans `src/pages`. Pass a path relative to the Vite
|
|
38
|
+
project root to use another directory:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
plugins: [routes("src/routes")];
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Each `page.tsx` becomes a route page, and each `layout.tsx` becomes a lazy
|
|
45
|
+
layout for its directory. Directories become nested routes. For example:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
src/pages/
|
|
49
|
+
layout.tsx
|
|
50
|
+
page.tsx
|
|
51
|
+
users/
|
|
52
|
+
page.tsx
|
|
53
|
+
[id]/
|
|
54
|
+
page.tsx
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Dynamic segments such as `[id]` become `:id`; catch-all segments such as
|
|
58
|
+
`[...slug]` become `*`.
|
|
59
|
+
|
|
60
|
+
Import the generated route configuration from the `virtual:routes` module in
|
|
61
|
+
your application:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { routes } from "virtual:routes";
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The virtual module exports a `routes` array with lazy-loaded page and layout
|
|
68
|
+
components.
|
|
69
|
+
|
|
70
|
+
## Build
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npm run build
|
|
74
|
+
```
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { genArrayFromRaw, genObjectFromRaw } from "knitwork";
|
|
4
|
+
import { normalizePath } from "vite";
|
|
5
|
+
// src/scanner.ts
|
|
6
|
+
function scan(directory) {
|
|
7
|
+
const node = {
|
|
8
|
+
name: path.basename(directory),
|
|
9
|
+
directory,
|
|
10
|
+
children: []
|
|
11
|
+
};
|
|
12
|
+
if (!fs.existsSync(directory)) return node;
|
|
13
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
const file = path.join(directory, entry.name);
|
|
16
|
+
if (entry.isDirectory()) {
|
|
17
|
+
node.children.push(scan(file));
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (entry.name === "page.tsx") node.page = file;
|
|
21
|
+
if (entry.name === "layout.tsx") node.layout = file;
|
|
22
|
+
}
|
|
23
|
+
return node;
|
|
24
|
+
}
|
|
25
|
+
// src/generator.ts
|
|
26
|
+
function getRoutePath(directory) {
|
|
27
|
+
const segment = path.basename(directory);
|
|
28
|
+
if (segment.startsWith("[...") && segment.endsWith("]")) return "*";
|
|
29
|
+
return segment.replace(/^\[(.+)\]$/, ":$1");
|
|
30
|
+
}
|
|
31
|
+
function generateImport(file) {
|
|
32
|
+
return `async ()=>({
|
|
33
|
+
Component: (await import(${JSON.stringify(normalizePath(file))})).default
|
|
34
|
+
})`;
|
|
35
|
+
}
|
|
36
|
+
function generate(node, root = false) {
|
|
37
|
+
const route = {};
|
|
38
|
+
if (!root) route.path = JSON.stringify(getRoutePath(node.directory));
|
|
39
|
+
if (node.layout) route.lazy = generateImport(node.layout);
|
|
40
|
+
const children = [];
|
|
41
|
+
if (node.page) children.push(genObjectFromRaw({
|
|
42
|
+
index: "true",
|
|
43
|
+
lazy: generateImport(node.page)
|
|
44
|
+
}));
|
|
45
|
+
for (const child of node.children) children.push(generate(child));
|
|
46
|
+
if (children.length) route.children = genArrayFromRaw(children);
|
|
47
|
+
return genObjectFromRaw(route);
|
|
48
|
+
}
|
|
49
|
+
// src/index.ts
|
|
50
|
+
var MODULE_ID = "virtual:routes";
|
|
51
|
+
var RESOLVED_ID = `\0${MODULE_ID}`;
|
|
52
|
+
function routes(directory = "src/pages") {
|
|
53
|
+
let root = "";
|
|
54
|
+
function execute() {
|
|
55
|
+
const routes = generate(scan(root), true);
|
|
56
|
+
return `
|
|
57
|
+
export const routes = ${genArrayFromRaw([routes])};
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
name: "routes",
|
|
62
|
+
configResolved(config) {
|
|
63
|
+
root = path.resolve(config.root, directory);
|
|
64
|
+
},
|
|
65
|
+
resolveId: (id) => id === MODULE_ID ? RESOLVED_ID : void 0,
|
|
66
|
+
load: (id) => id === RESOLVED_ID ? execute() : void 0,
|
|
67
|
+
configureServer: (server) => {
|
|
68
|
+
server.watcher.add(root);
|
|
69
|
+
server.watcher.on("all", (event, file) => {
|
|
70
|
+
if (![
|
|
71
|
+
"add",
|
|
72
|
+
"unlink",
|
|
73
|
+
"addDir",
|
|
74
|
+
"unlinkDir",
|
|
75
|
+
"change"
|
|
76
|
+
].includes(event)) return;
|
|
77
|
+
const location = path.resolve(file);
|
|
78
|
+
if (location !== root && !location.startsWith(root + path.sep)) return;
|
|
79
|
+
const module = server.moduleGraph.getModuleById(RESOLVED_ID);
|
|
80
|
+
if (module) server.moduleGraph.invalidateModule(module);
|
|
81
|
+
server.ws.send({ type: "full-reload" });
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export { routes };
|
package/package.json
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@troyio/vite-file-router",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "File-system routing plugin for Vite",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"types"
|
|
13
|
+
],
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./types/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "vite build && tsc -p tsconfig.build.json",
|
|
24
|
+
"lint": "eslint src vite.config.ts"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"knitwork": "^1.3.0",
|
|
28
|
+
"react-router": "^8.5.1",
|
|
29
|
+
"vite-file-router": "link:"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@eslint/js": "^10.0.1",
|
|
36
|
+
"@types/node": "^24.19.2",
|
|
37
|
+
"eslint": "^10.13.0",
|
|
38
|
+
"globals": "^17.13.0",
|
|
39
|
+
"typescript": "~6.0.3",
|
|
40
|
+
"typescript-eslint": "^8.72.0",
|
|
41
|
+
"vite": "^8.3.5"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/types/index.d.ts
ADDED