@efebia/fastify-auto-import 1.1.0 → 1.2.0
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 +24 -0
- package/lib/cjs/globber.d.ts +10 -1
- package/lib/cjs/globber.js +36 -16
- package/lib/cjs/index.js +15 -2
- package/lib/cjs/router.d.ts +16 -0
- package/lib/cjs/router.js +34 -0
- package/lib/esm/globber.d.ts +10 -1
- package/lib/esm/globber.js +32 -15
- package/lib/esm/index.js +16 -3
- package/lib/esm/router.d.ts +16 -0
- package/lib/esm/router.js +25 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,6 +70,7 @@ A `fastify-plugin` compatible plugin. Register it with `fastify.register()`.
|
|
|
70
70
|
| `routeFile` | `string` | `'routes'` | File name (without extension) to import from each subdirectory. |
|
|
71
71
|
| `excludedDirectories` | `string[]` | `[]` | Subdirectory names to skip. |
|
|
72
72
|
| `recursive` | `boolean` | `false` | Scan nested subdirectories for route files, not just direct children. |
|
|
73
|
+
| `fileBasedRouting` | `boolean` | `false` | Derive each folder's URL prefix from its path. See below. |
|
|
73
74
|
| `log` | `pino.BaseLogger` | Fastify logger | Logger instance for import errors. |
|
|
74
75
|
|
|
75
76
|
## Recursive mode
|
|
@@ -104,6 +105,29 @@ Without `recursive`, only `users/routes.ts` and `orders/routes.ts` would be regi
|
|
|
104
105
|
|
|
105
106
|
`excludedDirectories` still applies at every depth — a matching directory name is skipped along with all its descendants.
|
|
106
107
|
|
|
108
|
+
## File-based routing
|
|
109
|
+
|
|
110
|
+
Set `fileBasedRouting: true` to make folders the URL. It walks the whole tree (no need for `recursive`).
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
src/routes/
|
|
114
|
+
api/
|
|
115
|
+
users/
|
|
116
|
+
routes.ts ← /api/users
|
|
117
|
+
get.ts ← plain handler, imported by routes.ts (not auto-loaded)
|
|
118
|
+
[userId]/
|
|
119
|
+
routes.ts ← /api/users/:userId
|
|
120
|
+
(private)/
|
|
121
|
+
tasks/routes.ts ← /api/tasks ((private) adds no segment)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- The full folder path is the URL. Each `routes.ts` is registered flat, in its own encapsulated scope, with that path as `prefix`, so it only declares paths relative to its folder (`app.get('/', ...)`, `app.get('/export', ...)`).
|
|
125
|
+
- Scopes are independent: hooks in a parent folder's `routes.ts` never apply to child folders (and vice versa).
|
|
126
|
+
- `[id]` becomes `:id`. Folders named `_x` or `(group)` add no segment.
|
|
127
|
+
- Only the `routes` file is loaded. Anything else in the folder (handlers, schemas) is yours to import from it.
|
|
128
|
+
- Middleware is not built in. Add hooks or `preHandler`s in each `routes.ts` that needs them.
|
|
129
|
+
- `excludedDirectories` and `routeFile` behave as usual.
|
|
130
|
+
|
|
107
131
|
## License
|
|
108
132
|
|
|
109
133
|
MIT
|
package/lib/cjs/globber.d.ts
CHANGED
|
@@ -6,7 +6,16 @@ export type Globber = {
|
|
|
6
6
|
log?: pino.BaseLogger;
|
|
7
7
|
excludedDirectories?: string[];
|
|
8
8
|
recursive?: boolean;
|
|
9
|
+
fileBasedRouting?: boolean;
|
|
9
10
|
};
|
|
10
|
-
export declare const
|
|
11
|
+
export declare const getPackageType: (directory: string) => Promise<any>;
|
|
12
|
+
export declare const resolvePluginsDirectory: (opts: Pick<Globber, "directory" | "startingDirectory"> & {
|
|
13
|
+
directory: string;
|
|
14
|
+
}) => Promise<{
|
|
15
|
+
packageType: any;
|
|
16
|
+
pluginsDirectory: string;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const importPlugin: (dir: string, fileName: string, packageType: string, entries: string[], log?: pino.BaseLogger) => Promise<any | undefined>;
|
|
19
|
+
export declare const globFiles: (opts: Required<Omit<Globber, "fileBasedRouting">> & {
|
|
11
20
|
log?: pino.BaseLogger;
|
|
12
21
|
}) => Promise<any[]>;
|
package/lib/cjs/globber.js
CHANGED
|
@@ -36,12 +36,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.globFiles = void 0;
|
|
39
|
+
exports.globFiles = exports.importPlugin = exports.resolvePluginsDirectory = exports.getPackageType = void 0;
|
|
40
40
|
const promises_1 = require("node:fs/promises");
|
|
41
41
|
const node_path_1 = __importDefault(require("node:path"));
|
|
42
42
|
const node_url_1 = __importStar(require("node:url"));
|
|
43
43
|
const pkg_up_1 = __importDefault(require("pkg-up"));
|
|
44
|
-
const serial_js_1 = require("./serial.js");
|
|
45
44
|
const getPackageType = async (directory) => {
|
|
46
45
|
const nearestPackage = await (0, pkg_up_1.default)({ cwd: directory });
|
|
47
46
|
if (nearestPackage) {
|
|
@@ -52,19 +51,40 @@ const getPackageType = async (directory) => {
|
|
|
52
51
|
return JSON.parse(file).type;
|
|
53
52
|
}
|
|
54
53
|
};
|
|
54
|
+
exports.getPackageType = getPackageType;
|
|
55
|
+
const resolvePluginsDirectory = async (opts) => {
|
|
56
|
+
const packageType = await (0, exports.getPackageType)(opts.directory);
|
|
57
|
+
const startingDirectory = packageType === 'module' ? node_path_1.default.dirname((0, node_url_1.fileURLToPath)(opts.startingDirectory)) : opts.startingDirectory;
|
|
58
|
+
return { packageType, pluginsDirectory: node_path_1.default.join(startingDirectory, opts.directory) };
|
|
59
|
+
};
|
|
60
|
+
exports.resolvePluginsDirectory = resolvePluginsDirectory;
|
|
61
|
+
const importPlugin = async (dir, fileName, packageType, entries, log) => {
|
|
62
|
+
const name = fileName.replace('.js', '');
|
|
63
|
+
if (!entries.some((f) => f === `${name}.js` || f === `${name}.ts`))
|
|
64
|
+
return undefined;
|
|
65
|
+
const osPath = node_path_1.default.join(dir, packageType === 'module' ? `${name}.js` : name);
|
|
66
|
+
try {
|
|
67
|
+
if (packageType === 'module') {
|
|
68
|
+
return (await Promise.resolve(`${node_url_1.default.pathToFileURL(osPath).href}`).then(s => __importStar(require(s)))).default;
|
|
69
|
+
}
|
|
70
|
+
return require(osPath);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
log === null || log === void 0 ? void 0 : log.error({ error: e, path: osPath }, '@efebia/fastify-auto-import error on importing plugin');
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
exports.importPlugin = importPlugin;
|
|
55
78
|
const collectRecursivePlugins = async (dir, opts) => {
|
|
56
|
-
var _a;
|
|
57
79
|
const entries = await (0, promises_1.readdir)(dir, { withFileTypes: true });
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
continue;
|
|
62
|
-
if (opts.excludedDirectories.includes(entry.name))
|
|
63
|
-
continue;
|
|
80
|
+
const dirEntries = entries.filter((entry) => entry.isDirectory() && !opts.excludedDirectories.includes(entry.name));
|
|
81
|
+
const perDirResults = await Promise.all(dirEntries.map(async (entry) => {
|
|
82
|
+
var _a;
|
|
64
83
|
const subDir = node_path_1.default.join(dir, entry.name);
|
|
65
84
|
const subEntries = await (0, promises_1.readdir)(subDir);
|
|
66
85
|
const routeFileName = opts.routeFile.replace('.js', '');
|
|
67
86
|
const hasRouteFile = subEntries.some((f) => f === `${routeFileName}.js` || f === `${routeFileName}.ts`);
|
|
87
|
+
const own = [];
|
|
68
88
|
if (hasRouteFile) {
|
|
69
89
|
const osPath = node_path_1.default.join(subDir, opts.packageType === 'module' ? `${routeFileName}.js` : routeFileName);
|
|
70
90
|
const href = node_url_1.default.pathToFileURL(osPath).href;
|
|
@@ -72,12 +92,12 @@ const collectRecursivePlugins = async (dir, opts) => {
|
|
|
72
92
|
if (opts.packageType === 'module') {
|
|
73
93
|
const imported = await Promise.resolve(`${href}`).then(s => __importStar(require(s)));
|
|
74
94
|
if (imported.default)
|
|
75
|
-
|
|
95
|
+
own.push(imported.default);
|
|
76
96
|
}
|
|
77
97
|
else {
|
|
78
98
|
const imported = require(osPath);
|
|
79
99
|
if (imported)
|
|
80
|
-
|
|
100
|
+
own.push(imported);
|
|
81
101
|
}
|
|
82
102
|
}
|
|
83
103
|
catch (e) {
|
|
@@ -86,12 +106,12 @@ const collectRecursivePlugins = async (dir, opts) => {
|
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
108
|
const nested = await collectRecursivePlugins(subDir, opts);
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
return
|
|
109
|
+
return [...own, ...nested];
|
|
110
|
+
}));
|
|
111
|
+
return perDirResults.flat();
|
|
92
112
|
};
|
|
93
113
|
const globFiles = async (opts) => {
|
|
94
|
-
const packageType = await getPackageType(opts.directory);
|
|
114
|
+
const packageType = await (0, exports.getPackageType)(opts.directory);
|
|
95
115
|
const startingDirectory = packageType === 'module' ? node_path_1.default.dirname((0, node_url_1.fileURLToPath)(opts.startingDirectory)) : opts.startingDirectory;
|
|
96
116
|
const pluginsDirectory = node_path_1.default.join(startingDirectory, opts.directory);
|
|
97
117
|
if (opts.recursive) {
|
|
@@ -105,7 +125,7 @@ const globFiles = async (opts) => {
|
|
|
105
125
|
}
|
|
106
126
|
const routesDirectories = await (0, promises_1.readdir)(pluginsDirectory);
|
|
107
127
|
const filteredDirectories = routesDirectories.filter(dir => !opts.excludedDirectories.includes(dir));
|
|
108
|
-
const importedFiles = await
|
|
128
|
+
const importedFiles = await Promise.all(filteredDirectories.map(async (dir) => {
|
|
109
129
|
var _a;
|
|
110
130
|
const osPath = node_path_1.default.join(pluginsDirectory, dir, packageType === 'module' ? `${opts.routeFile.replace(".js", "")}.js` : opts.routeFile.replace(".js", ""));
|
|
111
131
|
const href = node_url_1.default.pathToFileURL(osPath).href;
|
package/lib/cjs/index.js
CHANGED
|
@@ -5,9 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
7
7
|
const globber_js_1 = require("./globber.js");
|
|
8
|
+
const router_js_1 = require("./router.js");
|
|
8
9
|
const plugin = (0, fastify_plugin_1.default)(async (fastify, opts) => {
|
|
9
|
-
var _a, _b, _c, _d;
|
|
10
|
-
|
|
10
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
11
|
+
if (opts.fileBasedRouting) {
|
|
12
|
+
const directory = (_a = opts.directory) !== null && _a !== void 0 ? _a : 'src/plugins';
|
|
13
|
+
const { packageType, pluginsDirectory } = await (0, globber_js_1.resolvePluginsDirectory)(Object.assign(Object.assign({}, opts), { directory }));
|
|
14
|
+
const routes = await (0, router_js_1.scanRoutes)(pluginsDirectory, {
|
|
15
|
+
routeFile: (_b = opts.routeFile) !== null && _b !== void 0 ? _b : 'routes',
|
|
16
|
+
packageType,
|
|
17
|
+
excludedDirectories: (_c = opts.excludedDirectories) !== null && _c !== void 0 ? _c : [],
|
|
18
|
+
log: opts.log || fastify.log,
|
|
19
|
+
});
|
|
20
|
+
await (0, router_js_1.registerRoutes)(fastify, routes);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const globbedFiles = await (0, globber_js_1.globFiles)(Object.assign(Object.assign({}, opts), { routeFile: (_d = opts.routeFile) !== null && _d !== void 0 ? _d : 'routes', directory: (_e = opts.directory) !== null && _e !== void 0 ? _e : 'src/plugins', log: opts.log || fastify.log, excludedDirectories: (_f = opts.excludedDirectories) !== null && _f !== void 0 ? _f : [], recursive: (_g = opts.recursive) !== null && _g !== void 0 ? _g : false }));
|
|
11
24
|
await Promise.all(globbedFiles.map(async (globbedFile) => {
|
|
12
25
|
await fastify.register(globbedFile);
|
|
13
26
|
}));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyPluginAsync } from "fastify";
|
|
2
|
+
import type pino from "pino";
|
|
3
|
+
export type RouteEntry = {
|
|
4
|
+
prefix: string;
|
|
5
|
+
plugin: FastifyPluginAsync;
|
|
6
|
+
};
|
|
7
|
+
export declare const segmentToPrefix: (name: string) => string;
|
|
8
|
+
type ScanOptions = {
|
|
9
|
+
routeFile: string;
|
|
10
|
+
packageType: string;
|
|
11
|
+
excludedDirectories: string[];
|
|
12
|
+
log?: pino.BaseLogger;
|
|
13
|
+
};
|
|
14
|
+
export declare const scanRoutes: (dir: string, opts: ScanOptions, prefix?: string) => Promise<RouteEntry[]>;
|
|
15
|
+
export declare const registerRoutes: (fastify: FastifyInstance, routes: RouteEntry[]) => Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerRoutes = exports.scanRoutes = exports.segmentToPrefix = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const promises_1 = require("node:fs/promises");
|
|
9
|
+
const globber_js_1 = require("./globber.js");
|
|
10
|
+
const segmentToPrefix = (name) => {
|
|
11
|
+
if (name.startsWith("_") || /^\(.*\)$/.test(name))
|
|
12
|
+
return "";
|
|
13
|
+
const param = name.match(/^\[(.+)\]$/);
|
|
14
|
+
if (param)
|
|
15
|
+
return `/:${param[1]}`;
|
|
16
|
+
return `/${name}`;
|
|
17
|
+
};
|
|
18
|
+
exports.segmentToPrefix = segmentToPrefix;
|
|
19
|
+
const scanRoutes = async (dir, opts, prefix = "") => {
|
|
20
|
+
const entries = await (0, promises_1.readdir)(dir, { withFileTypes: true });
|
|
21
|
+
const files = entries.filter((e) => e.isFile()).map((e) => e.name);
|
|
22
|
+
const plugin = await (0, globber_js_1.importPlugin)(dir, opts.routeFile, opts.packageType, files, opts.log);
|
|
23
|
+
const nested = await Promise.all(entries
|
|
24
|
+
.filter((e) => e.isDirectory() && !opts.excludedDirectories.includes(e.name))
|
|
25
|
+
.map((e) => (0, exports.scanRoutes)(node_path_1.default.join(dir, e.name), opts, prefix + (0, exports.segmentToPrefix)(e.name))));
|
|
26
|
+
return [...(plugin ? [{ prefix, plugin }] : []), ...nested.flat()];
|
|
27
|
+
};
|
|
28
|
+
exports.scanRoutes = scanRoutes;
|
|
29
|
+
const registerRoutes = async (fastify, routes) => {
|
|
30
|
+
for (const { prefix, plugin } of routes) {
|
|
31
|
+
await fastify.register(plugin, prefix ? { prefix } : {});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
exports.registerRoutes = registerRoutes;
|
package/lib/esm/globber.d.ts
CHANGED
|
@@ -6,7 +6,16 @@ export type Globber = {
|
|
|
6
6
|
log?: pino.BaseLogger;
|
|
7
7
|
excludedDirectories?: string[];
|
|
8
8
|
recursive?: boolean;
|
|
9
|
+
fileBasedRouting?: boolean;
|
|
9
10
|
};
|
|
10
|
-
export declare const
|
|
11
|
+
export declare const getPackageType: (directory: string) => Promise<any>;
|
|
12
|
+
export declare const resolvePluginsDirectory: (opts: Pick<Globber, "directory" | "startingDirectory"> & {
|
|
13
|
+
directory: string;
|
|
14
|
+
}) => Promise<{
|
|
15
|
+
packageType: any;
|
|
16
|
+
pluginsDirectory: string;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const importPlugin: (dir: string, fileName: string, packageType: string, entries: string[], log?: pino.BaseLogger) => Promise<any | undefined>;
|
|
19
|
+
export declare const globFiles: (opts: Required<Omit<Globber, "fileBasedRouting">> & {
|
|
11
20
|
log?: pino.BaseLogger;
|
|
12
21
|
}) => Promise<any[]>;
|
package/lib/esm/globber.js
CHANGED
|
@@ -2,8 +2,7 @@ import { readFile, readdir } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import url, { fileURLToPath } from "node:url";
|
|
4
4
|
import pkgUp from "pkg-up";
|
|
5
|
-
|
|
6
|
-
const getPackageType = async (directory) => {
|
|
5
|
+
export const getPackageType = async (directory) => {
|
|
7
6
|
const nearestPackage = await pkgUp({ cwd: directory });
|
|
8
7
|
if (nearestPackage) {
|
|
9
8
|
if ('require' in global) {
|
|
@@ -13,19 +12,37 @@ const getPackageType = async (directory) => {
|
|
|
13
12
|
return JSON.parse(file).type;
|
|
14
13
|
}
|
|
15
14
|
};
|
|
15
|
+
export const resolvePluginsDirectory = async (opts) => {
|
|
16
|
+
const packageType = await getPackageType(opts.directory);
|
|
17
|
+
const startingDirectory = packageType === 'module' ? path.dirname(fileURLToPath(opts.startingDirectory)) : opts.startingDirectory;
|
|
18
|
+
return { packageType, pluginsDirectory: path.join(startingDirectory, opts.directory) };
|
|
19
|
+
};
|
|
20
|
+
export const importPlugin = async (dir, fileName, packageType, entries, log) => {
|
|
21
|
+
const name = fileName.replace('.js', '');
|
|
22
|
+
if (!entries.some((f) => f === `${name}.js` || f === `${name}.ts`))
|
|
23
|
+
return undefined;
|
|
24
|
+
const osPath = path.join(dir, packageType === 'module' ? `${name}.js` : name);
|
|
25
|
+
try {
|
|
26
|
+
if (packageType === 'module') {
|
|
27
|
+
return (await import(url.pathToFileURL(osPath).href)).default;
|
|
28
|
+
}
|
|
29
|
+
return require(osPath);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
log === null || log === void 0 ? void 0 : log.error({ error: e, path: osPath }, '@efebia/fastify-auto-import error on importing plugin');
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
16
36
|
const collectRecursivePlugins = async (dir, opts) => {
|
|
17
|
-
var _a;
|
|
18
37
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
continue;
|
|
23
|
-
if (opts.excludedDirectories.includes(entry.name))
|
|
24
|
-
continue;
|
|
38
|
+
const dirEntries = entries.filter((entry) => entry.isDirectory() && !opts.excludedDirectories.includes(entry.name));
|
|
39
|
+
const perDirResults = await Promise.all(dirEntries.map(async (entry) => {
|
|
40
|
+
var _a;
|
|
25
41
|
const subDir = path.join(dir, entry.name);
|
|
26
42
|
const subEntries = await readdir(subDir);
|
|
27
43
|
const routeFileName = opts.routeFile.replace('.js', '');
|
|
28
44
|
const hasRouteFile = subEntries.some((f) => f === `${routeFileName}.js` || f === `${routeFileName}.ts`);
|
|
45
|
+
const own = [];
|
|
29
46
|
if (hasRouteFile) {
|
|
30
47
|
const osPath = path.join(subDir, opts.packageType === 'module' ? `${routeFileName}.js` : routeFileName);
|
|
31
48
|
const href = url.pathToFileURL(osPath).href;
|
|
@@ -33,12 +50,12 @@ const collectRecursivePlugins = async (dir, opts) => {
|
|
|
33
50
|
if (opts.packageType === 'module') {
|
|
34
51
|
const imported = await import(href);
|
|
35
52
|
if (imported.default)
|
|
36
|
-
|
|
53
|
+
own.push(imported.default);
|
|
37
54
|
}
|
|
38
55
|
else {
|
|
39
56
|
const imported = require(osPath);
|
|
40
57
|
if (imported)
|
|
41
|
-
|
|
58
|
+
own.push(imported);
|
|
42
59
|
}
|
|
43
60
|
}
|
|
44
61
|
catch (e) {
|
|
@@ -47,9 +64,9 @@ const collectRecursivePlugins = async (dir, opts) => {
|
|
|
47
64
|
}
|
|
48
65
|
}
|
|
49
66
|
const nested = await collectRecursivePlugins(subDir, opts);
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
return
|
|
67
|
+
return [...own, ...nested];
|
|
68
|
+
}));
|
|
69
|
+
return perDirResults.flat();
|
|
53
70
|
};
|
|
54
71
|
export const globFiles = async (opts) => {
|
|
55
72
|
const packageType = await getPackageType(opts.directory);
|
|
@@ -66,7 +83,7 @@ export const globFiles = async (opts) => {
|
|
|
66
83
|
}
|
|
67
84
|
const routesDirectories = await readdir(pluginsDirectory);
|
|
68
85
|
const filteredDirectories = routesDirectories.filter(dir => !opts.excludedDirectories.includes(dir));
|
|
69
|
-
const importedFiles = await
|
|
86
|
+
const importedFiles = await Promise.all(filteredDirectories.map(async (dir) => {
|
|
70
87
|
var _a;
|
|
71
88
|
const osPath = path.join(pluginsDirectory, dir, packageType === 'module' ? `${opts.routeFile.replace(".js", "")}.js` : opts.routeFile.replace(".js", ""));
|
|
72
89
|
const href = url.pathToFileURL(osPath).href;
|
package/lib/esm/index.js
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import fp from 'fastify-plugin';
|
|
2
|
-
import { globFiles } from './globber.js';
|
|
2
|
+
import { globFiles, resolvePluginsDirectory } from './globber.js';
|
|
3
|
+
import { registerRoutes, scanRoutes } from './router.js';
|
|
3
4
|
const plugin = fp(async (fastify, opts) => {
|
|
4
|
-
var _a, _b, _c, _d;
|
|
5
|
-
|
|
5
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
6
|
+
if (opts.fileBasedRouting) {
|
|
7
|
+
const directory = (_a = opts.directory) !== null && _a !== void 0 ? _a : 'src/plugins';
|
|
8
|
+
const { packageType, pluginsDirectory } = await resolvePluginsDirectory(Object.assign(Object.assign({}, opts), { directory }));
|
|
9
|
+
const routes = await scanRoutes(pluginsDirectory, {
|
|
10
|
+
routeFile: (_b = opts.routeFile) !== null && _b !== void 0 ? _b : 'routes',
|
|
11
|
+
packageType,
|
|
12
|
+
excludedDirectories: (_c = opts.excludedDirectories) !== null && _c !== void 0 ? _c : [],
|
|
13
|
+
log: opts.log || fastify.log,
|
|
14
|
+
});
|
|
15
|
+
await registerRoutes(fastify, routes);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const globbedFiles = await globFiles(Object.assign(Object.assign({}, opts), { routeFile: (_d = opts.routeFile) !== null && _d !== void 0 ? _d : 'routes', directory: (_e = opts.directory) !== null && _e !== void 0 ? _e : 'src/plugins', log: opts.log || fastify.log, excludedDirectories: (_f = opts.excludedDirectories) !== null && _f !== void 0 ? _f : [], recursive: (_g = opts.recursive) !== null && _g !== void 0 ? _g : false }));
|
|
6
19
|
await Promise.all(globbedFiles.map(async (globbedFile) => {
|
|
7
20
|
await fastify.register(globbedFile);
|
|
8
21
|
}));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyPluginAsync } from "fastify";
|
|
2
|
+
import type pino from "pino";
|
|
3
|
+
export type RouteEntry = {
|
|
4
|
+
prefix: string;
|
|
5
|
+
plugin: FastifyPluginAsync;
|
|
6
|
+
};
|
|
7
|
+
export declare const segmentToPrefix: (name: string) => string;
|
|
8
|
+
type ScanOptions = {
|
|
9
|
+
routeFile: string;
|
|
10
|
+
packageType: string;
|
|
11
|
+
excludedDirectories: string[];
|
|
12
|
+
log?: pino.BaseLogger;
|
|
13
|
+
};
|
|
14
|
+
export declare const scanRoutes: (dir: string, opts: ScanOptions, prefix?: string) => Promise<RouteEntry[]>;
|
|
15
|
+
export declare const registerRoutes: (fastify: FastifyInstance, routes: RouteEntry[]) => Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readdir } from "node:fs/promises";
|
|
3
|
+
import { importPlugin } from "./globber.js";
|
|
4
|
+
export const segmentToPrefix = (name) => {
|
|
5
|
+
if (name.startsWith("_") || /^\(.*\)$/.test(name))
|
|
6
|
+
return "";
|
|
7
|
+
const param = name.match(/^\[(.+)\]$/);
|
|
8
|
+
if (param)
|
|
9
|
+
return `/:${param[1]}`;
|
|
10
|
+
return `/${name}`;
|
|
11
|
+
};
|
|
12
|
+
export const scanRoutes = async (dir, opts, prefix = "") => {
|
|
13
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
14
|
+
const files = entries.filter((e) => e.isFile()).map((e) => e.name);
|
|
15
|
+
const plugin = await importPlugin(dir, opts.routeFile, opts.packageType, files, opts.log);
|
|
16
|
+
const nested = await Promise.all(entries
|
|
17
|
+
.filter((e) => e.isDirectory() && !opts.excludedDirectories.includes(e.name))
|
|
18
|
+
.map((e) => scanRoutes(path.join(dir, e.name), opts, prefix + segmentToPrefix(e.name))));
|
|
19
|
+
return [...(plugin ? [{ prefix, plugin }] : []), ...nested.flat()];
|
|
20
|
+
};
|
|
21
|
+
export const registerRoutes = async (fastify, routes) => {
|
|
22
|
+
for (const { prefix, plugin } of routes) {
|
|
23
|
+
await fastify.register(plugin, prefix ? { prefix } : {});
|
|
24
|
+
}
|
|
25
|
+
};
|