@asterflow/fs 1.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 +121 -0
- package/cjs/index.cjs +67 -0
- package/cjs/package.json +3 -0
- package/mjs/index.js +46 -0
- package/mjs/package.json +3 -0
- package/package.json +40 -0
- package/tsconfig.json +40 -0
- package/types/index.d.ts +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @asterflow/fs
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
> Roteamento baseado em convenΓ§Γ΅es de sistema de arquivos para o AsterFlow.
|
|
12
|
+
|
|
13
|
+
## π¦ Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# You can use any package manager
|
|
17
|
+
npm install @asterflow/fs
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## π‘ About
|
|
21
|
+
|
|
22
|
+
`@asterflow/fs` brings the convenience of file system-based routing to your AsterFlow projects. Inspired by modern web frameworks, this plugin automatically discovers and registers your routes based on the file and directory structure, allowing you to focus on writing your API logic instead of manual route configuration.
|
|
23
|
+
|
|
24
|
+
## β¨ Features
|
|
25
|
+
|
|
26
|
+
- **Convention over Configuration:** Automatically generates API routes from your file structure.
|
|
27
|
+
- **Dynamic Parameters:** Support for dynamic segments in filenames (e.g., `$id.ts`).
|
|
28
|
+
- **Index Routes:** `index.ts` files are treated as the base route of a directory.
|
|
29
|
+
- **Type-Safe:** Fully integrated with AsterFlow's type system.
|
|
30
|
+
- **Seamless Integration:** Automatically registers all discovered routes before the server starts.
|
|
31
|
+
|
|
32
|
+
## π Usage
|
|
33
|
+
|
|
34
|
+
### 1\. Project Structure
|
|
35
|
+
|
|
36
|
+
Create a directory to store your route files. By convention, this directory is usually `routes/` or `src/routes/`.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
.
|
|
40
|
+
βββ routes/
|
|
41
|
+
β βββ index.ts # Handles GET /
|
|
42
|
+
β βββ users/
|
|
43
|
+
β β βββ index.ts # Handles GET /users
|
|
44
|
+
β β βββ $id.ts # Handles GET /users/:id
|
|
45
|
+
βββ src
|
|
46
|
+
β βββ index.ts # Your main application file
|
|
47
|
+
βββ package.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2\. Define Your Routes
|
|
51
|
+
|
|
52
|
+
Each route file must have a `default export` of an AsterFlow `Method` or `Router`.
|
|
53
|
+
|
|
54
|
+
**`src/routes/users/$id.ts`**
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { Method } from '@asterflow/router';
|
|
58
|
+
|
|
59
|
+
export default new Method({
|
|
60
|
+
// The 'path' property will be overwritten by the plugin,
|
|
61
|
+
// but can be useful for isolated testing.
|
|
62
|
+
path: '/',
|
|
63
|
+
method: 'get',
|
|
64
|
+
handler({ response, url }) {
|
|
65
|
+
// 'id' will be available at runtime
|
|
66
|
+
const params = url.getParams();
|
|
67
|
+
return response.success({ user: { id: params.id } });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 3\. Register the Plugin
|
|
73
|
+
|
|
74
|
+
In your main application file, import and register the `fsRouting` plugin.
|
|
75
|
+
|
|
76
|
+
**`src/index.ts`**
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { AsterFlow } from '@asterflow/core';
|
|
80
|
+
import { fsRouting } from '@asterflow/fs';
|
|
81
|
+
import { join } from 'path';
|
|
82
|
+
|
|
83
|
+
// Register the plugin and point it to your routes directory
|
|
84
|
+
export const app = new AsterFlow();
|
|
85
|
+
.use(fsRouting, {
|
|
86
|
+
path: join(process.cwd(), 'src', 'routes')
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Start the server
|
|
90
|
+
app.listen({ port: 3333 }, () => {
|
|
91
|
+
console.log('Server running with file system routing!');
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
That's it\! The plugin will scan the `src/routes` directory and register all valid route files when the application starts.
|
|
96
|
+
|
|
97
|
+
## πΊοΈ Routing Conventions
|
|
98
|
+
|
|
99
|
+
The plugin transforms file paths into URL routes based on the following rules:
|
|
100
|
+
|
|
101
|
+
| File Path | Generated Route |
|
|
102
|
+
| ----------------- | ------------------ |
|
|
103
|
+
| `index.ts` | `/` |
|
|
104
|
+
| `users.ts` | `/users` |
|
|
105
|
+
| `users/index.ts` | `/users` |
|
|
106
|
+
| `$id.ts` | `/:id` |
|
|
107
|
+
| `products/$id.ts` | `/products/:id` |
|
|
108
|
+
| `categories/$categoryId/products/$productId.ts` | `/categories/:categoryId/products/:productId` |
|
|
109
|
+
|
|
110
|
+
- Files named `index` become the root of their directory.
|
|
111
|
+
- Filenames prefixed with `$` (e.g., `$id.ts`) are converted to dynamic URL parameters (e.g., `/:id`).
|
|
112
|
+
|
|
113
|
+
## π Related Packages
|
|
114
|
+
|
|
115
|
+
- [@asterflow/core](https://www.npmjs.com/package/@asterflow/core) - The core of the AsterFlow framework.
|
|
116
|
+
- [@asterflow/plugin](https://www.npmjs.com/package/@asterflow/plugin) - The main plugin system.
|
|
117
|
+
- [@asterflow/router](https://www.npmjs.com/package/@asterflow/router) - The type-safe routing system used by this plugin.
|
|
118
|
+
|
|
119
|
+
## π License
|
|
120
|
+
|
|
121
|
+
MIT - See the main project [LICENSE](https://github.com/AsterFlow/AsterFlow/blob/main/LICENSE) for more details.
|
package/cjs/index.cjs
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var a = Object.defineProperty;
|
|
3
|
+
var h = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var x = Object.getOwnPropertyNames;
|
|
5
|
+
var w = Object.prototype.hasOwnProperty;
|
|
6
|
+
var A = (t, e) => {
|
|
7
|
+
for (var i in e)
|
|
8
|
+
a(t, i, { get: e[i], enumerable: !0 });
|
|
9
|
+
}, v = (t, e, i, r) => {
|
|
10
|
+
if (e && typeof e == "object" || typeof e == "function")
|
|
11
|
+
for (let s of x(e))
|
|
12
|
+
!w.call(t, s) && s !== i && a(t, s, { get: () => e[s], enumerable: !(r = h(e, s)) || r.enumerable });
|
|
13
|
+
return t;
|
|
14
|
+
};
|
|
15
|
+
var j = (t) => v(a({}, "__esModule", { value: !0 }), t);
|
|
16
|
+
|
|
17
|
+
// plugins/fs/src/index.ts
|
|
18
|
+
var b = {};
|
|
19
|
+
A(b, {
|
|
20
|
+
default: () => P,
|
|
21
|
+
fsRoutingPlugin: () => y,
|
|
22
|
+
getFilesRecursively: () => o,
|
|
23
|
+
transformPathToUrl: () => p
|
|
24
|
+
});
|
|
25
|
+
module.exports = j(b);
|
|
26
|
+
var d = require("@asterflow/plugin"), n = require("@asterflow/router"), g = require("@asterflow/url-parser");
|
|
27
|
+
|
|
28
|
+
// plugins/fs/package.json
|
|
29
|
+
var u = "1.0.1";
|
|
30
|
+
|
|
31
|
+
// plugins/fs/src/utils/format.ts
|
|
32
|
+
var c = require("path");
|
|
33
|
+
function p(t, e) {
|
|
34
|
+
let r = (0, c.relative)(e, t).replace(/\.(ts|js)$/, "");
|
|
35
|
+
return r.endsWith("/index") ? r = r.slice(0, -6) : r === "index" && (r = ""), r = r.replace(/\$/g, ":"), `/${r}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// plugins/fs/src/utils/glob.ts
|
|
39
|
+
var f = require("fs/promises"), m = require("path");
|
|
40
|
+
async function o(t) {
|
|
41
|
+
let e = await (0, f.readdir)(t, { withFileTypes: !0 });
|
|
42
|
+
return (await Promise.all(
|
|
43
|
+
e.map(async (r) => {
|
|
44
|
+
let s = (0, m.join)(t, r.name);
|
|
45
|
+
return r.isDirectory() ? o(s) : s;
|
|
46
|
+
})
|
|
47
|
+
)).flat();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// plugins/fs/src/index.ts
|
|
51
|
+
var y = d.Plugin.instance().create({ name: "fs-routing" }).decorate("creator", "Ashu11-A").decorate("version", u).config({ path: "" }).derive("files", async (t) => await o(t.path)).extends((t) => ({
|
|
52
|
+
async registerRoutes(e) {
|
|
53
|
+
if (e.files.length !== 0)
|
|
54
|
+
for (let i of e.files) {
|
|
55
|
+
let s = (await import(i)).default;
|
|
56
|
+
if (!s || !(s instanceof n.Method || s instanceof n.Router)) continue;
|
|
57
|
+
let l = p(i, e.path);
|
|
58
|
+
s.path = l, s.url = new g.Analyze(l), t.controller(s);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})).on("beforeInitialize", async (t, e) => await t.registerRoutes(e)), P = y;
|
|
62
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
63
|
+
0 && (module.exports = {
|
|
64
|
+
fsRoutingPlugin,
|
|
65
|
+
getFilesRecursively,
|
|
66
|
+
transformPathToUrl
|
|
67
|
+
});
|
package/cjs/package.json
ADDED
package/mjs/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// plugins/fs/src/index.ts
|
|
2
|
+
import { Plugin as d } from "@asterflow/plugin";
|
|
3
|
+
import { Method as g, Router as y } from "@asterflow/router";
|
|
4
|
+
import { Analyze as h } from "@asterflow/url-parser";
|
|
5
|
+
|
|
6
|
+
// plugins/fs/package.json
|
|
7
|
+
var a = "1.0.1";
|
|
8
|
+
|
|
9
|
+
// plugins/fs/src/utils/format.ts
|
|
10
|
+
import { relative as c } from "path";
|
|
11
|
+
function p(t, r) {
|
|
12
|
+
let e = c(r, t).replace(/\.(ts|js)$/, "");
|
|
13
|
+
return e.endsWith("/index") ? e = e.slice(0, -6) : e === "index" && (e = ""), e = e.replace(/\$/g, ":"), `/${e}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// plugins/fs/src/utils/glob.ts
|
|
17
|
+
import { readdir as f } from "fs/promises";
|
|
18
|
+
import { join as m } from "path";
|
|
19
|
+
async function o(t) {
|
|
20
|
+
let r = await f(t, { withFileTypes: !0 });
|
|
21
|
+
return (await Promise.all(
|
|
22
|
+
r.map(async (e) => {
|
|
23
|
+
let s = m(t, e.name);
|
|
24
|
+
return e.isDirectory() ? o(s) : s;
|
|
25
|
+
})
|
|
26
|
+
)).flat();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// plugins/fs/src/index.ts
|
|
30
|
+
var x = d.instance().create({ name: "fs-routing" }).decorate("creator", "Ashu11-A").decorate("version", a).config({ path: "" }).derive("files", async (t) => await o(t.path)).extends((t) => ({
|
|
31
|
+
async registerRoutes(r) {
|
|
32
|
+
if (r.files.length !== 0)
|
|
33
|
+
for (let i of r.files) {
|
|
34
|
+
let s = (await import(i)).default;
|
|
35
|
+
if (!s || !(s instanceof g || s instanceof y)) continue;
|
|
36
|
+
let n = p(i, r.path);
|
|
37
|
+
s.path = n, s.url = new h(n), t.controller(s);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
})).on("beforeInitialize", async (t, r) => await t.registerRoutes(r)), D = x;
|
|
41
|
+
export {
|
|
42
|
+
D as default,
|
|
43
|
+
x as fsRoutingPlugin,
|
|
44
|
+
o as getFilesRecursively,
|
|
45
|
+
p as transformPathToUrl
|
|
46
|
+
};
|
package/mjs/package.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@asterflow/fs",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"main": "dist/cjs/index.cjs",
|
|
5
|
+
"module": "dist/mjs/index.js",
|
|
6
|
+
"types": "dist/types/index.d.ts",
|
|
7
|
+
"typings": "dist/types/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": "Ashu11-A",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/AsterFlow/plugins.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/AsterFlow/plugins/issues"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/AsterFlow/plugins",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/types/index.d.ts",
|
|
22
|
+
"import": "./dist/mjs/index.js",
|
|
23
|
+
"require": "./dist/cjs/index.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@asterflow/core": "^1.0.7",
|
|
31
|
+
"@types/bun": "latest"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"typescript": "^5.8.3"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@asterflow/router": "^1.0.7",
|
|
38
|
+
"@asterflow/url-parser": "^2.0.1"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": [
|
|
4
|
+
"ESNext"
|
|
5
|
+
],
|
|
6
|
+
"target": "ESNext",
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"moduleDetection": "force",
|
|
9
|
+
"jsx": "react-jsx",
|
|
10
|
+
"allowJs": true,
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"strict": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"noUncheckedIndexedAccess": true,
|
|
19
|
+
"noUnusedLocals": false,
|
|
20
|
+
"noUnusedParameters": false,
|
|
21
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
22
|
+
"paths": {
|
|
23
|
+
"@asterflow/fs": [
|
|
24
|
+
"./types"
|
|
25
|
+
],
|
|
26
|
+
"@asterflow/fs/*": [
|
|
27
|
+
"./types/*"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"declaration": true,
|
|
31
|
+
"declarationMap": true
|
|
32
|
+
},
|
|
33
|
+
"include": [
|
|
34
|
+
"types"
|
|
35
|
+
],
|
|
36
|
+
"exclude": [
|
|
37
|
+
"node_modules",
|
|
38
|
+
"**/*.spec.ts"
|
|
39
|
+
]
|
|
40
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
import { Plugin } from '@asterflow/plugin';
|
|
4
|
+
|
|
5
|
+
export declare const fsRoutingPlugin: Plugin<"fs-routing", import("@asterflow/core").IAsterflow<import("@asterflow/core").AnyReminist, {}, [
|
|
6
|
+
], import("@asterflow/adapter").Adapter<import("@asterflow/adapter").Runtime.Node>, {}>, {
|
|
7
|
+
path: string;
|
|
8
|
+
}, {
|
|
9
|
+
creator: string;
|
|
10
|
+
} & {
|
|
11
|
+
version: string;
|
|
12
|
+
} & {
|
|
13
|
+
files: string[];
|
|
14
|
+
}, Omit<{}, Event> & {
|
|
15
|
+
beforeInitialize: [
|
|
16
|
+
(instance: import("@asterflow/core").IAsterflow<import("@asterflow/core").AnyReminist, {}, [
|
|
17
|
+
], import("@asterflow/adapter").Adapter<import("@asterflow/adapter").Runtime.Node>, {}> & {
|
|
18
|
+
registerRoutes(context: FSRoutingContext): Promise<void>;
|
|
19
|
+
}, context: {
|
|
20
|
+
creator: string;
|
|
21
|
+
} & {
|
|
22
|
+
version: string;
|
|
23
|
+
} & {
|
|
24
|
+
files: string[];
|
|
25
|
+
} & {
|
|
26
|
+
path: string;
|
|
27
|
+
}) => Promise<void>
|
|
28
|
+
];
|
|
29
|
+
}, {
|
|
30
|
+
registerRoutes(context: FSRoutingContext): Promise<void>;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Recursively lists files from a starting directory.
|
|
34
|
+
* @param dirPath The path to the starting directory.
|
|
35
|
+
* @returns A promise that resolves to an array of file paths.
|
|
36
|
+
*/
|
|
37
|
+
export declare function getFilesRecursively(dirPath: string): Promise<string[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Transforms a file path into a robustly formatted URL route.
|
|
40
|
+
*/
|
|
41
|
+
export declare function transformPathToUrl(filePath: string, rootDir: string): string;
|
|
42
|
+
export type FSRoutingContext = {
|
|
43
|
+
path: string;
|
|
44
|
+
files: string[];
|
|
45
|
+
creator: string;
|
|
46
|
+
version: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
fsRoutingPlugin as default,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export {};
|