-
Notifications
You must be signed in to change notification settings - Fork 8
/
index.ts
131 lines (120 loc) · 4.71 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import path from 'path';
import fs from 'fs';
import fp from 'fastify-plugin';
import type {
FastifyInstance,
RawServerBase,
RawServerDefault,
RawRequestDefaultExpression,
RawReplyDefaultExpression,
ContextConfigDefault,
RouteShorthandOptions,
FastifyPluginAsync,
FastifyRequest,
FastifyReply,
HTTPMethods,
} from 'fastify';
import type { RouteGenericInterface, RouteHandlerMethod } from 'fastify/types/route';
const methods: HTTPMethods[] = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS'];
const typeScriptEnabled = Boolean(
// @ts-expect-error 7053 https://github.com/TypeStrong/ts-node/issues/846#issuecomment-631828160
process[Symbol.for('ts-node.register.instance')] || process.env.TS_NODE_DEV,
);
const extensions = ['.js'];
if (typeScriptEnabled) {
extensions.push('.ts');
}
const isRoute = (ext: string) => extensions.includes(ext);
const isTest = (name: string) =>
name.endsWith('.test') || name.endsWith('.spec') || name.endsWith('.bench');
const isDeclaration = (name: string, ext: string) => ext === '.ts' && name.endsWith('.d');
function addRequestHandler(
module: Record<HTTPMethods, NowRequestHandler>,
method: HTTPMethods,
server: FastifyInstance,
fileRouteServerPath: string,
) {
const handler = module[method];
if (handler) {
server.log.debug(`${method} ${fileRouteServerPath}`);
const methodFunctionName = method.toLowerCase() as keyof Pick<
FastifyInstance,
'get' | 'put' | 'delete' | 'head' | 'post' | 'options' | 'patch'
>;
server[methodFunctionName](
fileRouteServerPath,
handler.opts || {},
handler as RouteHandlerMethod,
);
}
}
export async function registerRoutes(server: FastifyInstance, folder: string, pathPrefix = '') {
const registerRoutesFolders = fs
.readdirSync(folder, { withFileTypes: true })
.map(async (folderOrFile) => {
const currentPath = path.join(folder, folderOrFile.name);
const routeServerPath = `${pathPrefix}/${folderOrFile.name
.replace('[', ':')
.replace(']', '')}`;
if (folderOrFile.isDirectory()) {
await registerRoutes(server, currentPath, routeServerPath);
} else if (folderOrFile.isFile()) {
const { ext, name } = path.parse(folderOrFile.name);
if (!isRoute(ext) || isTest(name) || isDeclaration(name, ext)) {
return;
}
let fileRouteServerPath = pathPrefix;
if (name !== 'index') {
fileRouteServerPath += '/' + name.replace('[', ':').replace(/\]?$/, '');
}
if (fileRouteServerPath.length === 0) {
fileRouteServerPath = '/';
}
const module = await import(currentPath);
Object.values(methods).forEach((method) => {
addRequestHandler(module, method as HTTPMethods, server, fileRouteServerPath);
});
}
});
await Promise.all(registerRoutesFolders);
}
interface FastifyNowOpts {
routesFolder: string;
pathPrefix?: string;
}
const fastifyNow: FastifyPluginAsync<FastifyNowOpts> = async (
server: FastifyInstance,
opts: FastifyNowOpts,
) => {
if (!(opts && opts.routesFolder)) {
throw new Error('fastify-now: should provide opts.routesFolder');
}
try {
await registerRoutes(server, opts.routesFolder, opts.pathPrefix);
} catch (error) {
const { message } = error as Error;
throw new Error(`fastify-now: error registering routers: ${message}`);
}
};
type NowRouteHandlerMethod<
RawServer extends RawServerBase = RawServerDefault,
RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>,
RawReply extends RawReplyDefaultExpression<RawServer> = RawReplyDefaultExpression<RawServer>,
RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
ContextConfig = ContextConfigDefault,
> = (
this: FastifyInstance<RawServer, RawRequest, RawReply>,
request: FastifyRequest<RouteGeneric, RawServer, RawRequest>,
reply: FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig>,
server: FastifyInstance<RawServer, RawRequest, RawReply>,
) => void | RouteGeneric['Reply'] | Promise<RouteGeneric['Reply'] | void>;
export interface NowRequestHandler<
RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
ContextConfig = ContextConfigDefault,
RawServer extends RawServerBase = RawServerDefault,
RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>,
RawReply extends RawReplyDefaultExpression<RawServer> = RawReplyDefaultExpression<RawServer>,
> extends NowRouteHandlerMethod<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig> {
opts?: RouteShorthandOptions<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig>;
}
export default fp<FastifyNowOpts>(fastifyNow);