ts-file-router 6.0.2 â 7.0.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 +20 -21
- package/dist/index.cjs +6 -0
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -3
- package/dist/index.js +6 -2
- package/package.json +23 -15
- package/dist/generator.d.ts +0 -3
- package/dist/generator.d.ts.map +0 -1
- package/dist/generator.js +0 -129
- package/dist/helpers/fileHelper.d.ts +0 -6
- package/dist/helpers/fileHelper.d.ts.map +0 -1
- package/dist/helpers/fileHelper.js +0 -10
- package/dist/helpers/index.d.ts +0 -3
- package/dist/helpers/index.d.ts.map +0 -1
- package/dist/helpers/index.js +0 -2
- package/dist/helpers/serializeHelper.d.ts +0 -7
- package/dist/helpers/serializeHelper.d.ts.map +0 -1
- package/dist/helpers/serializeHelper.js +0 -84
- package/dist/index.d.ts.map +0 -1
- package/dist/plugins/index.d.ts +0 -2
- package/dist/plugins/index.d.ts.map +0 -1
- package/dist/plugins/index.js +0 -1
- package/dist/plugins/vite.d.ts +0 -4
- package/dist/plugins/vite.d.ts.map +0 -1
- package/dist/plugins/vite.js +0 -17
- package/dist/types.d.ts +0 -23
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
package/README.md
CHANGED
|
@@ -6,10 +6,10 @@ A lightweight **filesystem router generator** for TypeScript projects. Automatic
|
|
|
6
6
|
|
|
7
7
|
## âĻ Features
|
|
8
8
|
|
|
9
|
-
- ð **Automatic folder scanning** -
|
|
9
|
+
- ð **Automatic folder scanning** - Automatically scans your configured directory to build a complete route tree manifest.
|
|
10
10
|
- ð **Generated TypeScript routes** - Fully typed `routes.ts` file with `as const` assertions
|
|
11
11
|
- âïļ **Framework agnostic** - Works with React.lazy, Vue, Solid, or any framework with dynamic imports
|
|
12
|
-
- ðŠķ **Zero runtime dependencies** -
|
|
12
|
+
- ðŠķ **Zero runtime dependencies** - The generated routes file is plain TypeScript/JavaScript; your final bundle won't include any extra library code.
|
|
13
13
|
- ð **File watcher support** - Auto-regenerate routes when files change (powered by Chokidar)
|
|
14
14
|
- ðĻ **Biome formatting** - Output files are automatically formatted with Biome
|
|
15
15
|
- ð§Đ **Vite plugin** - Seamless integration with Vite dev server
|
|
@@ -43,10 +43,10 @@ Create a script to generate your routes:
|
|
|
43
43
|
|
|
44
44
|
```js
|
|
45
45
|
// scripts/generate-routes.mjs
|
|
46
|
-
import {
|
|
46
|
+
import { generateFileRouter } from 'ts-file-router';
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
baseFolder: 'src/screens',
|
|
48
|
+
generateFileRouter({
|
|
49
|
+
baseFolder: './src/screens',
|
|
50
50
|
outputFile: 'routes.ts',
|
|
51
51
|
});
|
|
52
52
|
```
|
|
@@ -61,11 +61,11 @@ node scripts/generate-routes.mjs
|
|
|
61
61
|
|
|
62
62
|
```js
|
|
63
63
|
// scripts/generate-routes.mjs
|
|
64
|
-
import {
|
|
64
|
+
import { generateFileRouter } from 'ts-file-router';
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
generateFileRouter({
|
|
67
|
+
dir: './src/screens',
|
|
68
|
+
outputFilename: 'routes.ts',
|
|
69
69
|
options: {
|
|
70
70
|
watcher: {
|
|
71
71
|
watch: true,
|
|
@@ -83,13 +83,13 @@ This will keep running and regenerate routes whenever you add, remove, or modify
|
|
|
83
83
|
```ts
|
|
84
84
|
// vite.config.ts
|
|
85
85
|
import { defineConfig } from 'vite';
|
|
86
|
-
import {
|
|
86
|
+
import { generateViteFileRouter } from 'ts-file-router';
|
|
87
87
|
|
|
88
88
|
export default defineConfig({
|
|
89
89
|
plugins: [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
generateViteFileRouter({
|
|
91
|
+
dir: './src/screens',
|
|
92
|
+
outputFilename: 'routes.ts',
|
|
93
93
|
// Optional: customize watcher behavior
|
|
94
94
|
options: {
|
|
95
95
|
watcher: { watch: true, debounce: 500 },
|
|
@@ -147,13 +147,13 @@ export const routes = {
|
|
|
147
147
|
|
|
148
148
|
## ð§ Configuration Options
|
|
149
149
|
|
|
150
|
-
### `
|
|
150
|
+
### `generateFileRouter()` Parameters
|
|
151
151
|
|
|
152
|
-
| Parameter
|
|
153
|
-
|
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `options`
|
|
152
|
+
| Parameter | Type | Required | Description |
|
|
153
|
+
| ---------------- | ------------------------ | -------- | ---------------------------------- |
|
|
154
|
+
| `dir` | `string` | â
Yes | Root directory to scan for routes |
|
|
155
|
+
| `outputFilename` | `string` | â
Yes | Path for the generated routes file |
|
|
156
|
+
| `options` | `TGenerateRoutesOptions` | â No | Additional configuration |
|
|
157
157
|
|
|
158
158
|
### Options Object
|
|
159
159
|
|
|
@@ -239,7 +239,6 @@ When using the watcher, routes regenerate on:
|
|
|
239
239
|
|
|
240
240
|
- `add` - New file added
|
|
241
241
|
- `addDir` - New directory added
|
|
242
|
-
- `change` - File modified
|
|
243
242
|
- `unlink` - File deleted
|
|
244
243
|
- `unlinkDir` - Directory deleted
|
|
245
244
|
|
|
@@ -270,7 +269,7 @@ Both trigger proper watcher cleanup before exit.
|
|
|
270
269
|
}
|
|
271
270
|
```
|
|
272
271
|
|
|
273
|
-
2. **Use relative paths in output**: The `
|
|
272
|
+
2. **Use relative paths in output**: The `outputFilename` path is relative to `dir`.
|
|
274
273
|
|
|
275
274
|
---
|
|
276
275
|
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";var K=Object.create;var g=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var _=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var v=(e,t)=>{for(var o in t)g(e,o,{get:t[o],enumerable:!0})},S=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of P(t))!k.call(e,s)&&s!==o&&g(e,s,{get:()=>t[s],enumerable:!(n=N(t,s))||n.enumerable});return e};var p=(e,t,o)=>(o=e!=null?K(_(e)):{},S(t||!e||!e.__esModule?g(o,"default",{value:e,enumerable:!0}):o,e)),A=e=>S(g({},"__esModule",{value:!0}),e);var U={};v(U,{generateFileRouter:()=>F,generateViteFileRouter:()=>D});module.exports=A(U);var y=require("@biomejs/js-api"),r=p(require("typescript"),1),T=p(require("fs"),1),m=p(require("path"),1),a={biome:null,project:null},$=async()=>(a.biome||(a.biome=await y.Biome.create({distribution:y.Distribution.NODE})),a.project||(a.project=a.biome.openProject(),a.biome.applyConfiguration(a.project.projectKey,{formatter:{enabled:!0,indentStyle:"space",lineWidth:100},javascript:{formatter:{quoteStyle:"single"}}})),{biome:a.biome,projectKey:a.project.projectKey}),W=e=>typeof e=="object"&&e!==null&&"path"in e&&"import"in e,O=e=>{let t=Object.entries(e).map(([o,n])=>W(n)?r.default.factory.createPropertyAssignment(r.default.factory.createIdentifier(o),r.default.factory.createObjectLiteralExpression([r.default.factory.createPropertyAssignment("path",r.default.factory.createStringLiteral(n.path)),r.default.factory.createPropertyAssignment("import",r.default.factory.createArrowFunction(void 0,void 0,[],void 0,r.default.factory.createToken(r.default.SyntaxKind.EqualsGreaterThanToken),r.default.factory.createCallExpression(r.default.factory.createIdentifier("import"),void 0,[r.default.factory.createStringLiteral(n.import)])))],!0)):r.default.factory.createPropertyAssignment(r.default.factory.createIdentifier(o),O(n)));return r.default.factory.createObjectLiteralExpression(t,!0)},z=e=>{let t=O(e),o=r.default.factory.createVariableStatement([r.default.factory.createModifier(r.default.SyntaxKind.ExportKeyword)],r.default.factory.createVariableDeclarationList([r.default.factory.createVariableDeclaration("routes",void 0,void 0,r.default.factory.createAsExpression(t,r.default.factory.createTypeReferenceNode("const")))],r.default.NodeFlags.Const));return r.default.addSyntheticLeadingComment(o,r.default.SyntaxKind.SingleLineCommentTrivia," GENERATED WITH TS-FILE-ROUTER DO NOT EDIT",!0),r.default.factory.createSourceFile([o],r.default.factory.createToken(r.default.SyntaxKind.EndOfFileToken),r.default.NodeFlags.None)},H=async(e,t)=>{let o=await $(),n=o.biome.formatContent(o.projectKey,t,{filePath:m.basename(e)});T.mkdirSync(m.dirname(e),{recursive:!0}),T.writeFileSync(e,n.content,"utf8")},V=r.default.createPrinter({newLine:r.default.NewLineKind.LineFeed}),B=async(e,t)=>{let o=z(e);try{await H(m.resolve(t),V.printFile(o)),console.log(`\u2728 File was parsed succesfully
|
|
2
|
+
`)}catch(n){console.error(`\u274C Error parsing file:
|
|
3
|
+
`,n)}},j={serializeOutputFile:B};var E=(e,t)=>e.includes(t),q=(e,t)=>e.includes("index")||e.startsWith("_")||E(e,t),R={getIgnoredFiles:q,getIgnoredOutputFile:E};var w=p(require("fs/promises"),1),d=p(require("path"),1),i=null,M=e=>{i=e},I=async(e,t)=>{let o={},n=d.default.resolve(process.cwd(),e);try{let s=await w.default.readdir(n);s.length||(console.error(`Invalid pages structure: The folder "${n}" must contain at least one valid file.`),i?.options?.exitCodeOnResolution&&process.exit(1));for(let c of s){let l=d.default.join(n,c);if(R.getIgnoredFiles(l,t))continue;if((await w.default.stat(l)).isDirectory()){o[c]=await I(l,t);continue}let u=d.default.basename(c,d.default.extname(c));o[u]={path:`/${u}`,import:`./${u}`}}}catch(s){console.error(`Error mapping routes ${n}:`,s),process.exit(1)}return o},C=async(e,t)=>{let o=!!i?.options?.exitCodeOnResolution;try{j.serializeOutputFile(await I(e,t),d.default.resolve(e,t)),console.log(`\u{1F680} Routes generated successfully!
|
|
4
|
+
`),o&&process.exit(0)}catch(n){console.error(`\u274C Error generating routes:
|
|
5
|
+
`,n),o&&process.exit(1)}},J=async(e,t,o,n)=>{if(!o)return;let{watch:s}=await import("chokidar"),c,l=s(e,{ignoreInitial:!0,persistent:!0});console.log(`\u{1F440} Watching folder: "${e}" for changes...`);let b=()=>{c&&clearTimeout(c),c=setTimeout(async()=>{await C(e,t)},n)};l.on("all",(x,h)=>{if(R.getIgnoredOutputFile(h,t)&&x==="unlink"){console.log(`\u{1F6A8} ${t} was deleted, regenerating...`),b();return}let L=["add","addDir","unlink","unlinkDir"],G=R.getIgnoredFiles(h,t);L.includes(x)&&!G&&(console.log(`\u{1F50E} Detected new files from path: "${h}" creating new routes...
|
|
6
|
+
`),b())});let u=async()=>{console.log("\u{1F6D1} Stopping watcher..."),await l.close(),process.exit(0)};process.on("SIGINT",u),process.on("SIGTERM",u)},F=e=>{if(M(e),!i||!i.dir)throw Error("To generate file router you must define a valid config");i.outputFilename||(i.outputFilename="routes.ts"),C(i.dir,i.outputFilename),J(i.dir,i.outputFilename,!!i.options?.watcher?.watch,i.options?.watcher?.debounce??500)};var D=({dir:e,outputFilename:t,options:o={exitCodeOnResolution:!1,watcher:{watch:!0,debounce:500}}})=>({apply:"serve",name:"generate-vite-file-router",buildStart(){F({dir:e,options:o,outputFilename:t})}});0&&(module.exports={generateFileRouter,generateViteFileRouter});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
type TWatcherConfig = {
|
|
4
|
+
watch: boolean;
|
|
5
|
+
debounce?: number;
|
|
6
|
+
};
|
|
7
|
+
type TGenerateRoutesOptions = {
|
|
8
|
+
watcher?: TWatcherConfig;
|
|
9
|
+
exitCodeOnResolution?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type TGenerateRoutesConfig = {
|
|
12
|
+
dir: string;
|
|
13
|
+
outputFilename?: string;
|
|
14
|
+
options?: TGenerateRoutesOptions;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
declare const generateFileRouter: (config: TGenerateRoutesConfig) => void;
|
|
18
|
+
|
|
19
|
+
declare const generateViteFileRouter: ({ dir, outputFilename, options, }: TGenerateRoutesConfig) => Plugin;
|
|
20
|
+
|
|
21
|
+
export { generateFileRouter, generateViteFileRouter };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
type TWatcherConfig = {
|
|
4
|
+
watch: boolean;
|
|
5
|
+
debounce?: number;
|
|
6
|
+
};
|
|
7
|
+
type TGenerateRoutesOptions = {
|
|
8
|
+
watcher?: TWatcherConfig;
|
|
9
|
+
exitCodeOnResolution?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type TGenerateRoutesConfig = {
|
|
12
|
+
dir: string;
|
|
13
|
+
outputFilename?: string;
|
|
14
|
+
options?: TGenerateRoutesOptions;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
declare const generateFileRouter: (config: TGenerateRoutesConfig) => void;
|
|
18
|
+
|
|
19
|
+
declare const generateViteFileRouter: ({ dir, outputFilename, options, }: TGenerateRoutesConfig) => Plugin;
|
|
20
|
+
|
|
21
|
+
export { generateFileRouter, generateViteFileRouter };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import{Biome as C,Distribution as D}from"@biomejs/js-api";import t from"typescript";import*as g from"fs";import*as m from"path";var s={biome:null,project:null},L=async()=>(s.biome||(s.biome=await C.create({distribution:D.NODE})),s.project||(s.project=s.biome.openProject(),s.biome.applyConfiguration(s.project.projectKey,{formatter:{enabled:!0,indentStyle:"space",lineWidth:100},javascript:{formatter:{quoteStyle:"single"}}})),{biome:s.biome,projectKey:s.project.projectKey}),G=e=>typeof e=="object"&&e!==null&&"path"in e&&"import"in e,h=e=>{let r=Object.entries(e).map(([o,n])=>G(n)?t.factory.createPropertyAssignment(t.factory.createIdentifier(o),t.factory.createObjectLiteralExpression([t.factory.createPropertyAssignment("path",t.factory.createStringLiteral(n.path)),t.factory.createPropertyAssignment("import",t.factory.createArrowFunction(void 0,void 0,[],void 0,t.factory.createToken(t.SyntaxKind.EqualsGreaterThanToken),t.factory.createCallExpression(t.factory.createIdentifier("import"),void 0,[t.factory.createStringLiteral(n.import)])))],!0)):t.factory.createPropertyAssignment(t.factory.createIdentifier(o),h(n)));return t.factory.createObjectLiteralExpression(r,!0)},K=e=>{let r=h(e),o=t.factory.createVariableStatement([t.factory.createModifier(t.SyntaxKind.ExportKeyword)],t.factory.createVariableDeclarationList([t.factory.createVariableDeclaration("routes",void 0,void 0,t.factory.createAsExpression(r,t.factory.createTypeReferenceNode("const")))],t.NodeFlags.Const));return t.addSyntheticLeadingComment(o,t.SyntaxKind.SingleLineCommentTrivia," GENERATED WITH TS-FILE-ROUTER DO NOT EDIT",!0),t.factory.createSourceFile([o],t.factory.createToken(t.SyntaxKind.EndOfFileToken),t.NodeFlags.None)},N=async(e,r)=>{let o=await L(),n=o.biome.formatContent(o.projectKey,r,{filePath:m.basename(e)});g.mkdirSync(m.dirname(e),{recursive:!0}),g.writeFileSync(e,n.content,"utf8")},P=t.createPrinter({newLine:t.NewLineKind.LineFeed}),_=async(e,r)=>{let o=K(e);try{await N(m.resolve(r),P.printFile(o)),console.log(`\u2728 File was parsed succesfully
|
|
2
|
+
`)}catch(n){console.error(`\u274C Error parsing file:
|
|
3
|
+
`,n)}},w={serializeOutputFile:_};var x=(e,r)=>e.includes(r),k=(e,r)=>e.includes("index")||e.startsWith("_")||x(e,r),y={getIgnoredFiles:k,getIgnoredOutputFile:x};import S from"fs/promises";import d from"path";var i=null,v=e=>{i=e},O=async(e,r)=>{let o={},n=d.resolve(process.cwd(),e);try{let c=await S.readdir(n);c.length||(console.error(`Invalid pages structure: The folder "${n}" must contain at least one valid file.`),i?.options?.exitCodeOnResolution&&process.exit(1));for(let a of c){let l=d.join(n,a);if(y.getIgnoredFiles(l,r))continue;if((await S.stat(l)).isDirectory()){o[a]=await O(l,r);continue}let u=d.basename(a,d.extname(a));o[u]={path:`/${u}`,import:`./${u}`}}}catch(c){console.error(`Error mapping routes ${n}:`,c),process.exit(1)}return o},j=async(e,r)=>{let o=!!i?.options?.exitCodeOnResolution;try{w.serializeOutputFile(await O(e,r),d.resolve(e,r)),console.log(`\u{1F680} Routes generated successfully!
|
|
4
|
+
`),o&&process.exit(0)}catch(n){console.error(`\u274C Error generating routes:
|
|
5
|
+
`,n),o&&process.exit(1)}},A=async(e,r,o,n)=>{if(!o)return;let{watch:c}=await import("chokidar"),a,l=c(e,{ignoreInitial:!0,persistent:!0});console.log(`\u{1F440} Watching folder: "${e}" for changes...`);let T=()=>{a&&clearTimeout(a),a=setTimeout(async()=>{await j(e,r)},n)};l.on("all",(b,R)=>{if(y.getIgnoredOutputFile(R,r)&&b==="unlink"){console.log(`\u{1F6A8} ${r} was deleted, regenerating...`),T();return}let E=["add","addDir","unlink","unlinkDir"],I=y.getIgnoredFiles(R,r);E.includes(b)&&!I&&(console.log(`\u{1F50E} Detected new files from path: "${R}" creating new routes...
|
|
6
|
+
`),T())});let u=async()=>{console.log("\u{1F6D1} Stopping watcher..."),await l.close(),process.exit(0)};process.on("SIGINT",u),process.on("SIGTERM",u)},F=e=>{if(v(e),!i||!i.dir)throw Error("To generate file router you must define a valid config");i.outputFilename||(i.outputFilename="routes.ts"),j(i.dir,i.outputFilename),A(i.dir,i.outputFilename,!!i.options?.watcher?.watch,i.options?.watcher?.debounce??500)};var $=({dir:e,outputFilename:r,options:o={exitCodeOnResolution:!1,watcher:{watch:!0,debounce:500}}})=>({apply:"serve",name:"generate-vite-file-router",buildStart(){F({dir:e,options:o,outputFilename:r})}});export{F as generateFileRouter,$ as generateViteFileRouter};
|
package/package.json
CHANGED
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-file-router",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "router based on project files using typescript",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
5
|
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"author": "MatheusF10",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
8
18
|
"files": [
|
|
9
19
|
"dist"
|
|
10
20
|
],
|
|
11
21
|
"scripts": {
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"lint": "biome check .",
|
|
25
|
+
"release": "npm run build && npm publish"
|
|
16
26
|
},
|
|
17
|
-
"author": "MatheusF10",
|
|
18
|
-
"license": "ISC",
|
|
19
27
|
"devDependencies": {
|
|
20
|
-
"@types/node": "
|
|
21
|
-
"chokidar": "
|
|
22
|
-
"rimraf": "
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
28
|
+
"@types/node": "24.10.1",
|
|
29
|
+
"chokidar": "5.0.0",
|
|
30
|
+
"rimraf": "6.1.2",
|
|
31
|
+
"typescript": "5.9.3",
|
|
32
|
+
"vite": "7.3.2",
|
|
33
|
+
"tsup": "8.5.1"
|
|
26
34
|
},
|
|
27
35
|
"peerDependencies": {
|
|
28
36
|
"chokidar": ">=4.0.0",
|
package/dist/generator.d.ts
DELETED
package/dist/generator.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAKrE,eAAO,MAAM,cAAc,GAAI,sCAI5B,qBAAqB,SAmKvB,CAAC"}
|
package/dist/generator.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { FileHelper, SerializeHelper } from './helpers/index.js';
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
export const generateRoutes = ({ baseFolder, outputFile, options = { 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
|
-
try {
|
|
12
|
-
const directory = await fs.readdir(dir);
|
|
13
|
-
if (!directory || !directory.length || directory.length === 0) {
|
|
14
|
-
throw new Error(`Invalid pages structure: The folder "${dir}" must contain at least one valid file.`);
|
|
15
|
-
}
|
|
16
|
-
for (const file of directory) {
|
|
17
|
-
// ignore index files, underscore marked, or route file generated
|
|
18
|
-
if (FileHelper.getIgnoredFiles(file, outputFile)) {
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
const fullPath = path.join(dir, file);
|
|
22
|
-
// Get directory info to control file or folder
|
|
23
|
-
const dirInfo = await fs.stat(fullPath);
|
|
24
|
-
// Path to browser sync if necessary
|
|
25
|
-
const relativePath = '/' + path.relative(basePath, dir);
|
|
26
|
-
const importPath = './' + path.relative(basePath, fullPath);
|
|
27
|
-
// Remove extension from file to naming the route
|
|
28
|
-
const key = path.basename(file, path.extname(file));
|
|
29
|
-
if (dirInfo.isDirectory()) {
|
|
30
|
-
// Dev friendly when enter in this conditional file is a directory(folder)
|
|
31
|
-
const directory = file;
|
|
32
|
-
// Recursively for sub directories
|
|
33
|
-
routes[directory] = await mapRoutes(fullPath);
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
// Mount the route object with path like "/folder" and import
|
|
37
|
-
// import will be like "import((./baseFolder/file or ./baseFolder/folders).extension)"
|
|
38
|
-
routes[key] = {
|
|
39
|
-
// Normalize path
|
|
40
|
-
path: FileHelper.cleanPaths(relativePath),
|
|
41
|
-
// Normalize import path to esm pattern
|
|
42
|
-
import: FileHelper.cleanPaths(importPath),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch (err) {
|
|
47
|
-
console.error(`Error mapping routes ${dir}:`, err);
|
|
48
|
-
// Stop the process immediatelly
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
return routes;
|
|
52
|
-
};
|
|
53
|
-
const createRoutes = async () => {
|
|
54
|
-
try {
|
|
55
|
-
// Create routes
|
|
56
|
-
const routes = await mapRoutes(basePath);
|
|
57
|
-
// Create ts file
|
|
58
|
-
await SerializeHelper.serializeOutputFile(routes, output);
|
|
59
|
-
// Promise writeFile was successfully resolved
|
|
60
|
-
console.log('ð Routes generated successfully!\n');
|
|
61
|
-
if (options.exitCodeOnResolution) {
|
|
62
|
-
// Code 0 to finish the process as success
|
|
63
|
-
process.exit(0);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
console.error('â Error generating routes:\n', err);
|
|
68
|
-
if (options.exitCodeOnResolution) {
|
|
69
|
-
// Code 1 to finish the process as error
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const watcher = async ({ debounce = 500 }) => {
|
|
75
|
-
const { watch } = await import('chokidar');
|
|
76
|
-
let timeoutId;
|
|
77
|
-
const watcher = watch(baseFolder, {
|
|
78
|
-
ignoreInitial: false,
|
|
79
|
-
persistent: true,
|
|
80
|
-
});
|
|
81
|
-
console.log(`ð Watching folder: "${baseFolder}" for changes...`);
|
|
82
|
-
const runFromWatcher = () => {
|
|
83
|
-
if (timeoutId) {
|
|
84
|
-
clearTimeout(timeoutId);
|
|
85
|
-
}
|
|
86
|
-
timeoutId = setTimeout(() => {
|
|
87
|
-
createRoutes();
|
|
88
|
-
}, debounce);
|
|
89
|
-
};
|
|
90
|
-
runFromWatcher();
|
|
91
|
-
watcher.on('all', (ev, file) => {
|
|
92
|
-
const ignoredOuput = FileHelper.getIgnoredOutputFile(file, outputFile);
|
|
93
|
-
// If output file as deleted regenerate it
|
|
94
|
-
if (ignoredOuput && ev === 'unlink') {
|
|
95
|
-
console.log(`ðĻ ${outputFile} was deleted, regenerating...`);
|
|
96
|
-
runFromWatcher();
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
const watchOnEvents = [
|
|
100
|
-
'add',
|
|
101
|
-
'addDir',
|
|
102
|
-
'change',
|
|
103
|
-
'unlink',
|
|
104
|
-
'unlinkDir',
|
|
105
|
-
];
|
|
106
|
-
const ignoredGeneralFiles = FileHelper.getIgnoredFiles(file, outputFile);
|
|
107
|
-
// If added, or change (renamed) or deleted, update routes
|
|
108
|
-
if (watchOnEvents.includes(ev) && !ignoredGeneralFiles) {
|
|
109
|
-
console.log(`ð Watching files from path: "${file}" for changes...`);
|
|
110
|
-
runFromWatcher();
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
const cleanup = async () => {
|
|
114
|
-
console.log('ð Stopping watcher...');
|
|
115
|
-
await watcher.close();
|
|
116
|
-
// Code 0 to finish the process as success
|
|
117
|
-
process.exit(0);
|
|
118
|
-
};
|
|
119
|
-
// If CRTL + C was pressed
|
|
120
|
-
process.on('SIGINT', cleanup);
|
|
121
|
-
// If Script or container treatment
|
|
122
|
-
process.on('SIGTERM', cleanup);
|
|
123
|
-
};
|
|
124
|
-
if (!!options.watcher) {
|
|
125
|
-
watcher(options.watcher);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
createRoutes();
|
|
129
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const FileHelper: {
|
|
2
|
-
readonly cleanPaths: (path: string) => string;
|
|
3
|
-
readonly getIgnoredFiles: (file: string, output: string) => boolean;
|
|
4
|
-
readonly getIgnoredOutputFile: (file: string, output: string) => boolean;
|
|
5
|
-
};
|
|
6
|
-
//# sourceMappingURL=fileHelper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fileHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/fileHelper.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,UAAU;gCAHG,MAAM;qCALD,MAAM,UAAU,MAAM;0CAHjB,MAAM,UAAU,MAAM;CAehD,CAAC"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
const getIgnoredOutputFile = (file, output) => file.includes(output);
|
|
2
|
-
const getIgnoredFiles = (file, output) => file.includes('index') ||
|
|
3
|
-
file.startsWith('_') ||
|
|
4
|
-
getIgnoredOutputFile(file, output);
|
|
5
|
-
const cleanPaths = (path) => path.replaceAll(/\\/gi, '/').replaceAll(/.(tsx|ts|jsx|js)/gi, '');
|
|
6
|
-
export const FileHelper = {
|
|
7
|
-
cleanPaths,
|
|
8
|
-
getIgnoredFiles,
|
|
9
|
-
getIgnoredOutputFile,
|
|
10
|
-
};
|
package/dist/helpers/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC"}
|
package/dist/helpers/index.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { TRouteLeaf, TRoutesTree } from '../types.js';
|
|
2
|
-
export declare const SerializeHelper: {
|
|
3
|
-
readonly isRouteLeaf: (value: unknown) => value is TRouteLeaf;
|
|
4
|
-
readonly serializeOutputFile: (routes: TRoutesTree, outputPath: string) => Promise<void>;
|
|
5
|
-
readonly formatAndWriteOutputFile: (filePath: string, code: string) => Promise<void>;
|
|
6
|
-
};
|
|
7
|
-
//# sourceMappingURL=serializeHelper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serializeHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/serializeHelper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA0JtD,eAAO,MAAM,eAAe;kCAxHA,OAAO,KAAG,KAAK,IAAI,UAAU;2CAyGd,WAAW,cAAc,MAAM;kDAhBxB,MAAM,QAAQ,MAAM;CAmC5D,CAAC"}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Biome, Distribution } from '@biomejs/js-api';
|
|
2
|
-
import ts from 'typescript';
|
|
3
|
-
import * as fs from 'node:fs';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
const biomeInstance = {
|
|
6
|
-
biome: null,
|
|
7
|
-
project: null,
|
|
8
|
-
};
|
|
9
|
-
const getBiomeSingleton = async () => {
|
|
10
|
-
if (!biomeInstance.biome) {
|
|
11
|
-
biomeInstance.biome = await Biome.create({
|
|
12
|
-
distribution: Distribution.NODE,
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
if (!biomeInstance.project) {
|
|
16
|
-
biomeInstance.project = biomeInstance.biome.openProject();
|
|
17
|
-
biomeInstance.biome.applyConfiguration(biomeInstance.project.projectKey, {
|
|
18
|
-
formatter: { enabled: true, indentStyle: 'space', lineWidth: 100 },
|
|
19
|
-
javascript: { formatter: { quoteStyle: 'single' } },
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
biome: biomeInstance.biome,
|
|
24
|
-
projectKey: biomeInstance.project.projectKey,
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
// Type guard
|
|
28
|
-
const isRouteLeaf = (value) => {
|
|
29
|
-
return (typeof value === 'object' &&
|
|
30
|
-
value !== null &&
|
|
31
|
-
'path' in value &&
|
|
32
|
-
'import' in value);
|
|
33
|
-
};
|
|
34
|
-
const createRouteObject = (obj) => {
|
|
35
|
-
const properties = Object.entries(obj).map(([key, value]) => {
|
|
36
|
-
if (isRouteLeaf(value)) {
|
|
37
|
-
return ts.factory.createPropertyAssignment(ts.factory.createIdentifier(key), ts.factory.createObjectLiteralExpression([
|
|
38
|
-
ts.factory.createPropertyAssignment('path', ts.factory.createStringLiteral(value.path)),
|
|
39
|
-
ts.factory.createPropertyAssignment('import', ts.factory.createArrowFunction(undefined, // modifiers
|
|
40
|
-
undefined, // type parameters
|
|
41
|
-
[], // parameters
|
|
42
|
-
undefined, // return type
|
|
43
|
-
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), // =>
|
|
44
|
-
ts.factory.createCallExpression(ts.factory.createIdentifier('import'), undefined, [ts.factory.createStringLiteral(value.import)]))),
|
|
45
|
-
], true));
|
|
46
|
-
}
|
|
47
|
-
// Recursive object
|
|
48
|
-
return ts.factory.createPropertyAssignment(ts.factory.createIdentifier(key), createRouteObject(value));
|
|
49
|
-
});
|
|
50
|
-
return ts.factory.createObjectLiteralExpression(properties, true);
|
|
51
|
-
};
|
|
52
|
-
const createRoutesFile = (obj) => {
|
|
53
|
-
const routesObject = createRouteObject(obj);
|
|
54
|
-
// Create AST from routes variable
|
|
55
|
-
const exportStatement = ts.factory.createVariableStatement([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
|
|
56
|
-
ts.factory.createVariableDeclaration('routes', undefined, undefined, ts.factory.createAsExpression(routesObject, ts.factory.createTypeReferenceNode('const'))),
|
|
57
|
-
], ts.NodeFlags.Const));
|
|
58
|
-
// Add the comment
|
|
59
|
-
ts.addSyntheticLeadingComment(exportStatement, ts.SyntaxKind.SingleLineCommentTrivia, ' GENERATED WITH TS-FILE-ROUTER DO NOT EDIT', true);
|
|
60
|
-
// Create the source file
|
|
61
|
-
return ts.factory.createSourceFile([exportStatement], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
62
|
-
};
|
|
63
|
-
const formatAndWriteOutputFile = async (filePath, code) => {
|
|
64
|
-
const biomeSingleton = await getBiomeSingleton();
|
|
65
|
-
const formatted = biomeSingleton.biome.formatContent(biomeSingleton.projectKey, code, { filePath: path.basename(filePath) });
|
|
66
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
67
|
-
fs.writeFileSync(filePath, formatted.content, 'utf8');
|
|
68
|
-
};
|
|
69
|
-
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
70
|
-
const serializeOutputFile = async (routes, outputPath) => {
|
|
71
|
-
const code = createRoutesFile(routes);
|
|
72
|
-
try {
|
|
73
|
-
await formatAndWriteOutputFile(path.resolve(outputPath), printer.printFile(code));
|
|
74
|
-
console.log('âĻ File was parsed succesfully');
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
console.error('â Error parsing file:\n', err);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
export const SerializeHelper = {
|
|
81
|
-
isRouteLeaf,
|
|
82
|
-
serializeOutputFile,
|
|
83
|
-
formatAndWriteOutputFile,
|
|
84
|
-
};
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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,oBAAoB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/plugins/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC"}
|
package/dist/plugins/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './vite.js';
|
package/dist/plugins/vite.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../../src/plugins/vite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGpD,eAAO,MAAM,oBAAoB,GAAI,sCAOlC,qBAAqB,KAAG,MAY1B,CAAC"}
|
package/dist/plugins/vite.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { generateRoutes } from '../generator.js';
|
|
2
|
-
export const generateRoutesPlugin = ({ baseFolder, outputFile, options = {
|
|
3
|
-
watcher: { watch: true, debounce: 500 },
|
|
4
|
-
exitCodeOnResolution: false,
|
|
5
|
-
}, }) => {
|
|
6
|
-
return {
|
|
7
|
-
name: 'file-router-plugin',
|
|
8
|
-
apply: 'serve',
|
|
9
|
-
buildStart() {
|
|
10
|
-
generateRoutes({
|
|
11
|
-
baseFolder,
|
|
12
|
-
outputFile,
|
|
13
|
-
options,
|
|
14
|
-
});
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
};
|
package/dist/types.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
type TWatcherConfig = {
|
|
2
|
-
watch: boolean;
|
|
3
|
-
debounce?: number;
|
|
4
|
-
};
|
|
5
|
-
type TGenerateRoutesOptions = {
|
|
6
|
-
watcher?: TWatcherConfig;
|
|
7
|
-
exitCodeOnResolution?: boolean;
|
|
8
|
-
};
|
|
9
|
-
export type TGenerateRoutesConfig = {
|
|
10
|
-
baseFolder: string;
|
|
11
|
-
outputFile: string;
|
|
12
|
-
routeFileName?: string;
|
|
13
|
-
options?: TGenerateRoutesOptions;
|
|
14
|
-
};
|
|
15
|
-
export type TRouteLeaf = {
|
|
16
|
-
path: string;
|
|
17
|
-
import: string;
|
|
18
|
-
};
|
|
19
|
-
export type TRoutesTree = {
|
|
20
|
-
[key: string]: TRouteLeaf | TRoutesTree;
|
|
21
|
-
};
|
|
22
|
-
export {};
|
|
23
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,sBAAsB,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,CAAC;CACzC,CAAC"}
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|