-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhandler.ts
94 lines (81 loc) · 2.57 KB
/
handler.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
function getSlashCount(str: string): number {
let count = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] === "/") {
count++;
}
}
return count;
}
const handlerFileNames = new Set([
"get.ts",
"post.ts",
"patch.ts",
"options.ts",
"delete.ts",
"put.ts",
] as const);
type HandlerFileName = typeof handlerFileNames extends Set<infer R> ? R : never;
export function isHandlerFile(fileName: string): fileName is HandlerFileName {
return handlerFileNames.has(fileName as HandlerFileName);
}
const handlerMethods = new Set(["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"] as const);
export type HandlerMethod = typeof handlerMethods extends Set<infer R> ? R : never;
function isHandlerMethod(method: string): method is HandlerMethod {
return handlerMethods.has(method as HandlerMethod);
}
export class Handler {
public readonly filePath: string;
public readonly handlerPath: string;
public readonly params: string[] = [];
public readonly method: HandlerMethod;
public readonly isDefaultHandler: boolean;
private readonly rootPath: string;
constructor(filePath: string, rootPath: string) {
this.filePath = filePath;
this.rootPath = rootPath;
this.isDefaultHandler = filePath.endsWith("$default.ts");
/**
* Set the handler path.
*/
this.handlerPath = this.filePath.slice(0, this.filePath.length - 3).concat(".handler");
/**
* Set the handler method.
*/
const method = this.handlerPath
.slice(this.handlerPath.lastIndexOf("/") + 1, this.handlerPath.length - 8)
.toUpperCase();
if (!isHandlerMethod(method) && this.isDefaultHandler) {
// Doesn't matter what this is, it won't be used.
this.method = "GET";
} else if (isHandlerMethod(method)) {
this.method = method;
} else {
throw new Error(`Invalid handler file method: ${method}`);
}
/**
* Parse out handler path parameters.
*/
const matches = this.handlerPath.matchAll(/{(.*?)}/g);
const used = new Set<string>();
for (const match of matches) {
if (used.has(match[1])) {
throw new Error(
`A path parameter appeared twice in a handler path. Parameter: ${match[1]} Path: ${this.handlerPath}`,
);
}
this.params.push(match[1]);
used.add(match[1]);
}
}
public pathString(): string {
if (this.isDefaultHandler) {
return "$default";
}
const urlPath = this.handlerPath.replace(this.rootPath, "");
const lastSlashPosition = urlPath.lastIndexOf("/");
const slashCount = getSlashCount(urlPath);
const sliceOffset = slashCount === 1 ? 1 : 0;
return `${this.method} ${urlPath.slice(urlPath.indexOf("/"), lastSlashPosition + sliceOffset)}`;
}
}