aaex-file-router 1.3.0 → 1.3.2
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/FileScanner.d.ts +11 -1
- package/dist/core/FileScanner.d.ts.map +1 -1
- package/dist/core/FileScanner.js +23 -8
- package/dist/core/FileScanner.js.map +1 -1
- package/dist/core/RouteGenerator.d.ts +13 -13
- package/dist/core/RouteGenerator.d.ts.map +1 -1
- package/dist/core/RouteGenerator.js +65 -78
- package/dist/core/RouteGenerator.js.map +1 -1
- 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.2
|
|
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: [
|
|
@@ -10,10 +10,20 @@ export declare class FileScanner {
|
|
|
10
10
|
constructor(pages_dir: string);
|
|
11
11
|
/**
|
|
12
12
|
* Recursively scan directory and return nested FileData
|
|
13
|
+
* - Scans files of a given folder
|
|
14
|
+
* - converts and returns as usable data
|
|
15
|
+
* - Format:
|
|
16
|
+
* {name: string,
|
|
17
|
+
* relative_path: string,
|
|
18
|
+
* parent_path: string,
|
|
19
|
+
* isDirectory: string
|
|
20
|
+
* }
|
|
21
|
+
* @param dir string
|
|
22
|
+
* @returns FileData[]
|
|
13
23
|
*/
|
|
14
24
|
private scan_files;
|
|
15
25
|
/**
|
|
16
|
-
* Public
|
|
26
|
+
* Public file point: returns nested FileData structure
|
|
17
27
|
*/
|
|
18
28
|
get_file_data(): Promise<FileData[]>;
|
|
19
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileScanner.d.ts","sourceRoot":"","sources":["../../src/core/FileScanner.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,qBAAa,WAAW;IACtB,QAAQ,EAAE,MAAM,CAAC;gBAEL,SAAS,EAAE,MAAM;IAI7B
|
|
1
|
+
{"version":3,"file":"FileScanner.d.ts","sourceRoot":"","sources":["../../src/core/FileScanner.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,qBAAa,WAAW;IACtB,QAAQ,EAAE,MAAM,CAAC;gBAEL,SAAS,EAAE,MAAM;IAI7B;;;;;;;;;;;;OAYG;YACW,UAAU;IA+BxB;;OAEG;IACU,aAAa,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;CAKlD"}
|
package/dist/core/FileScanner.js
CHANGED
|
@@ -7,21 +7,36 @@ export class FileScanner {
|
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Recursively scan directory and return nested FileData
|
|
10
|
+
* - Scans files of a given folder
|
|
11
|
+
* - converts and returns as usable data
|
|
12
|
+
* - Format:
|
|
13
|
+
* {name: string,
|
|
14
|
+
* relative_path: string,
|
|
15
|
+
* parent_path: string,
|
|
16
|
+
* isDirectory: string
|
|
17
|
+
* }
|
|
18
|
+
* @param dir string
|
|
19
|
+
* @returns FileData[]
|
|
10
20
|
*/
|
|
11
21
|
async scan_files(dir) {
|
|
12
|
-
|
|
22
|
+
//scans the parent folder and outputs an array of files
|
|
23
|
+
const files = await fs.readdir(dir, { withFileTypes: true });
|
|
13
24
|
const result = [];
|
|
14
|
-
for (const
|
|
15
|
-
const fullPath = path.join(dir,
|
|
16
|
-
const relativePath = path
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
const fullPath = path.join(dir, file.name);
|
|
27
|
+
const relativePath = path
|
|
28
|
+
.relative(process.cwd(), fullPath)
|
|
29
|
+
.replace(/\\/g, "/");
|
|
17
30
|
const parentPath = path.relative(process.cwd(), dir).replace(/\\/g, "/") + "/";
|
|
31
|
+
// convert to usable data
|
|
18
32
|
const fileData = {
|
|
19
|
-
name:
|
|
33
|
+
name: file.name,
|
|
20
34
|
relative_path: relativePath,
|
|
21
35
|
parent_path: parentPath,
|
|
22
|
-
isDirectory:
|
|
36
|
+
isDirectory: file.isDirectory(),
|
|
23
37
|
};
|
|
24
|
-
|
|
38
|
+
//recurivly scan directories for more files
|
|
39
|
+
if (file.isDirectory()) {
|
|
25
40
|
fileData.children = await this.scan_files(fullPath);
|
|
26
41
|
}
|
|
27
42
|
result.push(fileData);
|
|
@@ -29,7 +44,7 @@ export class FileScanner {
|
|
|
29
44
|
return result;
|
|
30
45
|
}
|
|
31
46
|
/**
|
|
32
|
-
* Public
|
|
47
|
+
* Public file point: returns nested FileData structure
|
|
33
48
|
*/
|
|
34
49
|
async get_file_data() {
|
|
35
50
|
const fullDir = path.join(process.cwd(), this.page_dir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileScanner.js","sourceRoot":"","sources":["../../src/core/FileScanner.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"FileScanner.js","sourceRoot":"","sources":["../../src/core/FileScanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,MAAM,CAAC;AAUxB,MAAM,OAAO,WAAW;IACtB,QAAQ,CAAS;IAEjB,YAAY,SAAiB;QAC3B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;;;OAYG;IACK,KAAK,CAAC,UAAU,CAAC,GAAW;QAClC,uDAAuD;QACvD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAe,EAAE,CAAC;QAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,YAAY,GAAG,IAAI;iBACtB,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC;iBACjC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACvB,MAAM,UAAU,GACd,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;YAE9D,yBAAyB;YACzB,MAAM,QAAQ,GAAa;gBACzB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,aAAa,EAAE,YAAY;gBAC3B,WAAW,EAAE,UAAU;gBACvB,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;aAChC,CAAC;YACF,2CAA2C;YAC3C,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvB,QAAQ,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACtD,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,aAAa;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
@@ -8,21 +8,21 @@ interface FileNode {
|
|
|
8
8
|
export declare class RouteGenerator {
|
|
9
9
|
private topLevelImports;
|
|
10
10
|
private importSet;
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
/** Converts filenames/folders to URL-friendly paths, e.g., [id] -> :id */
|
|
12
|
+
private normalizeSegment;
|
|
13
|
+
/** Converts string to PascalCase for React component names */
|
|
14
|
+
private toPascal;
|
|
15
|
+
/** Adds import statement for codegen, avoiding duplicates */
|
|
16
|
+
private addImport;
|
|
17
|
+
/** Generates PascalCase import name, handles nested index files */
|
|
18
|
+
private getImportName;
|
|
19
|
+
private createDirectoryRoute;
|
|
20
|
+
private createFileRoute;
|
|
21
|
+
/** Converts file tree to nested RouteConfig array */
|
|
16
22
|
private fileDataToRoutes;
|
|
17
|
-
/**
|
|
18
|
-
* Generates a React Router routes file as a string
|
|
19
|
-
* @param fileData - FileData-like tree
|
|
20
|
-
*/
|
|
23
|
+
/** Generates a TypeScript routes file as a string */
|
|
21
24
|
generateRoutesFile(fileData: FileNode[]): Promise<string>;
|
|
22
|
-
/**
|
|
23
|
-
* Generates TypeScript type definition for all route paths
|
|
24
|
-
* @param fileData - FileData-like tree
|
|
25
|
-
*/
|
|
25
|
+
/** Generates a TypeScript union type for all routes */
|
|
26
26
|
generateRoutesTypeDef(fileData: FileNode[]): Promise<string>;
|
|
27
27
|
}
|
|
28
28
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteGenerator.d.ts","sourceRoot":"","sources":["../../src/core/RouteGenerator.ts"],"names":[],"mappings":"AAMA,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,SAAS,CAA0B;
|
|
1
|
+
{"version":3,"file":"RouteGenerator.d.ts","sourceRoot":"","sources":["../../src/core/RouteGenerator.ts"],"names":[],"mappings":"AAMA,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,SAAS,CAA0B;IAI3C,0EAA0E;IAC1E,OAAO,CAAC,gBAAgB;IAIxB,8DAA8D;IAC9D,OAAO,CAAC,QAAQ;IAMhB,6DAA6D;IAC7D,OAAO,CAAC,SAAS;IAQjB,mEAAmE;IACnE,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,eAAe;IAmBvB,qDAAqD;IACrD,OAAO,CAAC,gBAAgB;IAcxB,qDAAqD;IACxC,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBtE,uDAAuD;IAC1C,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CA6B1E"}
|
|
@@ -1,87 +1,77 @@
|
|
|
1
1
|
export class RouteGenerator {
|
|
2
2
|
topLevelImports = [];
|
|
3
|
-
importSet = new Set();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const toPascal = (str) => str
|
|
3
|
+
importSet = new Set(); // avoids duplicate imports
|
|
4
|
+
// ---------------- Helpers ----------------
|
|
5
|
+
/** Converts filenames/folders to URL-friendly paths, e.g., [id] -> :id */
|
|
6
|
+
normalizeSegment(name) {
|
|
7
|
+
return name.replace(/\[([^\]]+)\]/g, ":$1").toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
/** Converts string to PascalCase for React component names */
|
|
10
|
+
toPascal(str) {
|
|
11
|
+
return str
|
|
13
12
|
.replace(/\[|\]/g, "")
|
|
14
13
|
.replace(/(^\w|[-_]\w)/g, (m) => m.replace(/[-_]/, "").toUpperCase());
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
else {
|
|
49
|
-
const nameWithoutExt = file.name.replace(/\.[jt]sx?$/, "");
|
|
50
|
-
const isIndex = nameWithoutExt.toLowerCase() === "index";
|
|
51
|
-
// If child, path is relative; otherwise, top-level gets full path
|
|
52
|
-
let pathSegment = isIndex ? "" : normalizeSegment(nameWithoutExt);
|
|
53
|
-
if (isChild) {
|
|
54
|
-
// Children always use relative paths
|
|
55
|
-
pathSegment = pathSegment;
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
pathSegment = parentPath
|
|
59
|
-
? `${parentPath}/${pathSegment}`
|
|
60
|
-
: pathSegment;
|
|
61
|
-
}
|
|
62
|
-
const importName = getImportName(file, parentPath);
|
|
63
|
-
;
|
|
64
|
-
const importPath = `./${file.relative_path.replace(/^src[\/\\]/, "")}`;
|
|
65
|
-
if (!this.importSet.has(file.relative_path)) {
|
|
66
|
-
this.topLevelImports.push(`import ${importName} from '${importPath}';`);
|
|
67
|
-
this.importSet.add(file.relative_path);
|
|
68
|
-
}
|
|
69
|
-
routes.push({
|
|
70
|
-
path: pathSegment,
|
|
71
|
-
element: `React.createElement(${importName})`,
|
|
72
|
-
});
|
|
14
|
+
}
|
|
15
|
+
/** Adds import statement for codegen, avoiding duplicates */
|
|
16
|
+
addImport(file, importName) {
|
|
17
|
+
if (!this.importSet.has(file.relative_path)) {
|
|
18
|
+
const importPath = `./${file.relative_path.replace(/^src[\/\\]/, "")}`;
|
|
19
|
+
this.topLevelImports.push(`import ${importName} from '${importPath}';`);
|
|
20
|
+
this.importSet.add(file.relative_path);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Generates PascalCase import name, handles nested index files */
|
|
24
|
+
getImportName(file, parentPath) {
|
|
25
|
+
const nameWithoutExt = file.name.replace(/\.[jt]sx?$/, "");
|
|
26
|
+
if (nameWithoutExt.toLowerCase() === "index" && parentPath) {
|
|
27
|
+
const segments = parentPath.split("/").filter(Boolean);
|
|
28
|
+
return this.toPascal(segments.join("")) + "Index";
|
|
29
|
+
}
|
|
30
|
+
return this.toPascal(nameWithoutExt);
|
|
31
|
+
}
|
|
32
|
+
// ---------------- Route Creation ----------------
|
|
33
|
+
createDirectoryRoute(file) {
|
|
34
|
+
const route = { path: this.normalizeSegment(file.name) };
|
|
35
|
+
// Use layout if present
|
|
36
|
+
const layout = file.children?.find((f) => !f.isDirectory && /^layout\.(tsx|jsx|ts|js)$/i.test(f.name));
|
|
37
|
+
if (layout) {
|
|
38
|
+
const importName = `${this.toPascal(file.name)}Layout`;
|
|
39
|
+
this.addImport(layout, importName);
|
|
40
|
+
route.element = `React.createElement(${importName})`;
|
|
41
|
+
}
|
|
42
|
+
// Recursively add children routes
|
|
43
|
+
if (file.children?.length) {
|
|
44
|
+
const children = file.children.filter((f) => !/^layout\.(tsx|jsx|ts|js)$/i.test(f.name));
|
|
45
|
+
if (children.length) {
|
|
46
|
+
route.children = this.fileDataToRoutes(children, route.path, true);
|
|
73
47
|
}
|
|
74
48
|
}
|
|
75
|
-
return
|
|
49
|
+
return route;
|
|
50
|
+
}
|
|
51
|
+
createFileRoute(file, parentPath, isChild) {
|
|
52
|
+
const nameWithoutExt = file.name.replace(/\.[jt]sx?$/, "");
|
|
53
|
+
const isIndex = nameWithoutExt.toLowerCase() === "index";
|
|
54
|
+
let pathSegment = isIndex ? "" : this.normalizeSegment(nameWithoutExt);
|
|
55
|
+
if (!isChild && parentPath)
|
|
56
|
+
pathSegment = `${parentPath}/${pathSegment}`;
|
|
57
|
+
const importName = this.getImportName(file, parentPath);
|
|
58
|
+
this.addImport(file, importName);
|
|
59
|
+
return { path: pathSegment, element: `React.createElement(${importName})` };
|
|
60
|
+
}
|
|
61
|
+
// ---------------- Recursion ----------------
|
|
62
|
+
/** Converts file tree to nested RouteConfig array */
|
|
63
|
+
fileDataToRoutes(files, parentPath = "", isChild = false) {
|
|
64
|
+
return files.map((file) => file.isDirectory
|
|
65
|
+
? this.createDirectoryRoute(file)
|
|
66
|
+
: this.createFileRoute(file, parentPath, isChild));
|
|
76
67
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* @param fileData - FileData-like tree
|
|
80
|
-
*/
|
|
68
|
+
// ---------------- Code Generation ----------------
|
|
69
|
+
/** Generates a TypeScript routes file as a string */
|
|
81
70
|
async generateRoutesFile(fileData) {
|
|
82
71
|
this.topLevelImports = [];
|
|
83
|
-
this.importSet
|
|
72
|
+
this.importSet.clear();
|
|
84
73
|
const routes = this.fileDataToRoutes(fileData);
|
|
74
|
+
// Replace quotes around element strings with actual React code
|
|
85
75
|
const routesString = JSON.stringify(routes, null, 2).replace(/"React\.createElement\((\w+)\)"/g, "React.createElement($1)");
|
|
86
76
|
return `//* AUTO GENERATED: DO NOT EDIT
|
|
87
77
|
import React from 'react';
|
|
@@ -93,10 +83,7 @@ const routes: RouteObject[] = ${routesString};
|
|
|
93
83
|
export default routes;
|
|
94
84
|
`;
|
|
95
85
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Generates TypeScript type definition for all route paths
|
|
98
|
-
* @param fileData - FileData-like tree
|
|
99
|
-
*/
|
|
86
|
+
/** Generates a TypeScript union type for all routes */
|
|
100
87
|
async generateRoutesTypeDef(fileData) {
|
|
101
88
|
const routes = this.fileDataToRoutes(fileData);
|
|
102
89
|
const paths = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteGenerator.js","sourceRoot":"","sources":["../../src/core/RouteGenerator.ts"],"names":[],"mappings":"AAcA,MAAM,OAAO,cAAc;IACjB,eAAe,GAAa,EAAE,CAAC;IAC/B,SAAS,GAAgB,IAAI,GAAG,EAAE,CAAC
|
|
1
|
+
{"version":3,"file":"RouteGenerator.js","sourceRoot":"","sources":["../../src/core/RouteGenerator.ts"],"names":[],"mappings":"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"}
|
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.2",
|
|
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",
|