ts-file-router 1.0.5 â 2.0.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 +75 -6
- package/dist/generator.d.ts +3 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +69 -0
- package/dist/generatorWatcher.d.ts +3 -0
- package/dist/generatorWatcher.d.ts.map +1 -0
- package/dist/generatorWatcher.js +21 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -20
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +13 -0
- package/dist/serialize.d.ts +3 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +21 -0
- package/dist/types.d.ts +10 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -2
- package/package.json +30 -20
- package/dist/file-router.d.ts +0 -3
- package/dist/file-router.d.ts.map +0 -1
- package/dist/file-router.js +0 -84
package/README.md
CHANGED
|
@@ -7,12 +7,12 @@ Automatically scans folders and outputs a clean, structured `routes.ts` tree rea
|
|
|
7
7
|
|
|
8
8
|
## âĻ Features
|
|
9
9
|
|
|
10
|
-
- ð Recursive folder scanning
|
|
11
|
-
- ð Auto-generated `routes.ts` (TypeScript code, no `resolveJsonModule` required)
|
|
12
|
-
- âïļ Works perfectly with `React.lazy()` and dynamic imports
|
|
13
|
-
- ð Full TypeScript `.d.ts` definitions included
|
|
14
|
-
- ð§Đ Custom route file name (default: `page.ts`)
|
|
15
|
-
- ðŠķ Zero runtime dependencies
|
|
10
|
+
- ð Recursive folder scanning
|
|
11
|
+
- ð Auto-generated `routes.ts` (TypeScript code, no `resolveJsonModule` required)
|
|
12
|
+
- âïļ Works perfectly with `React.lazy()` and dynamic imports
|
|
13
|
+
- ð Full TypeScript `.d.ts` definitions included
|
|
14
|
+
- ð§Đ Custom route file name (default: `page.ts`)
|
|
15
|
+
- ðŠķ Zero runtime dependencies
|
|
16
16
|
- ðŊ Clean, formatted output with readable keys
|
|
17
17
|
|
|
18
18
|
---
|
|
@@ -23,3 +23,72 @@ Automatically scans folders and outputs a clean, structured `routes.ts` tree rea
|
|
|
23
23
|
npm install ts-file-router
|
|
24
24
|
# or
|
|
25
25
|
yarn add ts-file-router
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ðŊ Usage
|
|
32
|
+
|
|
33
|
+
Create a script to generate your routes. Example:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
// scripts/generate-routes.mjs
|
|
37
|
+
import { generateRoutes } from 'ts-file-router';
|
|
38
|
+
|
|
39
|
+
generateRoutes({
|
|
40
|
+
baseFolder: 'src/screens',
|
|
41
|
+
outputFile: 'src/screens/routes.ts',
|
|
42
|
+
routeFileName: 'page.tsx',
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## ðŊ Usage With Vite
|
|
47
|
+
|
|
48
|
+
Create a script to generate your routes. Example:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
// scripts/vite.config.ts
|
|
52
|
+
import { generateRoutesPlugin } from 'ts-file-router';
|
|
53
|
+
|
|
54
|
+
// https://vite.dev/config/
|
|
55
|
+
export default defineConfig({
|
|
56
|
+
plugins: [
|
|
57
|
+
generateRoutesPlugin({
|
|
58
|
+
baseFolder: 'src/screens',
|
|
59
|
+
outputFile: 'screens.ts',
|
|
60
|
+
exitCodeOnResolution: false,
|
|
61
|
+
}),
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## ðŊ Usage With Watcher (Chokidar peerDependencie)
|
|
67
|
+
|
|
68
|
+
Create a script to generate your routes. Example:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
// scripts/generate-routes.mjs
|
|
72
|
+
import { generateRoutesWithWatcher } from 'ts-file-router';
|
|
73
|
+
|
|
74
|
+
generateRoutesWithWatcher({
|
|
75
|
+
input: {
|
|
76
|
+
baseFolder: 'src/screens',
|
|
77
|
+
outputFile: 'screens.ts',
|
|
78
|
+
exitCodeOnResolution: false,
|
|
79
|
+
},
|
|
80
|
+
options: { debounce: 500 },
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## What this does
|
|
85
|
+
|
|
86
|
+
- baseFolder: Root directory where your screens/pages live
|
|
87
|
+
|
|
88
|
+
- routeFileName: File that represents a route (e.g. page.tsx)
|
|
89
|
+
|
|
90
|
+
- outputFile: Generated routes file (fully typed)
|
|
91
|
+
|
|
92
|
+
Run the script with:
|
|
93
|
+
|
|
94
|
+
node scripts/generate-routes.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGhE,eAAO,MAAM,cAAc,GAAI,kEAK5B,qBAAqB,SAqFvB,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { serialize } from './serialize.js';
|
|
4
|
+
export const generateRoutes = ({ baseFolder, outputFile, routeFileName = 'page.tsx', exitCodeOnResolution = true, }) => {
|
|
5
|
+
// Get the pages dir to resolve routes
|
|
6
|
+
const basePath = path.resolve(process.cwd(), baseFolder);
|
|
7
|
+
// Output file for routes
|
|
8
|
+
const output = path.resolve(basePath, outputFile);
|
|
9
|
+
const mapRoutes = async (dir) => {
|
|
10
|
+
const routes = {};
|
|
11
|
+
const directory = await fs.readdir(dir);
|
|
12
|
+
if (!directory.includes(routeFileName)) {
|
|
13
|
+
throw new Error(`Invalid pages structure: The folder "${dir}" must contain a ${routeFileName} file.`);
|
|
14
|
+
}
|
|
15
|
+
for (const file of directory) {
|
|
16
|
+
// ignore index files, underscore marked, or route file generated
|
|
17
|
+
if (file.includes('index') ||
|
|
18
|
+
file.startsWith('_') ||
|
|
19
|
+
file.includes(outputFile)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const fullPath = path.join(dir, file);
|
|
23
|
+
// Get directory info to control file or folder
|
|
24
|
+
const dirInfo = await fs.stat(fullPath);
|
|
25
|
+
// Path to browser sync if necessary
|
|
26
|
+
const relativePath = '/' + path.relative(basePath, dir).replaceAll(/\\/g, '/');
|
|
27
|
+
// Normalize import path to esm pattern
|
|
28
|
+
const importPath = './' + path.relative(basePath, fullPath).replaceAll(/\\/g, '/');
|
|
29
|
+
// Remove extension from file to naming the route
|
|
30
|
+
const key = path.basename(file, path.extname(file));
|
|
31
|
+
if (dirInfo.isDirectory()) {
|
|
32
|
+
// Dev friendly when enter in this conditional file is a directory(folder)
|
|
33
|
+
const directory = file;
|
|
34
|
+
// Recursively for sub directories
|
|
35
|
+
routes[directory] = await mapRoutes(fullPath);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// Mount the route object with path like "/folder" and import
|
|
39
|
+
// import will be like "(./baseFolder/file or ./baseFolder/folders).extension"
|
|
40
|
+
routes[key] = {
|
|
41
|
+
path: relativePath,
|
|
42
|
+
import: importPath,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return routes;
|
|
46
|
+
};
|
|
47
|
+
const createRoutes = async () => {
|
|
48
|
+
try {
|
|
49
|
+
// Create routes
|
|
50
|
+
const routes = await mapRoutes(basePath);
|
|
51
|
+
// Create ts file
|
|
52
|
+
serialize(routes, output);
|
|
53
|
+
// Promise writeFile was successfully resolved
|
|
54
|
+
console.log('ð Routes generated successfully!\n');
|
|
55
|
+
if (exitCodeOnResolution) {
|
|
56
|
+
// Code 0 to finish the process as success
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error('â Error generating routes:\n', err);
|
|
62
|
+
if (exitCodeOnResolution) {
|
|
63
|
+
// Code 1 to finish the process as error
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
createRoutes();
|
|
69
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generatorWatcher.d.ts","sourceRoot":"","sources":["../src/generatorWatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,eAAO,MAAM,yBAAyB,GAAU,qBAG7C,cAAc,iCA0BhB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { generateRoutes } from './generator.js';
|
|
2
|
+
export const generateRoutesWithWatcher = async ({ input, options = { debounce: 500 }, }) => {
|
|
3
|
+
const { watch } = await import('chokidar');
|
|
4
|
+
let timeoutId;
|
|
5
|
+
const runFromWatcher = () => {
|
|
6
|
+
if (timeoutId) {
|
|
7
|
+
clearTimeout(timeoutId);
|
|
8
|
+
}
|
|
9
|
+
timeoutId = setTimeout(() => {
|
|
10
|
+
generateRoutes(input);
|
|
11
|
+
}, options.debounce);
|
|
12
|
+
};
|
|
13
|
+
const watcher = watch(input.baseFolder, {
|
|
14
|
+
ignoreInitial: true,
|
|
15
|
+
ignored: `${input.baseFolder}/${input.outputFile}`,
|
|
16
|
+
persistent: true,
|
|
17
|
+
});
|
|
18
|
+
console.log(`ð Watching folder: ${input.baseFolder} for changes...`);
|
|
19
|
+
watcher.on('add', runFromWatcher);
|
|
20
|
+
return () => watcher.close();
|
|
21
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
1
|
+
export { generateRoutes } from './generator.js';
|
|
2
|
+
export { generateRoutesWithWatcher } from './generatorWatcher.js';
|
|
3
|
+
export { generateRoutesPlugin } from './plugin.js';
|
|
4
|
+
export { TGenerateRoutesConfig } from './types.js';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.start = void 0;
|
|
18
|
-
var file_router_1 = require("./file-router");
|
|
19
|
-
Object.defineProperty(exports, "start", { enumerable: true, get: function () { return file_router_1.start; } });
|
|
20
|
-
__exportStar(require("./types"), exports);
|
|
1
|
+
export { generateRoutes } from './generator.js';
|
|
2
|
+
export { generateRoutesWithWatcher } from './generatorWatcher.js';
|
|
3
|
+
export { generateRoutesPlugin } from './plugin.js';
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGnD,eAAO,MAAM,oBAAoB,GAAI,mDAIlC,qBAAqB,KAAG,MAW1B,CAAC"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { generateRoutesWithWatcher } from './generatorWatcher.js';
|
|
2
|
+
export const generateRoutesPlugin = ({ baseFolder, outputFile, exitCodeOnResolution = false, }) => {
|
|
3
|
+
return {
|
|
4
|
+
name: 'file-router-plugin',
|
|
5
|
+
apply: 'serve',
|
|
6
|
+
buildStart() {
|
|
7
|
+
generateRoutesWithWatcher({
|
|
8
|
+
input: { baseFolder, outputFile, exitCodeOnResolution },
|
|
9
|
+
options: { debounce: 500 },
|
|
10
|
+
});
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAuB9C,eAAO,MAAM,SAAS,GAAI,QAAQ,WAAW,EAAE,YAAY,MAAM,SAMhE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
const stringifyTS = (obj, indent = 0) => {
|
|
4
|
+
const spaces = ' '.repeat(indent);
|
|
5
|
+
const innerSpaces = ' '.repeat(indent + 2);
|
|
6
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
7
|
+
return JSON.stringify(obj);
|
|
8
|
+
}
|
|
9
|
+
const entries = Object.entries(obj).map(([key, value]) => {
|
|
10
|
+
const validKey = /^[a-z_$][a-z0-9_$]*$/i.test(key)
|
|
11
|
+
? key
|
|
12
|
+
: JSON.stringify(key);
|
|
13
|
+
return `${innerSpaces}${validKey}: ${stringifyTS(value, indent + 2)}`;
|
|
14
|
+
});
|
|
15
|
+
return `{\n${entries.join(',\n')}\n${spaces}}`;
|
|
16
|
+
};
|
|
17
|
+
export const serialize = (routes, outputPath) => {
|
|
18
|
+
const content = `export const routes = ${stringifyTS(routes)} as const;\n`;
|
|
19
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
20
|
+
fs.writeFileSync(outputPath, content, 'utf8');
|
|
21
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
type TWatcherOptions = {
|
|
2
|
+
debounce?: number;
|
|
3
|
+
};
|
|
4
|
+
export type TWatcherConfig = {
|
|
5
|
+
input: TGenerateRoutesConfig;
|
|
6
|
+
options?: TWatcherOptions;
|
|
7
|
+
};
|
|
8
|
+
export type TGenerateRoutesConfig = {
|
|
2
9
|
baseFolder: string;
|
|
3
10
|
outputFile: string;
|
|
4
11
|
routeFileName?: string;
|
|
12
|
+
exitCodeOnResolution?: boolean;
|
|
5
13
|
};
|
|
6
14
|
export type TRoute = {
|
|
7
15
|
path: string;
|
|
@@ -10,4 +18,5 @@ export type TRoute = {
|
|
|
10
18
|
export type TRoutesTree = {
|
|
11
19
|
[key: string]: TRoute | TRoutesTree;
|
|
12
20
|
};
|
|
21
|
+
export {};
|
|
13
22
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,KAAK,eAAe,GAAG;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,qBAAqB,CAAC;IAC7B,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC;CACrC,CAAC"}
|
package/dist/types.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ts-file-router",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "router based on project files using typescript",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "ts-file-router",
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"description": "router based on project files using typescript",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "tsx ./src/generator.ts",
|
|
13
|
+
"prebuild": "rimraf dist",
|
|
14
|
+
"build": "tsc"
|
|
15
|
+
},
|
|
16
|
+
"author": "MatheusF10",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^24.10.1",
|
|
20
|
+
"chokidar": "^5.0.0",
|
|
21
|
+
"rimraf": "^6.1.2",
|
|
22
|
+
"tsx": "^4.21.0",
|
|
23
|
+
"typescript": "^5.9.3",
|
|
24
|
+
"vite": "^7.3.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"chokidar": ">=4.0.0",
|
|
28
|
+
"vite": ">=3.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/file-router.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file-router.d.ts","sourceRoot":"","sources":["../src/file-router.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAe,MAAM,SAAS,CAAC;AA+BrD,eAAO,MAAM,KAAK,GAAI,4CAInB,aAAa,SAsEf,CAAC"}
|
package/dist/file-router.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.start = void 0;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
-
// Serialize routes objects to TS file output
|
|
10
|
-
const serializeRoutes = (obj, ident = 2) => {
|
|
11
|
-
const pad = ' '.repeat(ident);
|
|
12
|
-
let str = '{\n';
|
|
13
|
-
for (const key in obj) {
|
|
14
|
-
const value = obj[key];
|
|
15
|
-
// Manipulate JS keys
|
|
16
|
-
const keyStr = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`;
|
|
17
|
-
if (typeof value === 'object' &&
|
|
18
|
-
value !== null &&
|
|
19
|
-
!('path' in value && 'import' in value)) {
|
|
20
|
-
// Create sub folders
|
|
21
|
-
str += `${pad}${keyStr}: ${serializeRoutes(value, ident + 2)},\n`;
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
// Routes
|
|
25
|
-
str += `${pad}${keyStr}: { path: "${value.path}", import: "${value.import}" },\n`;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
str += ' '.repeat(ident - 2) + '}';
|
|
29
|
-
return str;
|
|
30
|
-
};
|
|
31
|
-
const start = ({ baseFolder, outputFile, routeFileName = 'page.tsx', }) => {
|
|
32
|
-
// Get the pages dir to resolve routes
|
|
33
|
-
const basePath = path_1.default.resolve(process.cwd(), baseFolder);
|
|
34
|
-
// Output file for routes
|
|
35
|
-
const output = path_1.default.resolve(process.cwd(), outputFile);
|
|
36
|
-
const generateRouter = async () => {
|
|
37
|
-
const mapRoutes = async (dir) => {
|
|
38
|
-
const routes = {};
|
|
39
|
-
const directory = await promises_1.default.readdir(dir);
|
|
40
|
-
if (!directory.includes(routeFileName)) {
|
|
41
|
-
throw new Error(`Invalid pages structure: The folder "${dir}" must contain a ${routeFileName} file.`);
|
|
42
|
-
}
|
|
43
|
-
for (const file of directory) {
|
|
44
|
-
const fullPath = path_1.default.join(dir, file);
|
|
45
|
-
// Get directory info to control file or folder
|
|
46
|
-
const dirInfo = await promises_1.default.stat(fullPath);
|
|
47
|
-
// Normalize import path to esm pattern
|
|
48
|
-
const importPath = './' + path_1.default.relative(basePath, fullPath).replaceAll(/\\/g, '/');
|
|
49
|
-
const relativePath = './' + path_1.default.relative(basePath, dir).replaceAll(/\\/g, '/');
|
|
50
|
-
// Remove extension from file to naming the route
|
|
51
|
-
const key = path_1.default.basename(file, path_1.default.extname(file));
|
|
52
|
-
if (dirInfo.isDirectory()) {
|
|
53
|
-
// Dev friendly when enter in this conditional file is a directory(folder)
|
|
54
|
-
const directory = file;
|
|
55
|
-
// Recursion for sub directories
|
|
56
|
-
routes[directory] = await mapRoutes(fullPath);
|
|
57
|
-
// Skip this directory iteration
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
routes[key] = {
|
|
61
|
-
path: relativePath,
|
|
62
|
-
import: importPath,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
return routes;
|
|
66
|
-
};
|
|
67
|
-
try {
|
|
68
|
-
// Create routes
|
|
69
|
-
const routes = await mapRoutes(basePath);
|
|
70
|
-
// Cria arquivo TS exportando JSON
|
|
71
|
-
const tsContent = `// AUTO-GENERATED ROUTES\n\nconst routes = ${serializeRoutes(routes)};\n\nexport default routes;\n`;
|
|
72
|
-
// Write .ts file
|
|
73
|
-
await promises_1.default.writeFile(output, tsContent, 'utf-8');
|
|
74
|
-
console.log('ð Routes generated successfully!\n', output);
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
77
|
-
catch (err) {
|
|
78
|
-
console.error('â Error generating routes:', err);
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
generateRouter();
|
|
83
|
-
};
|
|
84
|
-
exports.start = start;
|