elysia-autoload 0.2.4 → 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 +45 -4
- package/dist/index.cjs +67 -29
- package/dist/index.js +57 -29
- package/package.json +1 -1
package/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# elysia-autoload
|
2
2
|
|
3
|
-
Plugin for [Elysia](https://elysiajs.com/) which autoload all routes in directory and code-generate types for [Eden](https://elysiajs.com/eden/overview.html)
|
3
|
+
Plugin for [Elysia](https://elysiajs.com/) which autoload all routes in directory and code-generate types for [Eden](https://elysiajs.com/eden/overview.html) with [`Bun.build`](https://bun.sh/docs/bundler) support!
|
4
4
|
|
5
5
|
**Currently, Eden types generation is broken!!**
|
6
6
|
|
@@ -28,11 +28,16 @@ bun install elysia-autoload
|
|
28
28
|
import { Elysia } from "elysia";
|
29
29
|
import { autoload } from "elysia-autoload";
|
30
30
|
|
31
|
-
const app = new Elysia().use(autoload()).listen(3000);
|
31
|
+
const app = new Elysia().use(await autoload()).listen(3000);
|
32
32
|
|
33
33
|
export type ElysiaApp = typeof app;
|
34
34
|
```
|
35
35
|
|
36
|
+
> [!IMPORTANT]
|
37
|
+
> We strictly recommend use `await` when registering plugin
|
38
|
+
>
|
39
|
+
> Read more about [Lazy-load plugins](https://elysiajs.com/patterns/lazy-loading-module.html)
|
40
|
+
|
36
41
|
## Create route
|
37
42
|
|
38
43
|
```ts
|
@@ -99,7 +104,7 @@ import { autoload } from "elysia-autoload";
|
|
99
104
|
|
100
105
|
const app = new Elysia()
|
101
106
|
.use(
|
102
|
-
autoload({
|
107
|
+
await autoload({
|
103
108
|
types: {
|
104
109
|
output: "./routes.ts",
|
105
110
|
typeName: "Routes",
|
@@ -129,8 +134,44 @@ const { data } = await app.test["some-path-param"].get({
|
|
129
134
|
console.log(data);
|
130
135
|
```
|
131
136
|
|
137
|
+
`routes.ts` will be:
|
138
|
+
|
139
|
+
```ts
|
140
|
+
// @filename: routes.ts
|
141
|
+
|
142
|
+
import type { ElysiaWithBaseUrl } from "elysia-autoload";
|
143
|
+
import type Route0 from "./routes/index";
|
144
|
+
import type Route1 from "./routes/test/[some]/index";
|
145
|
+
|
146
|
+
declare global {
|
147
|
+
export type Routes = ElysiaWithBaseUrl<"/api", ReturnType<typeof Route0>> &
|
148
|
+
ElysiaWithBaseUrl<"/api/test/:some", ReturnType<typeof Route1>>;
|
149
|
+
}
|
150
|
+
```
|
151
|
+
|
132
152
|
Example of app with types code-generation you can see in [example](https://github.com/kravetsone/elysia-autoload/tree/main/example)
|
133
153
|
|
154
|
+
**Currently, Eden types generation is broken!!**
|
155
|
+
|
156
|
+
### Bun build usage
|
157
|
+
|
158
|
+
You can use this plugin with `Bun.build`, thanks to [esbuild-plugin-autoload](https://github.com/kravetsone/esbuild-plugin-autoload)!
|
159
|
+
|
160
|
+
```ts
|
161
|
+
// @filename: build.ts
|
162
|
+
import { autoload } from "esbuild-plugin-autoload"; // default import also supported
|
163
|
+
|
164
|
+
await Bun.build({
|
165
|
+
entrypoints: ["src/index.ts"],
|
166
|
+
outdir: "out",
|
167
|
+
plugins: [autoload()],
|
168
|
+
}).then(console.log);
|
169
|
+
```
|
170
|
+
|
171
|
+
Then, build it with `bun build.ts` and run with `bun out/index.ts`.
|
172
|
+
|
173
|
+
[Read more](https://github.com/kravetsone/esbuild-plugin-autoload)
|
174
|
+
|
134
175
|
### Usage of schema handler
|
135
176
|
|
136
177
|
```ts
|
@@ -140,7 +181,7 @@ import { autoload } from "elysia-autoload";
|
|
140
181
|
|
141
182
|
const app = new Elysia()
|
142
183
|
.use(
|
143
|
-
autoload({
|
184
|
+
await autoload({
|
144
185
|
schema: ({ path, url }) => {
|
145
186
|
const tag = url.split("/").at(1)!;
|
146
187
|
|
package/dist/index.cjs
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __create = Object.create;
|
2
3
|
var __defProp = Object.defineProperty;
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
6
8
|
var __export = (target, all) => {
|
7
9
|
for (var name in all)
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
15
17
|
}
|
16
18
|
return to;
|
17
19
|
};
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
26
|
+
mod
|
27
|
+
));
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
19
29
|
|
20
30
|
// src/index.ts
|
@@ -23,18 +33,20 @@ __export(src_exports, {
|
|
23
33
|
autoload: () => autoload
|
24
34
|
});
|
25
35
|
module.exports = __toCommonJS(src_exports);
|
26
|
-
var import_node_fs = require("fs");
|
27
|
-
var import_node_path2 = require("path");
|
36
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
37
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
28
38
|
var import_elysia = require("elysia");
|
29
39
|
|
30
40
|
// src/utils.ts
|
31
|
-
var import_node_path = require("path");
|
41
|
+
var import_node_path = __toESM(require("path"), 1);
|
32
42
|
function getPath(dir) {
|
33
|
-
if (
|
34
|
-
|
35
|
-
|
43
|
+
if (import_node_path.default.isAbsolute(dir))
|
44
|
+
return dir;
|
45
|
+
if (import_node_path.default.isAbsolute(process.argv[1]))
|
46
|
+
return import_node_path.default.join(process.argv[1], "..", dir);
|
47
|
+
return import_node_path.default.join(process.cwd(), process.argv[1], "..", dir);
|
36
48
|
}
|
37
|
-
function transformToUrl(
|
49
|
+
function transformToUrl(path3) {
|
38
50
|
const replacements = [
|
39
51
|
// Clean the url extensions
|
40
52
|
{ regex: /\.(ts|tsx|js|jsx|mjs|cjs)$/u, replacement: "" },
|
@@ -56,32 +68,51 @@ function transformToUrl(path) {
|
|
56
68
|
// remove index from end of path
|
57
69
|
{ regex: /\/?index$/, replacement: "" }
|
58
70
|
];
|
59
|
-
let url =
|
71
|
+
let url = path3;
|
60
72
|
for (const { regex, replacement } of replacements) {
|
61
73
|
url = url.replace(regex, replacement);
|
62
74
|
}
|
63
75
|
return url;
|
64
76
|
}
|
65
|
-
function getParamsCount(
|
66
|
-
return
|
77
|
+
function getParamsCount(path3) {
|
78
|
+
return path3.match(/\[(.*?)\]/gu)?.length || 0;
|
67
79
|
}
|
68
80
|
function sortByNestedParams(routes) {
|
69
81
|
return routes.sort((a, b) => getParamsCount(a) - getParamsCount(b));
|
70
82
|
}
|
71
83
|
function fixSlashes(prefix) {
|
72
|
-
if (!prefix?.endsWith("/"))
|
84
|
+
if (!prefix?.endsWith("/"))
|
85
|
+
return prefix;
|
73
86
|
return prefix.slice(0, -1);
|
74
87
|
}
|
88
|
+
function addRelativeIfNotDot(path3) {
|
89
|
+
if (path3.at(0) !== ".")
|
90
|
+
return `./${path3}`;
|
91
|
+
return path3;
|
92
|
+
}
|
75
93
|
|
76
94
|
// src/index.ts
|
95
|
+
var DIR_ROUTES_DEFAULT = "./routes";
|
77
96
|
var TYPES_OUTPUT_DEFAULT = "./routes-types.ts";
|
78
97
|
var TYPES_TYPENAME_DEFAULT = "Routes";
|
98
|
+
var TYPES_OBJECT_DEFAULT = {
|
99
|
+
output: [TYPES_OUTPUT_DEFAULT],
|
100
|
+
typeName: TYPES_TYPENAME_DEFAULT
|
101
|
+
};
|
79
102
|
async function autoload(options = {}) {
|
80
|
-
const
|
81
|
-
const
|
82
|
-
|
103
|
+
const fileSources = {};
|
104
|
+
const { pattern, prefix, schema } = options;
|
105
|
+
const dir = options.dir ?? DIR_ROUTES_DEFAULT;
|
106
|
+
const types = options.types && options.types !== true ? {
|
107
|
+
...TYPES_OBJECT_DEFAULT,
|
108
|
+
...options.types,
|
109
|
+
// This code allows you to omit the output data or specify it as an string[] or string.
|
110
|
+
output: !options.types.output ? [TYPES_OUTPUT_DEFAULT] : Array.isArray(options.types.output) ? options.types.output : [options.types.output]
|
111
|
+
} : TYPES_OBJECT_DEFAULT;
|
112
|
+
const directoryPath = getPath(dir);
|
113
|
+
if (!import_node_fs.default.existsSync(directoryPath))
|
83
114
|
throw new Error(`Directory ${directoryPath} doesn't exists`);
|
84
|
-
if (!
|
115
|
+
if (!import_node_fs.default.statSync(directoryPath).isDirectory())
|
85
116
|
throw new Error(`${directoryPath} isn't a directory`);
|
86
117
|
const plugin = new import_elysia.Elysia({
|
87
118
|
name: "elysia-autoload",
|
@@ -100,32 +131,39 @@ async function autoload(options = {}) {
|
|
100
131
|
})
|
101
132
|
);
|
102
133
|
const paths = [];
|
103
|
-
for await (const
|
104
|
-
const fullPath =
|
134
|
+
for await (const filePath of sortByNestedParams(files)) {
|
135
|
+
const fullPath = import_node_path2.default.join(directoryPath, filePath);
|
105
136
|
const file = await import(fullPath);
|
106
137
|
if (!file.default)
|
107
|
-
throw new Error(`${
|
108
|
-
const url = transformToUrl(
|
109
|
-
const groupOptions = schema ? schema({ path, url }) : {};
|
138
|
+
throw new Error(`${filePath} doesn't provide default export`);
|
139
|
+
const url = transformToUrl(filePath);
|
140
|
+
const groupOptions = schema ? schema({ path: filePath, url }) : {};
|
110
141
|
plugin.group(url, groupOptions, file.default);
|
111
|
-
if (types)
|
142
|
+
if (types)
|
143
|
+
paths.push(fullPath.replace(directoryPath, ""));
|
112
144
|
}
|
113
145
|
if (types) {
|
114
|
-
const
|
115
|
-
|
116
|
-
|
117
|
-
|
146
|
+
for await (const outputPath of types.output) {
|
147
|
+
const outputAbsolutePath = getPath(outputPath);
|
148
|
+
const imports = paths.map(
|
149
|
+
(x, index) => `import type Route${index} from "${addRelativeIfNotDot(
|
150
|
+
import_node_path2.default.relative(
|
151
|
+
import_node_path2.default.dirname(outputAbsolutePath),
|
152
|
+
directoryPath + x.replace(".ts", "").replace(".tsx", "")
|
153
|
+
).replace(/\\/gu, "/")
|
154
|
+
)}";`
|
155
|
+
);
|
118
156
|
await Bun.write(
|
119
|
-
|
157
|
+
outputAbsolutePath,
|
120
158
|
[
|
121
159
|
`import type { ElysiaWithBaseUrl } from "elysia-autoload";`,
|
122
160
|
imports.join("\n"),
|
123
161
|
"",
|
124
|
-
|
125
|
-
` export type ${types
|
162
|
+
!types.useExport ? "declare global {" : "",
|
163
|
+
` export type ${types.typeName} = ${paths.map(
|
126
164
|
(x, index) => `ElysiaWithBaseUrl<"${((prefix?.endsWith("/") ? prefix.slice(0, -1) : prefix) ?? "") + transformToUrl(x) || "/"}", ReturnType<typeof Route${index}>>`
|
127
165
|
).join("\n & ")}`,
|
128
|
-
|
166
|
+
!types.useExport ? "}" : ""
|
129
167
|
].join("\n")
|
130
168
|
);
|
131
169
|
}
|
package/dist/index.js
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
// src/index.ts
|
2
|
-
import
|
3
|
-
import
|
2
|
+
import fs from "node:fs";
|
3
|
+
import path2 from "node:path";
|
4
4
|
import {
|
5
5
|
Elysia
|
6
6
|
} from "elysia";
|
7
7
|
|
8
8
|
// src/utils.ts
|
9
|
-
import
|
9
|
+
import path from "node:path";
|
10
10
|
function getPath(dir) {
|
11
|
-
if (isAbsolute(dir))
|
12
|
-
|
13
|
-
|
11
|
+
if (path.isAbsolute(dir))
|
12
|
+
return dir;
|
13
|
+
if (path.isAbsolute(process.argv[1]))
|
14
|
+
return path.join(process.argv[1], "..", dir);
|
15
|
+
return path.join(process.cwd(), process.argv[1], "..", dir);
|
14
16
|
}
|
15
|
-
function transformToUrl(
|
17
|
+
function transformToUrl(path3) {
|
16
18
|
const replacements = [
|
17
19
|
// Clean the url extensions
|
18
20
|
{ regex: /\.(ts|tsx|js|jsx|mjs|cjs)$/u, replacement: "" },
|
@@ -34,32 +36,51 @@ function transformToUrl(path) {
|
|
34
36
|
// remove index from end of path
|
35
37
|
{ regex: /\/?index$/, replacement: "" }
|
36
38
|
];
|
37
|
-
let url =
|
39
|
+
let url = path3;
|
38
40
|
for (const { regex, replacement } of replacements) {
|
39
41
|
url = url.replace(regex, replacement);
|
40
42
|
}
|
41
43
|
return url;
|
42
44
|
}
|
43
|
-
function getParamsCount(
|
44
|
-
return
|
45
|
+
function getParamsCount(path3) {
|
46
|
+
return path3.match(/\[(.*?)\]/gu)?.length || 0;
|
45
47
|
}
|
46
48
|
function sortByNestedParams(routes) {
|
47
49
|
return routes.sort((a, b) => getParamsCount(a) - getParamsCount(b));
|
48
50
|
}
|
49
51
|
function fixSlashes(prefix) {
|
50
|
-
if (!prefix?.endsWith("/"))
|
52
|
+
if (!prefix?.endsWith("/"))
|
53
|
+
return prefix;
|
51
54
|
return prefix.slice(0, -1);
|
52
55
|
}
|
56
|
+
function addRelativeIfNotDot(path3) {
|
57
|
+
if (path3.at(0) !== ".")
|
58
|
+
return `./${path3}`;
|
59
|
+
return path3;
|
60
|
+
}
|
53
61
|
|
54
62
|
// src/index.ts
|
63
|
+
var DIR_ROUTES_DEFAULT = "./routes";
|
55
64
|
var TYPES_OUTPUT_DEFAULT = "./routes-types.ts";
|
56
65
|
var TYPES_TYPENAME_DEFAULT = "Routes";
|
66
|
+
var TYPES_OBJECT_DEFAULT = {
|
67
|
+
output: [TYPES_OUTPUT_DEFAULT],
|
68
|
+
typeName: TYPES_TYPENAME_DEFAULT
|
69
|
+
};
|
57
70
|
async function autoload(options = {}) {
|
58
|
-
const
|
59
|
-
const
|
60
|
-
|
71
|
+
const fileSources = {};
|
72
|
+
const { pattern, prefix, schema } = options;
|
73
|
+
const dir = options.dir ?? DIR_ROUTES_DEFAULT;
|
74
|
+
const types = options.types && options.types !== true ? {
|
75
|
+
...TYPES_OBJECT_DEFAULT,
|
76
|
+
...options.types,
|
77
|
+
// This code allows you to omit the output data or specify it as an string[] or string.
|
78
|
+
output: !options.types.output ? [TYPES_OUTPUT_DEFAULT] : Array.isArray(options.types.output) ? options.types.output : [options.types.output]
|
79
|
+
} : TYPES_OBJECT_DEFAULT;
|
80
|
+
const directoryPath = getPath(dir);
|
81
|
+
if (!fs.existsSync(directoryPath))
|
61
82
|
throw new Error(`Directory ${directoryPath} doesn't exists`);
|
62
|
-
if (!statSync(directoryPath).isDirectory())
|
83
|
+
if (!fs.statSync(directoryPath).isDirectory())
|
63
84
|
throw new Error(`${directoryPath} isn't a directory`);
|
64
85
|
const plugin = new Elysia({
|
65
86
|
name: "elysia-autoload",
|
@@ -78,32 +99,39 @@ async function autoload(options = {}) {
|
|
78
99
|
})
|
79
100
|
);
|
80
101
|
const paths = [];
|
81
|
-
for await (const
|
82
|
-
const fullPath =
|
102
|
+
for await (const filePath of sortByNestedParams(files)) {
|
103
|
+
const fullPath = path2.join(directoryPath, filePath);
|
83
104
|
const file = await import(fullPath);
|
84
105
|
if (!file.default)
|
85
|
-
throw new Error(`${
|
86
|
-
const url = transformToUrl(
|
87
|
-
const groupOptions = schema ? schema({ path, url }) : {};
|
106
|
+
throw new Error(`${filePath} doesn't provide default export`);
|
107
|
+
const url = transformToUrl(filePath);
|
108
|
+
const groupOptions = schema ? schema({ path: filePath, url }) : {};
|
88
109
|
plugin.group(url, groupOptions, file.default);
|
89
|
-
if (types)
|
110
|
+
if (types)
|
111
|
+
paths.push(fullPath.replace(directoryPath, ""));
|
90
112
|
}
|
91
113
|
if (types) {
|
92
|
-
const
|
93
|
-
|
94
|
-
|
95
|
-
|
114
|
+
for await (const outputPath of types.output) {
|
115
|
+
const outputAbsolutePath = getPath(outputPath);
|
116
|
+
const imports = paths.map(
|
117
|
+
(x, index) => `import type Route${index} from "${addRelativeIfNotDot(
|
118
|
+
path2.relative(
|
119
|
+
path2.dirname(outputAbsolutePath),
|
120
|
+
directoryPath + x.replace(".ts", "").replace(".tsx", "")
|
121
|
+
).replace(/\\/gu, "/")
|
122
|
+
)}";`
|
123
|
+
);
|
96
124
|
await Bun.write(
|
97
|
-
|
125
|
+
outputAbsolutePath,
|
98
126
|
[
|
99
127
|
`import type { ElysiaWithBaseUrl } from "elysia-autoload";`,
|
100
128
|
imports.join("\n"),
|
101
129
|
"",
|
102
|
-
|
103
|
-
` export type ${types
|
130
|
+
!types.useExport ? "declare global {" : "",
|
131
|
+
` export type ${types.typeName} = ${paths.map(
|
104
132
|
(x, index) => `ElysiaWithBaseUrl<"${((prefix?.endsWith("/") ? prefix.slice(0, -1) : prefix) ?? "") + transformToUrl(x) || "/"}", ReturnType<typeof Route${index}>>`
|
105
133
|
).join("\n & ")}`,
|
106
|
-
|
134
|
+
!types.useExport ? "}" : ""
|
107
135
|
].join("\n")
|
108
136
|
);
|
109
137
|
}
|