aaex-file-router 1.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +270 -0
  3. package/dist/core/FileScanner.d.ts +28 -0
  4. package/dist/core/FileScanner.d.ts.map +1 -0
  5. package/dist/core/FileScanner.js +80 -0
  6. package/dist/core/FileScanner.js.map +1 -0
  7. package/dist/core/RouteGenerator.d.ts +22 -0
  8. package/dist/core/RouteGenerator.d.ts.map +1 -0
  9. package/dist/core/RouteGenerator.js +170 -0
  10. package/dist/core/RouteGenerator.js.map +1 -0
  11. package/dist/core/types.d.ts +8 -0
  12. package/dist/core/types.d.ts.map +1 -0
  13. package/dist/core/types.js +2 -0
  14. package/dist/core/types.js.map +1 -0
  15. package/dist/index.d.ts +5 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +5 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/pages/index.d.ts +1 -0
  20. package/dist/pages/index.d.ts.map +1 -0
  21. package/dist/pages/index.js +2 -0
  22. package/dist/pages/index.js.map +1 -0
  23. package/dist/pages/test/index.d.ts +1 -0
  24. package/dist/pages/test/index.d.ts.map +1 -0
  25. package/dist/pages/test/index.js +2 -0
  26. package/dist/pages/test/index.js.map +1 -0
  27. package/dist/pages/test/layout.d.ts +1 -0
  28. package/dist/pages/test/layout.d.ts.map +1 -0
  29. package/dist/pages/test/layout.js +2 -0
  30. package/dist/pages/test/layout.js.map +1 -0
  31. package/dist/plugin.d.ts +11 -0
  32. package/dist/plugin.d.ts.map +1 -0
  33. package/dist/plugin.js +46 -0
  34. package/dist/plugin.js.map +1 -0
  35. package/dist/test/index.d.ts +2 -0
  36. package/dist/test/index.d.ts.map +1 -0
  37. package/dist/test/index.js +26 -0
  38. package/dist/test/index.js.map +1 -0
  39. package/dist/test/routes.d.ts +4 -0
  40. package/dist/test/routes.d.ts.map +1 -0
  41. package/dist/test/routes.js +20 -0
  42. package/dist/test/routes.js.map +1 -0
  43. package/dist/test/routes.ts +26 -0
  44. package/dist/vite.config.d.ts +3 -0
  45. package/dist/vite.config.d.ts.map +1 -0
  46. package/dist/vite.config.js +22 -0
  47. package/dist/vite.config.js.map +1 -0
  48. package/package.json +38 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 TmRAaEx
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,270 @@
1
+ # AAEX File Router
2
+
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
+
5
+ ## Features
6
+
7
+ - **Automatic Route Generation**: Routes are generated based on your file and folder structure
8
+ - **Layout Support**: Create `layout.tsx` files to wrap nested routes
9
+ - **Static & Lazy Loading**: Top-level routes use static imports, nested routes use lazy loading
10
+ - **Hot Reload**: Vite plugin watches for file changes and regenerates routes automatically
11
+ - **TypeScript Support**: Full TypeScript support with generated route types
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install aaex-file-router
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ### 1. Create your pages structure
22
+
23
+ ```
24
+ src/pages/
25
+ ├── index.tsx # Root page "/"
26
+ ├── about.tsx # Route "/about"
27
+ └── test/
28
+ ├── layout.tsx # Layout wrapper for /test/* routes
29
+ ├── index.tsx # Route "/test"
30
+ └── hello.tsx # Route "/test/hello"
31
+ ```
32
+
33
+ ### 2. Configure Vite
34
+
35
+ ```typescript
36
+ // vite.config.ts
37
+ import { defineConfig } from 'vite';
38
+ import react from '@vitejs/plugin-react';
39
+ import { aaexFileRouter } from 'aaex-file-router';
40
+
41
+ export default defineConfig({
42
+ plugins: [
43
+ react(),
44
+ aaexFileRouter({
45
+ pagesDir: './src/pages', //page files location(optional: default ./src/pages)
46
+ outputFile: './src/routes.ts', //generated routes (default: ./src/routes.ts)
47
+ }),
48
+ ],
49
+ });
50
+ ```
51
+
52
+ ### 3. Use in your app
53
+
54
+ ```typescript
55
+ // src/main.tsx
56
+ import React from 'react';
57
+ import ReactDOM from 'react-dom/client';
58
+ import { RouterProvider, createBrowserRouter } from 'react-router-dom';
59
+ import routes from './routes';
60
+
61
+ const router = createBrowserRouter(routes);
62
+
63
+ ReactDOM.createRoot(document.getElementById('root')!).render(
64
+ <React.StrictMode>
65
+ <RouterProvider router={router} />
66
+ </React.StrictMode>
67
+ );
68
+ ```
69
+
70
+ ## File Conventions
71
+
72
+ ### `index.tsx`
73
+ Renders at the parent route path.
74
+ ```
75
+ pages/index.tsx → "/"
76
+ pages/about/index.tsx → "/about"
77
+ ```
78
+
79
+ ### `layout.tsx`
80
+ Wraps all sibling and nested routes. Children are rendered in an `<Outlet />`.
81
+ ```typescript
82
+ // pages/admin/layout.tsx
83
+ import { Outlet } from 'react-router-dom';
84
+
85
+ export default function AdminLayout() {
86
+ return (
87
+ <div>
88
+ <nav>Admin Navigation</nav>
89
+ <Outlet /> {/* Nested routes render here */}
90
+ </div>
91
+ );
92
+ }
93
+ ```
94
+
95
+ ### Named files
96
+ Any other `.tsx` file becomes a route based on its filename.
97
+ ```
98
+ pages/about.tsx → "/about"
99
+ pages/blog/post.tsx → "/blog/post"
100
+ ```
101
+
102
+ ## Generated Routes File
103
+
104
+ The plugin generates a `routes.ts` file with all your routes:
105
+
106
+ ```typescript
107
+ // src/routes.ts
108
+ // AUTO GENERATED: DO NOT EDIT
109
+ import React from 'react';
110
+ import Index from './pages/index.tsx';
111
+ import AdminLayout from './pages/admin/layout.tsx';
112
+ import type { RouteObject } from 'react-router-dom';
113
+
114
+ const routes: RouteObject[] = [
115
+ {
116
+ path: '/',
117
+ element: React.createElement(Index),
118
+ },
119
+ {
120
+ path: 'admin',
121
+ element: React.createElement(AdminLayout),
122
+ children: [
123
+ // nested routes...
124
+ ],
125
+ },
126
+ ];
127
+
128
+ export default routes;
129
+ ```
130
+
131
+ ## Route Resolution Examples
132
+
133
+ | File Structure | Route Path |
134
+ |---|---|
135
+ | `pages/index.tsx` | `/` |
136
+ | `pages/about.tsx` | `/about` |
137
+ | `pages/blog/index.tsx` | `/blog` |
138
+ | `pages/blog/post.tsx` | `/blog/post` |
139
+ | `pages/admin/layout.tsx` + children | `/admin/*` (grouped) |
140
+
141
+ ## Layouts
142
+
143
+ Layouts wrap their child routes and provide shared UI:
144
+
145
+ ```typescript
146
+ // pages/dashboard/layout.tsx
147
+ import { Outlet } from 'react-router-dom';
148
+
149
+ export default function DashboardLayout() {
150
+ return (
151
+ <div className="dashboard">
152
+ <Sidebar />
153
+ <main>
154
+ <Outlet />
155
+ </main>
156
+ </div>
157
+ );
158
+ }
159
+ ```
160
+
161
+ All routes in `pages/dashboard/*` will render inside this layout.
162
+
163
+ ## API Reference
164
+
165
+ ### FileScanner
166
+ Scans the file system and converts files into a structured format.
167
+
168
+ ```typescript
169
+ import { FileScanner } from 'aaex-file-router';
170
+
171
+ const scanner = new FileScanner('./src/pages');
172
+ const fileData = await scanner.get_file_data();
173
+ ```
174
+
175
+ ### RouteGenerator
176
+ Converts file structure into React Router route configuration.
177
+
178
+ ```typescript
179
+ import { RouteGenerator } from 'aaex-file-router';
180
+
181
+ const generator = new RouteGenerator();
182
+ const routesCode = await generator.generateComponentsMap(fileData);
183
+ ```
184
+
185
+ ### aaexFileRouter (Vite Plugin)
186
+ Automatically watches for file changes and regenerates routes.
187
+
188
+ ```typescript
189
+ import { aaexFileRouter } from 'aaex-file-router';
190
+
191
+ export default defineConfig({
192
+ plugins: [
193
+ aaexFileRouter({
194
+ pagesDir: './src/pages',
195
+ outputFile: './src/routes.ts',
196
+ }),
197
+ ],
198
+ });
199
+ ```
200
+
201
+ ## How It Works
202
+
203
+ 1. **File Scanning**: Recursively scans your pages directory and builds a file tree
204
+ 2. **Route Generation**: Converts the file structure into React Router `RouteObject` format
205
+ 3. **Smart Importing**:
206
+ - Top-level files use static imports for faster initial load
207
+ - Nested/grouped routes use lazy loading for code splitting
208
+ - Layout files are statically imported as route wrappers
209
+ 4. **Auto-Regeneration**: Vite plugin watches for changes and automatically regenerates `routes.ts`
210
+
211
+ ## Performance Considerations
212
+
213
+ - **Static Imports**: Top-level routes are statically imported, included in the main bundle
214
+ - **Code Splitting**: Routes nested in layout groups are lazy-loaded, improving initial bundle size
215
+ - **Watch Mode**: File watching only runs in development (`vite serve`), not in production builds
216
+
217
+ ## Common Patterns
218
+
219
+ ### Shared Layout
220
+ ```
221
+ pages/
222
+ ├── layout.tsx # Wraps entire app
223
+ ├── index.tsx
224
+ └── about.tsx
225
+ ```
226
+
227
+ ### Nested Layouts
228
+ ```
229
+ pages/
230
+ ├── layout.tsx # Root layout
231
+ ├── admin/
232
+ │ ├── layout.tsx # Admin layout (inherits from root)
233
+ │ ├── index.tsx
234
+ │ └── users.tsx
235
+ ```
236
+
237
+ ### Route Groups Without Layout
238
+ ```
239
+ pages/
240
+ ├── blog/
241
+ │ ├── post.tsx # Routes as /blog/post (no grouping)
242
+ │ └── author.tsx # Routes as /blog/author
243
+ ```
244
+
245
+ ## Troubleshooting
246
+
247
+ ### Routes not updating on file change
248
+ - Ensure Vite dev server is running (`npm run dev`)
249
+ - Check that `pagesDir` in vite config matches your actual pages directory
250
+
251
+ ### Duplicate imports in generated file
252
+ - This shouldn't happen, but if it does, try restarting the dev server
253
+ - Check for files with the same name in different directories
254
+
255
+ ### Unexpected route paths
256
+ - Remember: `index.tsx` files inherit their parent's path
257
+ - Directories without `layout.tsx` flatten their children into absolute routes
258
+ - File names are converted to lowercase for routes
259
+
260
+ <!-- ## Contributing
261
+
262
+ Contributions are welcome! Please feel free to submit a Pull Request. -->
263
+
264
+ ## License
265
+
266
+ MIT
267
+
268
+ <!-- ## Support
269
+
270
+ For issues, questions, or suggestions, please open an issue on GitHub. -->
@@ -0,0 +1,28 @@
1
+ export interface FileData {
2
+ name: string;
3
+ relative_path: string;
4
+ parent_path: string;
5
+ isDirectory: boolean;
6
+ children?: FileData[];
7
+ }
8
+ export declare class FileScanner {
9
+ page_dir: string;
10
+ topLevelImports: string[];
11
+ constructor(pages_dir: string);
12
+ /**
13
+ * Recursively scan directory and flatten all files/folders
14
+ * Adds a parentPath property to each entry to track hierarchy
15
+ */
16
+ private scan_files;
17
+ /**
18
+ * Convert flat Dirent array into nested FileData structure
19
+ * Rebuilds directory hierarchy and filters children by parent path
20
+ */
21
+ private build_file_data;
22
+ /**
23
+ * Public entry point: scans pages directory and returns nested FileData structure
24
+ * Handles both flat scanning and hierarchical rebuilding
25
+ */
26
+ get_file_data(): Promise<FileData[]>;
27
+ }
28
+ //# sourceMappingURL=FileScanner.d.ts.map
@@ -0,0 +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;IACjB,eAAe,EAAE,MAAM,EAAE,CAAM;gBAEnB,SAAS,EAAE,MAAM;IAI7B;;;OAGG;YACW,UAAU;IAmBxB;;;OAGG;YACW,eAAe;IA+C7B;;;OAGG;IACU,aAAa,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;CAKlD"}
@@ -0,0 +1,80 @@
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
3
+ export class FileScanner {
4
+ page_dir;
5
+ topLevelImports = [];
6
+ constructor(pages_dir) {
7
+ this.page_dir = pages_dir;
8
+ }
9
+ /**
10
+ * Recursively scan directory and flatten all files/folders
11
+ * Adds a parentPath property to each entry to track hierarchy
12
+ */
13
+ async scan_files(dir) {
14
+ const entries = await fs.readdir(dir, { withFileTypes: true });
15
+ let result = [];
16
+ for (const entry of entries) {
17
+ // Store parent directory path for later reference
18
+ entry.parentPath = dir;
19
+ result.push(entry);
20
+ // Recursively scan subdirectories and flatten results
21
+ if (entry.isDirectory()) {
22
+ const subFiles = await this.scan_files(path.join(dir, entry.name));
23
+ result = result.concat(subFiles);
24
+ }
25
+ }
26
+ return result;
27
+ }
28
+ /**
29
+ * Convert flat Dirent array into nested FileData structure
30
+ * Rebuilds directory hierarchy and filters children by parent path
31
+ */
32
+ async build_file_data(files) {
33
+ const result = [];
34
+ for (const file of files) {
35
+ const fullpath = path.join(file.parentPath, file.name);
36
+ // Convert backslashes to forward slashes for cross-platform path consistency
37
+ const relative = path
38
+ .relative(process.cwd(), fullpath)
39
+ .replace(/\\/g, "/")
40
+ .trim();
41
+ if (file.isDirectory()) {
42
+ // Filter files to find only direct children of this directory
43
+ const children = files.filter((file) => file.parentPath === fullpath);
44
+ result.push({
45
+ name: file.name,
46
+ parent_path:
47
+ // Normalize parent path with forward slashes and trailing slash
48
+ path
49
+ .relative(process.cwd(), file.parentPath)
50
+ .replace(/\\/g, "/") + "/",
51
+ relative_path: relative,
52
+ isDirectory: true,
53
+ children: await this.build_file_data(children),
54
+ });
55
+ continue;
56
+ }
57
+ result.push({
58
+ name: file.name,
59
+ parent_path:
60
+ // Normalize parent path with forward slashes and trailing slash
61
+ path
62
+ .relative(process.cwd(), file.parentPath)
63
+ .replace(/\\/g, "/") + "/",
64
+ relative_path: relative,
65
+ isDirectory: false,
66
+ });
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Public entry point: scans pages directory and returns nested FileData structure
72
+ * Handles both flat scanning and hierarchical rebuilding
73
+ */
74
+ async get_file_data() {
75
+ const raw = await this.scan_files(path.join(process.cwd(), this.page_dir));
76
+ const result = await this.build_file_data(raw);
77
+ return result;
78
+ }
79
+ }
80
+ //# sourceMappingURL=FileScanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileScanner.js","sourceRoot":"","sources":["../../src/core/FileScanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { FileData } from "./FileScanner";
2
+ export declare class RouteGenerator {
3
+ private topLevelImports;
4
+ private importSet;
5
+ private processedFiles;
6
+ private clearImports;
7
+ /**
8
+ * Recursively converts FileData tree into React Router RouteConfig array
9
+ * Handles layout files, index files, and nested routes with proper pathing
10
+ * @param fileData - Array of files/folders to process
11
+ * @param parentPath - Current path context for nested routes (empty for root)
12
+ */
13
+ private fileDataToRoutes;
14
+ /**
15
+ * Generates a complete routes configuration file as a string
16
+ * Includes all imports and route definitions in valid TypeScript/React code
17
+ * @param fileData - FileData tree from FileScanner
18
+ * @returns Complete routes file content ready to be written to disk
19
+ */
20
+ generateComponentsMap(fileData: FileData[]): Promise<string>;
21
+ }
22
+ //# sourceMappingURL=RouteGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouteGenerator.d.ts","sourceRoot":"","sources":["../../src/core/RouteGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AASzC,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,cAAc,CAA0B;IAEhD,OAAO,CAAC,YAAY;IAGpB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IA6KxB;;;;;OAKG;IACU,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CA6B1E"}
@@ -0,0 +1,170 @@
1
+ export class RouteGenerator {
2
+ topLevelImports = [];
3
+ importSet = new Set();
4
+ processedFiles = new Set();
5
+ clearImports() {
6
+ this.topLevelImports = [];
7
+ }
8
+ /**
9
+ * Recursively converts FileData tree into React Router RouteConfig array
10
+ * Handles layout files, index files, and nested routes with proper pathing
11
+ * @param fileData - Array of files/folders to process
12
+ * @param parentPath - Current path context for nested routes (empty for root)
13
+ */
14
+ fileDataToRoutes(fileData, parentPath = "", flattenPrefix = "", inGroup = false) {
15
+ const routes = [];
16
+ const processedIndexes = new Set();
17
+ /**
18
+ * Utility function to safely join path segments
19
+ * - Filters out empty/falsy parts to avoid "///" in paths
20
+ * - Joins with "/" separator
21
+ * - Replaces all backslashes with forward slashes for cross-platform consistency
22
+ * Example: posixJoin("test", "", "hello") -> "test/hello"
23
+ * Example: posixJoin("pages\\test", "hello") -> "pages/test/hello"
24
+ */
25
+ const posixJoin = (...parts) => parts.filter(Boolean).join("/").replace(/\\/g, "/");
26
+ for (const file of fileData) {
27
+ // Skip files already emitted during this generation
28
+ if (!file.isDirectory && this.processedFiles.has(file.relative_path)) {
29
+ continue;
30
+ }
31
+ if (file.isDirectory && file.children && file.children.length > 0) {
32
+ const layoutChild = file.children.find((c) => !c.isDirectory && /^layout\.(tsx|jsx|ts|js)$/i.test(c.name));
33
+ if (!layoutChild) {
34
+ // No layout -> flatten children into absolute routes
35
+ const newFlatten = posixJoin(flattenPrefix, file.name.toLowerCase());
36
+ routes.push(...this.fileDataToRoutes(file.children, parentPath, newFlatten, false));
37
+ continue;
38
+ }
39
+ // Directory has layout -> import layout statically
40
+ const layoutPath = layoutChild.relative_path.replace(/^src[\/\\]/, "./");
41
+ const layoutImportName = `TestLayout`;
42
+ this.topLevelImports.push(`import ${layoutImportName} from '${layoutPath}';`);
43
+ this.processedFiles.add(layoutChild.relative_path); // Mark layout as processed
44
+ const childrenRoutes = [];
45
+ // Process children - filter out layout files
46
+ const nonLayoutChildren = file.children.filter((c) => !/^layout\.(tsx|jsx|ts|js)$/i.test(c.name));
47
+ for (const child of nonLayoutChildren) {
48
+ if (child.isDirectory) {
49
+ // Recursively handle child directories
50
+ childrenRoutes.push(...this.fileDataToRoutes([child], posixJoin(parentPath, file.name), "", true));
51
+ }
52
+ else {
53
+ const childNameWithoutExt = child.name.replace(/\.[jt]sx?$/, "");
54
+ const childPath = child.relative_path.replace(/^src[\/\\]/, "./");
55
+ const isIndexFile = childNameWithoutExt.toLowerCase() === "index";
56
+ if (isIndexFile) {
57
+ childrenRoutes.push({
58
+ path: "",
59
+ element: `React.createElement(React.lazy(() => import('${childPath}')))`,
60
+ });
61
+ }
62
+ else {
63
+ childrenRoutes.push({
64
+ path: childNameWithoutExt.toLowerCase(),
65
+ element: `React.createElement(React.lazy(() => import('${childPath}')))`,
66
+ });
67
+ }
68
+ // Mark child as processed so it doesn't get added as top-level
69
+ this.processedFiles.add(child.relative_path);
70
+ }
71
+ }
72
+ routes.push({
73
+ path: file.name.toLowerCase(),
74
+ element: `React.createElement(${layoutImportName})`,
75
+ children: childrenRoutes.length ? childrenRoutes : undefined,
76
+ });
77
+ }
78
+ else if (!file.isDirectory) {
79
+ const nameWithoutExt = file.name.replace(/\.[jt]sx?$/, "");
80
+ const isIndexFile = nameWithoutExt.toLowerCase() === "index";
81
+ // Skip if already processed
82
+ if (isIndexFile && processedIndexes.has(file.relative_path)) {
83
+ continue;
84
+ }
85
+ // If a sibling directory with the same name exists, skip this file
86
+ const siblingDirExists = fileData.some((f) => f.isDirectory &&
87
+ f.name.toLowerCase() === nameWithoutExt.toLowerCase());
88
+ if (siblingDirExists) {
89
+ this.processedFiles.add(file.relative_path);
90
+ continue;
91
+ }
92
+ // Skip layout files
93
+ if (/^layout\.(tsx|jsx|ts|js)$/i.test(file.name)) {
94
+ this.processedFiles.add(file.relative_path);
95
+ continue;
96
+ }
97
+ // Determine path
98
+ const fileSegment = isIndexFile ? "" : nameWithoutExt.toLowerCase();
99
+ let fullPath;
100
+ if (flattenPrefix) {
101
+ fullPath = posixJoin(flattenPrefix, fileSegment);
102
+ }
103
+ else if (inGroup) {
104
+ fullPath = fileSegment;
105
+ }
106
+ else if (parentPath) {
107
+ fullPath = posixJoin(parentPath, fileSegment);
108
+ }
109
+ else {
110
+ fullPath = isIndexFile ? "/" : fileSegment;
111
+ }
112
+ // Create import & avoid duplicates
113
+ const importNameBase = nameWithoutExt.replace(/[^a-zA-Z0-9]/g, "_");
114
+ const capitalized = importNameBase.charAt(0).toUpperCase() + importNameBase.slice(1);
115
+ const filePath = file.relative_path.replace(/^src[\/\\]/, "./");
116
+ if (inGroup) {
117
+ // Lazy-load child component inside group
118
+ routes.push({
119
+ path: fullPath === "/" ? "" : fullPath.replace(/^\/+/, ""),
120
+ element: `React.createElement(React.lazy(() => import('${filePath}')))`,
121
+ });
122
+ }
123
+ else {
124
+ // Top-level files use static imports
125
+ if (!this.importSet.has(filePath)) {
126
+ this.topLevelImports.push(`import ${capitalized} from '${filePath}';`);
127
+ this.importSet.add(filePath);
128
+ }
129
+ routes.push({
130
+ path: fullPath === "/" ? "/" : fullPath.replace(/^\/+/, ""),
131
+ element: `React.createElement(${capitalized})`,
132
+ });
133
+ }
134
+ this.processedFiles.add(file.relative_path);
135
+ if (isIndexFile)
136
+ processedIndexes.add(file.relative_path);
137
+ }
138
+ }
139
+ return routes;
140
+ }
141
+ /**
142
+ * Generates a complete routes configuration file as a string
143
+ * Includes all imports and route definitions in valid TypeScript/React code
144
+ * @param fileData - FileData tree from FileScanner
145
+ * @returns Complete routes file content ready to be written to disk
146
+ */
147
+ async generateComponentsMap(fileData) {
148
+ // reset import & processed tracking each generation to avoid duplication across regen
149
+ this.topLevelImports = [];
150
+ this.importSet = new Set();
151
+ this.processedFiles = new Set();
152
+ const routes = this.fileDataToRoutes(fileData);
153
+ const routesString = JSON.stringify(routes, null, 2)
154
+ // lazy imports were serialized as strings, restore them to function calls
155
+ .replace(/"React\.createElement\(React\.lazy\(\(\) => import\('(.*)'\)\)\)"/g, "React.createElement(React.lazy(() => import('$1')))")
156
+ // React.createElement(Component) serialized as string, unquote it
157
+ .replace(/"React\.createElement\((\w+)\)"/g, "React.createElement($1)");
158
+ const mapString = `//* AUTO GENERATED: DO NOT EDIT
159
+ import React from 'react';
160
+ ${this.topLevelImports.join("\n")}
161
+ import type { RouteObject } from 'react-router-dom';
162
+
163
+ const routes: RouteObject[] = ${routesString};
164
+
165
+ export default routes;
166
+ `;
167
+ return mapString;
168
+ }
169
+ }
170
+ //# sourceMappingURL=RouteGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouteGenerator.js","sourceRoot":"","sources":["../../src/core/RouteGenerator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface FileData {
2
+ name: string;
3
+ parent_path: string;
4
+ relative_path: string;
5
+ isDirectory: boolean;
6
+ children?: FileData[];
7
+ }
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CACxB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export { FileScanner } from './core/FileScanner.js';
2
+ export { RouteGenerator } from './core/RouteGenerator.js';
3
+ export { aaexFileRouter } from './plugin.js';
4
+ export type { VitePluginOptions } from './plugin.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { FileScanner } from './core/FileScanner.js';
2
+ export { RouteGenerator } from './core/RouteGenerator.js';
3
+ // export type { FileData } from './core/types';
4
+ export { aaexFileRouter } from './plugin.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,gDAAgD;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pages/index.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pages/index.tsx"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/test/index.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/pages/test/index.tsx"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/pages/test/layout.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/pages/test/layout.tsx"],"names":[],"mappings":""}
@@ -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.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,CA6CtE"}
package/dist/plugin.js ADDED
@@ -0,0 +1,46 @@
1
+ import { promises as fs } from '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.generateComponentsMap(fileData);
34
+ // Write routes file
35
+ await fs.writeFile(outputFile, routesCode, 'utf-8');
36
+ console.log(`✅ [aaex-file-router] Routes regenerated at ${outputFile}`);
37
+ }
38
+ catch (error) {
39
+ console.error('❌ [aaex-file-router] Error regenerating routes:', error);
40
+ }
41
+ }
42
+ });
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAO1D;;;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,qBAAqB,CAAC,QAAQ,CAAC,CAAC;wBAGnE,oBAAoB;wBACpB,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;wBAEpD,OAAO,CAAC,GAAG,CAAC,8CAA8C,UAAU,EAAE,CAAC,CAAC;oBAC1E,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,OAAO,CAAC,KAAK,CAAC,iDAAiD,EAAE,KAAK,CAAC,CAAC;oBAC1E,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import { fileURLToPath } from "url";
2
+ import { FileScanner } from "../core/FileScanner.js";
3
+ import { RouteGenerator } from "../core/RouteGenerator.js";
4
+ import { promises as fs } from "fs";
5
+ import path from "path";
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const pagesDir = "/src/pages";
9
+ async function generateRoutes() {
10
+ try {
11
+ const scanner = new FileScanner(pagesDir);
12
+ const fileData = await scanner.get_file_data();
13
+ const generator = new RouteGenerator();
14
+ const routeMap = await generator.generateComponentsMap(fileData);
15
+ // Go up from dist/test back to src/test
16
+ const srcTestDir = __dirname.replace(/dist[\\/]test/, "src/test");
17
+ const filePath = path.join(srcTestDir, "routes.ts");
18
+ await fs.writeFile(filePath, routeMap, "utf-8");
19
+ console.log(`Routes file created at: ${filePath}`);
20
+ }
21
+ catch (error) {
22
+ console.error("Error generating routes:", error);
23
+ }
24
+ }
25
+ generateRoutes();
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3C,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B,KAAK,UAAU,cAAc;IAC3B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;QAE/C,MAAM,SAAS,GAAG,IAAI,cAAc,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAEjE,wCAAwC;QACxC,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;QAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QACpD,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEhD,OAAO,CAAC,GAAG,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,cAAc,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { RouteObject } from 'react-router-dom';
2
+ declare const routes: RouteObject[];
3
+ export default routes;
4
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/test/routes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,QAAA,MAAM,MAAM,EAAE,WAAW,EAexB,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import Index from './pages/index.tsx';
3
+ const routes = [
4
+ {
5
+ "path": "/",
6
+ "element": React.createElement(Index)
7
+ },
8
+ {
9
+ "path": "test",
10
+ "element": React.createElement(React.lazy(() => import('./pages/test/layout.tsx'))),
11
+ "children": [
12
+ {
13
+ "path": "",
14
+ "element": React.createElement(React.lazy(() => import('./pages/test/index.tsx')))
15
+ }
16
+ ]
17
+ }
18
+ ];
19
+ export default routes;
20
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/test/routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,mBAAmB,CAAC;AAGtC,MAAM,MAAM,GAAkB;IAC5B;QACE,MAAM,EAAE,GAAG;QACX,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC;KACtC;IACD;QACE,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,CAAC;QACnF,UAAU,EAAE;YACV;gBACE,MAAM,EAAE,EAAE;gBACV,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,CAAC;aACnF;SACF;KACF;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,26 @@
1
+
2
+ import React from 'react';
3
+ import Index from './/pages/index.tsx';
4
+ import type { RouteObject } from 'react-router-dom';
5
+
6
+ const routes: RouteObject[] = [
7
+ {
8
+ "path": "/",
9
+ "element": "<Index/>"
10
+ },
11
+ {
12
+ "path": "test",
13
+ "element": React.createElement(React.lazy(() => import('.//pages/test/layout.tsx'))),
14
+ "children": [
15
+ {
16
+ "path": "",
17
+ "element": React.createElement(React.lazy(() => import('.//pages/test/index.tsx')))
18
+ }
19
+ ]
20
+ }
21
+ ];
22
+
23
+ // Convert stringified lazy imports back to actual function calls
24
+ // Regex: "React.createElement(React.lazy(() => import('(.*)')))" - matches stringified lazy import wrappers
25
+ // Replaces them with actual lazy import functions (removes surrounding quotes)
26
+ export default routes;
@@ -0,0 +1,3 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
3
+ //# sourceMappingURL=vite.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.config.d.ts","sourceRoot":"","sources":["../src/vite.config.ts"],"names":[],"mappings":";AAGA,wBAkBG"}
@@ -0,0 +1,22 @@
1
+ import { defineConfig } from 'vite';
2
+ import dts from 'vite-plugin-dts';
3
+ export default defineConfig({
4
+ build: {
5
+ lib: {
6
+ entry: 'src/index.ts',
7
+ name: 'AaexFileRouter',
8
+ fileName: (format) => `aaex-file-router.${format === 'es' ? 'js' : 'cjs'}`,
9
+ },
10
+ rollupOptions: {
11
+ external: ['react', 'react-router-dom', 'vite'],
12
+ output: {
13
+ globals: {
14
+ react: 'React',
15
+ 'react-router-dom': 'ReactRouterDOM',
16
+ },
17
+ },
18
+ },
19
+ },
20
+ plugins: [dts()], // Generate TypeScript declarations
21
+ });
22
+ //# sourceMappingURL=vite.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.config.js","sourceRoot":"","sources":["../src/vite.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,GAAG,MAAM,iBAAiB,CAAC;AAElC,eAAe,YAAY,CAAC;IAC1B,KAAK,EAAE;QACL,GAAG,EAAE;YACH,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,gBAAgB;YACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,oBAAoB,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE;SAC3E;QACD,aAAa,EAAE;YACb,QAAQ,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,MAAM,CAAC;YAC/C,MAAM,EAAE;gBACN,OAAO,EAAE;oBACP,KAAK,EAAE,OAAO;oBACd,kBAAkB,EAAE,gBAAgB;iBACrC;aACF;SACF;KACF;IACD,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,mCAAmC;CACtD,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "aaex-file-router",
3
+ "version": "1.0.0",
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
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "build": "npx tsc",
9
+ "dev": "npx tsc --watch"
10
+ },
11
+ "keywords": [
12
+ "react",
13
+ "router",
14
+ "vite-plugin"
15
+ ],
16
+ "author": "TmRAaEx",
17
+ "license": "MIT",
18
+ "type": "module",
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "LICENCE"
23
+ ],
24
+ "devDependencies": {
25
+ "@types/node": "^24.10.1",
26
+ "typescript": "^5.9.3",
27
+ "vite": "^7.2.4",
28
+ "vite-plugin-dts": "^4.5.4"
29
+ },
30
+ "dependencies": {
31
+ "fs": "^0.0.1-security"
32
+ },
33
+ "peerDependencies": {
34
+ "react": "^18.0.0",
35
+ "react-router-dom": "^6.0.0",
36
+ "vite": "^4.0.0"
37
+ }
38
+ }