aaex-file-router 1.3.0 → 1.3.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 +100 -30
- package/dist/core/index.d.ts +0 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +0 -1
- package/dist/core/index.js.map +1 -1
- package/dist/plugin/index.d.ts +3 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +2 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/plugin.d.ts +11 -0
- package/dist/plugin/plugin.d.ts.map +1 -0
- package/dist/plugin/plugin.js +48 -0
- package/dist/plugin/plugin.js.map +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -2,10 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
A file-based routing system for React projects that automatically generates routes from your file structure. Similar to Next.js App Router or Remix file conventions.
|
|
4
4
|
|
|
5
|
-
## V. 1.3.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
## V. 1.3.1
|
|
6
|
+
|
|
7
|
+
Routes are now able to be infinitely nested
|
|
8
|
+
|
|
9
|
+
**Notice** Only tested 3 levels deep but there is no reason i shouldnt work further
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Features](#features)
|
|
14
|
+
- [Installation](#installation)
|
|
15
|
+
- [Quick Start](#quick-start)
|
|
16
|
+
- [1. Create your pages structure](#1-create-your-pages-structure)
|
|
17
|
+
- [2. Configure Vite](#2-configure-vite)
|
|
18
|
+
- [3. Use in your app](#3-use-in-your-app)
|
|
19
|
+
- [Using createBrowserRouter](#1-using-createbrowserrouter-recommended-for-most-users)
|
|
20
|
+
- [Using nested Route elements](#2-using-nested-route-elements)
|
|
21
|
+
- [File Conventions](#file-conventions)
|
|
22
|
+
- [Generated Routes File](#generated-routes-file)
|
|
23
|
+
- [Route Resolution Examples](#route-resolution-examples)
|
|
24
|
+
- [Layouts](#layouts)
|
|
25
|
+
- [FileLink component](#filelink-component)
|
|
26
|
+
- [Usage](#usage)
|
|
27
|
+
- [API Reference](#api-reference)
|
|
28
|
+
- [How It Works](#how-it-works)
|
|
29
|
+
- [Performance Considerations](#performance-considerations)
|
|
30
|
+
- [Common Patterns](#common-patterns)
|
|
31
|
+
- [Troubleshooting](#troubleshooting)
|
|
32
|
+
- [License](#license)
|
|
9
33
|
|
|
10
34
|
## Features
|
|
11
35
|
|
|
@@ -44,7 +68,7 @@ src/pages/
|
|
|
44
68
|
// vite.config.ts
|
|
45
69
|
import { defineConfig } from "vite";
|
|
46
70
|
import react from "@vitejs/plugin-react";
|
|
47
|
-
import { aaexFileRouter } from "aaex-file-router";
|
|
71
|
+
import { aaexFileRouter } from "aaex-file-router/plugin";
|
|
48
72
|
|
|
49
73
|
export default defineConfig({
|
|
50
74
|
plugins: [
|
|
@@ -59,20 +83,66 @@ export default defineConfig({
|
|
|
59
83
|
|
|
60
84
|
### 3. Use in your app
|
|
61
85
|
|
|
86
|
+
#### 1. Using createBrowserRouter (recommended for most users)
|
|
87
|
+
|
|
62
88
|
```typescript
|
|
63
|
-
// src/
|
|
64
|
-
import
|
|
65
|
-
import ReactDOM from "react-dom/client";
|
|
66
|
-
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
|
89
|
+
// src/App.tsx
|
|
90
|
+
import "./App.css";
|
|
67
91
|
import routes from "./routes";
|
|
92
|
+
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
|
93
|
+
import { Suspense } from "react";
|
|
94
|
+
|
|
95
|
+
function App() {
|
|
96
|
+
const router = createBrowserRouter(routes);
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
100
|
+
<RouterProvider router={router} />
|
|
101
|
+
</Suspense>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default App;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## **OR**
|
|
109
|
+
|
|
110
|
+
### 2. using nested Route elements
|
|
68
111
|
|
|
69
|
-
|
|
112
|
+
**Note** I will probably create a custom route provider using this version later since this is the only solution that works with VITE-SSR if you wrap client in `<BrowserRouter/>` and server in `<StaticRouter/>`
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
//src/App.tsx
|
|
116
|
+
import {
|
|
117
|
+
BrowserRouter,
|
|
118
|
+
Routes,
|
|
119
|
+
Route,
|
|
120
|
+
type RouteObject,
|
|
121
|
+
} from "react-router-dom";
|
|
122
|
+
import routes from "./routes";
|
|
123
|
+
import { Suspense } from "react";
|
|
124
|
+
import "./App.css";
|
|
125
|
+
|
|
126
|
+
//recursivly creates nested routes
|
|
127
|
+
function createRoutes(route: RouteObject) {
|
|
128
|
+
return (
|
|
129
|
+
<Route key={route.path} path={route.path} element={route.element}>
|
|
130
|
+
{route.children?.map((child) => createRoutes(child))}
|
|
131
|
+
</Route>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function App() {
|
|
136
|
+
return (
|
|
137
|
+
<BrowserRouter>
|
|
138
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
139
|
+
<Routes>{routes.map((route) => createRoutes(route))}</Routes>
|
|
140
|
+
</Suspense>
|
|
141
|
+
</BrowserRouter>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
70
144
|
|
|
71
|
-
|
|
72
|
-
<React.StrictMode>
|
|
73
|
-
<RouterProvider router={router} />
|
|
74
|
-
</React.StrictMode>
|
|
75
|
-
);
|
|
145
|
+
export default App;
|
|
76
146
|
```
|
|
77
147
|
|
|
78
148
|
## File Conventions
|
|
@@ -106,14 +176,14 @@ export default function AdminLayout() {
|
|
|
106
176
|
|
|
107
177
|
### Slug files
|
|
108
178
|
|
|
109
|
-
Filenames wrapper in
|
|
179
|
+
Filenames wrapper in square brackets `[filename]` will resolve to a dynamic route
|
|
110
180
|
|
|
111
181
|
```
|
|
112
|
-
pages/test/[<filename>].tsx → "/test/:<filename>"
|
|
182
|
+
src/pages/test/[<filename>].tsx → "/test/:<filename>"
|
|
113
183
|
```
|
|
114
184
|
|
|
115
185
|
```tsx
|
|
116
|
-
//pages/test/[slug].tsx
|
|
186
|
+
// src/pages/test/[slug].tsx
|
|
117
187
|
|
|
118
188
|
import { useParams } from "react-router-dom";
|
|
119
189
|
|
|
@@ -165,20 +235,20 @@ export default routes;
|
|
|
165
235
|
|
|
166
236
|
## Route Resolution Examples
|
|
167
237
|
|
|
168
|
-
| File Structure
|
|
169
|
-
|
|
|
170
|
-
| `pages/index.tsx` | `/` |
|
|
171
|
-
| `pages/about.tsx` | `/about` |
|
|
172
|
-
| `pages/blog/index.tsx` | `/blog` |
|
|
173
|
-
| `pages/blog/post.tsx` | `/blog/post` |
|
|
174
|
-
| `pages/admin/layout.tsx` + children | `/admin/*` (grouped) |
|
|
238
|
+
| File Structure | Route Path |
|
|
239
|
+
| --------------------------------------- | -------------------- |
|
|
240
|
+
| `src/pages/index.tsx` | `/` |
|
|
241
|
+
| `src/pages/about.tsx` | `/about` |
|
|
242
|
+
| `src/pages/blog/index.tsx` | `/blog` |
|
|
243
|
+
| `src/pages/blog/post.tsx` | `/blog/post` |
|
|
244
|
+
| `src/pages/admin/layout.tsx` + children | `/admin/*` (grouped) |
|
|
175
245
|
|
|
176
246
|
## Layouts
|
|
177
247
|
|
|
178
248
|
Layouts wrap their child routes and provide shared UI:
|
|
179
249
|
|
|
180
250
|
```typescript
|
|
181
|
-
// pages/dashboard/layout.tsx
|
|
251
|
+
// src/pages/dashboard/layout.tsx
|
|
182
252
|
import { Outlet } from "react-router-dom";
|
|
183
253
|
|
|
184
254
|
export default function DashboardLayout() {
|
|
@@ -193,7 +263,7 @@ export default function DashboardLayout() {
|
|
|
193
263
|
}
|
|
194
264
|
```
|
|
195
265
|
|
|
196
|
-
All routes in `pages/dashboard/*` will render inside this layout.
|
|
266
|
+
All routes in `src/pages/dashboard/*` will render inside this layout.
|
|
197
267
|
|
|
198
268
|
## FileLink component
|
|
199
269
|
|
|
@@ -215,7 +285,7 @@ export type FileRoutes = "/" | "test";
|
|
|
215
285
|
```
|
|
216
286
|
|
|
217
287
|
```tsx
|
|
218
|
-
//src/pages/index.tsx
|
|
288
|
+
// src/pages/index.tsx
|
|
219
289
|
import { FileLink } from "aaex-file-router";
|
|
220
290
|
import type { FileRoutes } from "../routeTypes";
|
|
221
291
|
|
|
@@ -225,7 +295,7 @@ export default function Home() {
|
|
|
225
295
|
Hello Home!
|
|
226
296
|
{/* FileRoutes is optional and not required it will work fine with any string if not passed */}
|
|
227
297
|
<FileLink<FileRoutes> to="test">Test safe</FileLink>
|
|
228
|
-
|
|
298
|
+
{/* or without type safety */}
|
|
229
299
|
<FileLink to="some-route">Non safe</FileLink>
|
|
230
300
|
</>
|
|
231
301
|
);
|
|
@@ -261,7 +331,7 @@ const routesCode = await generator.generateComponentsMap(fileData);
|
|
|
261
331
|
Automatically watches for file changes and regenerates routes.
|
|
262
332
|
|
|
263
333
|
```typescript
|
|
264
|
-
import { aaexFileRouter } from "aaex-file-router/
|
|
334
|
+
import { aaexFileRouter } from "aaex-file-router/plugin";
|
|
265
335
|
|
|
266
336
|
export default defineConfig({
|
|
267
337
|
plugins: [
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/core/index.js
CHANGED
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,gDAAgD
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,gDAAgD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
export interface VitePluginOptions {
|
|
3
|
+
pagesDir?: string;
|
|
4
|
+
outputFile?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Vite plugin that auto-generates routes when page files change
|
|
8
|
+
* Watches the pages directory and regenerates routes.ts on file create/delete
|
|
9
|
+
*/
|
|
10
|
+
export declare function aaexFileRouter(options?: VitePluginOptions): Plugin;
|
|
11
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAM9B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAmDtE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { FileScanner } from "../core/FileScanner.js";
|
|
4
|
+
import { RouteGenerator } from "../core/RouteGenerator.js";
|
|
5
|
+
/**
|
|
6
|
+
* Vite plugin that auto-generates routes when page files change
|
|
7
|
+
* Watches the pages directory and regenerates routes.ts on file create/delete
|
|
8
|
+
*/
|
|
9
|
+
export function aaexFileRouter(options = {}) {
|
|
10
|
+
const pagesDir = options.pagesDir || "./src/pages";
|
|
11
|
+
const outputFile = options.outputFile || "./src/routes.ts";
|
|
12
|
+
let scanner;
|
|
13
|
+
let generator;
|
|
14
|
+
return {
|
|
15
|
+
name: "aaex-file-router",
|
|
16
|
+
apply: "serve", // Only run in dev mode
|
|
17
|
+
configResolved() {
|
|
18
|
+
scanner = new FileScanner(pagesDir);
|
|
19
|
+
generator = new RouteGenerator();
|
|
20
|
+
},
|
|
21
|
+
async configureServer(server) {
|
|
22
|
+
// Watch the pages directory for changes
|
|
23
|
+
server.watcher.add(path.resolve(process.cwd(), pagesDir));
|
|
24
|
+
server.watcher.on("all", async (event, filePath) => {
|
|
25
|
+
// Only regenerate on file add/unlink events
|
|
26
|
+
if (event === "add" || event === "unlink") {
|
|
27
|
+
try {
|
|
28
|
+
console.log(`📄 [aaex-file-router] ${event}: ${filePath}`);
|
|
29
|
+
// Regenerate routes
|
|
30
|
+
scanner = new FileScanner(pagesDir);
|
|
31
|
+
generator = new RouteGenerator();
|
|
32
|
+
const fileData = await scanner.get_file_data();
|
|
33
|
+
const routesCode = await generator.generateRoutesFile(fileData);
|
|
34
|
+
const routesType = await generator.generateRoutesTypeDef(fileData);
|
|
35
|
+
// Write routes file
|
|
36
|
+
await fs.writeFile(outputFile, routesCode, "utf-8");
|
|
37
|
+
await fs.writeFile("src/routeTypes.ts", routesType, "utf-8");
|
|
38
|
+
console.log(`✅ [aaex-file-router] Routes regenerated at ${outputFile}`);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error("❌ [aaex-file-router] Error regenerating routes:", error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAO3D;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,UAA6B,EAAE;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,aAAa,CAAC;IACnD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC;IAE3D,IAAI,OAAoB,CAAC;IACzB,IAAI,SAAyB,CAAC;IAE9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE,OAAO,EAAE,uBAAuB;QAEvC,cAAc;YACZ,OAAO,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;YACpC,SAAS,GAAG,IAAI,cAAc,EAAE,CAAC;QACnC,CAAC;QAED,KAAK,CAAC,eAAe,CAAC,MAAM;YAC1B,wCAAwC;YACxC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;YAE1D,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;gBACjD,4CAA4C;gBAC5C,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC1C,IAAI,CAAC;wBACH,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,KAAK,QAAQ,EAAE,CAAC,CAAC;wBAE3D,oBAAoB;wBACpB,OAAO,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;wBACpC,SAAS,GAAG,IAAI,cAAc,EAAE,CAAC;wBAEjC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;wBAC/C,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;wBAChE,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;wBAEnE,oBAAoB;wBACpB,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;wBACpD,MAAM,EAAE,CAAC,SAAS,CAAC,mBAAmB,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;wBAE7D,OAAO,CAAC,GAAG,CACT,8CAA8C,UAAU,EAAE,CAC3D,CAAC;oBACJ,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,OAAO,CAAC,KAAK,CACX,iDAAiD,EACjD,KAAK,CACN,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aaex-file-router",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "A file-based routing system for React projects that automatically generates routes from your file structure. Similar to Next.js App Router or Remix file conventions.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
"exports":{
|
|
18
18
|
".": "./dist/index.js",
|
|
19
|
-
"./core": "./dist/core/index.js"
|
|
19
|
+
"./core": "./dist/core/index.js",
|
|
20
|
+
"./plugin": "./dist/plugin/index.js"
|
|
20
21
|
},
|
|
21
22
|
"author": "TmRAaEx",
|
|
22
23
|
"license": "MIT",
|