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 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** - Recursively scans your pages/screens directory
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** - Only used at build/dev time
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 { generateRoutes } from 'ts-file-router';
46
+ import { generateFileRouter } from 'ts-file-router';
47
47
 
48
- generateRoutes({
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 { generateRoutes } from 'ts-file-router';
64
+ import { generateFileRouter } from 'ts-file-router';
65
65
 
66
- generateRoutes({
67
- baseFolder: 'src/screens',
68
- outputFile: 'routes.ts',
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 { generateRoutesPlugin } from 'ts-file-router';
86
+ import { generateViteFileRouter } from 'ts-file-router';
87
87
 
88
88
  export default defineConfig({
89
89
  plugins: [
90
- generateRoutesPlugin({
91
- baseFolder: 'src/screens',
92
- outputFile: 'src/screens/routes.ts',
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
- ### `generateRoutes()` Parameters
150
+ ### `generateFileRouter()` Parameters
151
151
 
152
- | Parameter | Type | Required | Description |
153
- | ------------ | ------------------------ | -------- | ---------------------------------- |
154
- | `baseFolder` | `string` | ✅ Yes | Root directory to scan for routes |
155
- | `outputFile` | `string` | ✅ Yes | Path for the generated routes file |
156
- | `options` | `TGenerateRoutesOptions` | ❌ No | Additional configuration |
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 `outputFile` path is relative to `baseFolder`.
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});
@@ -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
- export { generateRoutes } from './generator.js';
2
- export { generateRoutesPlugin } from './plugins/index.js';
3
- //# sourceMappingURL=index.d.ts.map
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
- export { generateRoutes } from './generator.js';
2
- export { generateRoutesPlugin } from './plugins/index.js';
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": "6.0.2",
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
- "start": "tsx ./src/generator.ts",
13
- "prebuild": "rimraf dist",
14
- "build": "tsc",
15
- "publish:package": "npm run build && npm publish"
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": "^24.10.1",
21
- "chokidar": "^5.0.0",
22
- "rimraf": "^6.1.2",
23
- "tsx": "^4.21.0",
24
- "typescript": "^5.9.3",
25
- "vite": "^7.3.0"
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",
@@ -1,3 +0,0 @@
1
- import type { TGenerateRoutesConfig } from './types.js';
2
- export declare const generateRoutes: ({ baseFolder, outputFile, options, }: TGenerateRoutesConfig) => void;
3
- //# sourceMappingURL=generator.d.ts.map
@@ -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
- };
@@ -1,3 +0,0 @@
1
- export * from './serializeHelper.js';
2
- export * from './fileHelper.js';
3
- //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -1,2 +0,0 @@
1
- export * from './serializeHelper.js';
2
- export * from './fileHelper.js';
@@ -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
- };
@@ -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"}
@@ -1,2 +0,0 @@
1
- export * from './vite.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC"}
@@ -1 +0,0 @@
1
- export * from './vite.js';
@@ -1,4 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- import { TGenerateRoutesConfig } from '../types.js';
3
- export declare const generateRoutesPlugin: ({ baseFolder, outputFile, options, }: TGenerateRoutesConfig) => Plugin;
4
- //# sourceMappingURL=vite.d.ts.map
@@ -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"}
@@ -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
@@ -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 {};