mirror of
https://github.com/denoland/deno.git
synced 2024-12-27 09:39:08 -05:00
169 lines
6.6 KiB
JavaScript
169 lines
6.6 KiB
JavaScript
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.Node = void 0;
|
||
|
const router_1 = require("../../router");
|
||
|
const url_1 = require("../../utils/url");
|
||
|
function findParam(node, name) {
|
||
|
for (let i = 0, len = node.patterns.length; i < len; i++) {
|
||
|
if (typeof node.patterns[i] === 'object' && node.patterns[i][1] === name) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
const nodes = Object.values(node.children);
|
||
|
for (let i = 0, len = nodes.length; i < len; i++) {
|
||
|
if (findParam(nodes[i], name)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
class Node {
|
||
|
constructor(method, handler, children) {
|
||
|
this.order = 0;
|
||
|
this.children = children || {};
|
||
|
this.methods = [];
|
||
|
this.name = '';
|
||
|
if (method && handler) {
|
||
|
const m = {};
|
||
|
m[method] = { handler: handler, score: 0, name: this.name };
|
||
|
this.methods = [m];
|
||
|
}
|
||
|
this.patterns = [];
|
||
|
this.handlerSetCache = {};
|
||
|
}
|
||
|
insert(method, path, handler) {
|
||
|
this.name = `${method} ${path}`;
|
||
|
this.order = ++this.order;
|
||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||
|
let curNode = this;
|
||
|
const parts = (0, url_1.splitPath)(path);
|
||
|
const parentPatterns = [];
|
||
|
const errorMessage = (name) => {
|
||
|
return `Duplicate param name, use another name instead of '${name}' - ${method} ${path} <--- '${name}'`;
|
||
|
};
|
||
|
for (let i = 0, len = parts.length; i < len; i++) {
|
||
|
const p = parts[i];
|
||
|
if (Object.keys(curNode.children).includes(p)) {
|
||
|
parentPatterns.push(...curNode.patterns);
|
||
|
curNode = curNode.children[p];
|
||
|
continue;
|
||
|
}
|
||
|
curNode.children[p] = new Node();
|
||
|
const pattern = (0, url_1.getPattern)(p);
|
||
|
if (pattern) {
|
||
|
if (typeof pattern === 'object') {
|
||
|
for (let j = 0, len = parentPatterns.length; j < len; j++) {
|
||
|
if (typeof parentPatterns[j] === 'object' && parentPatterns[j][1] === pattern[1]) {
|
||
|
throw new Error(errorMessage(pattern[1]));
|
||
|
}
|
||
|
}
|
||
|
if (Object.values(curNode.children).some((n) => findParam(n, pattern[1]))) {
|
||
|
throw new Error(errorMessage(pattern[1]));
|
||
|
}
|
||
|
}
|
||
|
curNode.patterns.push(pattern);
|
||
|
parentPatterns.push(...curNode.patterns);
|
||
|
}
|
||
|
parentPatterns.push(...curNode.patterns);
|
||
|
curNode = curNode.children[p];
|
||
|
}
|
||
|
if (!curNode.methods.length) {
|
||
|
curNode.methods = [];
|
||
|
}
|
||
|
const m = {};
|
||
|
const handlerSet = { handler: handler, name: this.name, score: this.order };
|
||
|
m[method] = handlerSet;
|
||
|
curNode.methods.push(m);
|
||
|
return curNode;
|
||
|
}
|
||
|
getHandlerSets(node, method, wildcard) {
|
||
|
var _a, _b;
|
||
|
return ((_a = node.handlerSetCache)[_b = `${method}:${wildcard ? '1' : '0'}`] || (_a[_b] = (() => {
|
||
|
const handlerSets = [];
|
||
|
node.methods.map((m) => {
|
||
|
const handlerSet = m[method] || m[router_1.METHOD_NAME_ALL];
|
||
|
if (handlerSet !== undefined) {
|
||
|
const hs = { ...handlerSet };
|
||
|
handlerSets.push(hs);
|
||
|
return;
|
||
|
}
|
||
|
});
|
||
|
return handlerSets;
|
||
|
})()));
|
||
|
}
|
||
|
search(method, path) {
|
||
|
const handlerSets = [];
|
||
|
const params = {};
|
||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||
|
const curNode = this;
|
||
|
let curNodes = [curNode];
|
||
|
const parts = (0, url_1.splitPath)(path);
|
||
|
for (let i = 0, len = parts.length; i < len; i++) {
|
||
|
const part = parts[i];
|
||
|
const isLast = i === len - 1;
|
||
|
const tempNodes = [];
|
||
|
let matched = false;
|
||
|
for (let j = 0, len2 = curNodes.length; j < len2; j++) {
|
||
|
const node = curNodes[j];
|
||
|
const nextNode = node.children[part];
|
||
|
if (nextNode) {
|
||
|
if (isLast === true) {
|
||
|
// '/hello/*' => match '/hello'
|
||
|
if (nextNode.children['*']) {
|
||
|
handlerSets.push(...this.getHandlerSets(nextNode.children['*'], method, true));
|
||
|
}
|
||
|
handlerSets.push(...this.getHandlerSets(nextNode, method));
|
||
|
matched = true;
|
||
|
}
|
||
|
tempNodes.push(nextNode);
|
||
|
}
|
||
|
for (let k = 0, len3 = node.patterns.length; k < len3; k++) {
|
||
|
const pattern = node.patterns[k];
|
||
|
// Wildcard
|
||
|
// '/hello/*/foo' => match /hello/bar/foo
|
||
|
if (pattern === '*') {
|
||
|
const astNode = node.children['*'];
|
||
|
if (astNode) {
|
||
|
handlerSets.push(...this.getHandlerSets(astNode, method));
|
||
|
tempNodes.push(astNode);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
if (part === '')
|
||
|
continue;
|
||
|
// Named match
|
||
|
// `/posts/:id` => match /posts/123
|
||
|
const [key, name, matcher] = pattern;
|
||
|
if (matcher === true || (matcher instanceof RegExp && matcher.test(part))) {
|
||
|
if (typeof key === 'string') {
|
||
|
if (isLast === true) {
|
||
|
handlerSets.push(...this.getHandlerSets(node.children[key], method));
|
||
|
}
|
||
|
tempNodes.push(node.children[key]);
|
||
|
}
|
||
|
// '/book/a' => not-slug
|
||
|
// '/book/:slug' => slug
|
||
|
// GET /book/a ~> no-slug, param['slug'] => undefined
|
||
|
// GET /book/foo ~> slug, param['slug'] => foo
|
||
|
if (typeof name === 'string' && !matched) {
|
||
|
params[name] = part;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
curNodes = tempNodes;
|
||
|
}
|
||
|
if (handlerSets.length <= 0)
|
||
|
return null;
|
||
|
const handlers = handlerSets
|
||
|
.sort((a, b) => {
|
||
|
return a.score - b.score;
|
||
|
})
|
||
|
.map((s) => {
|
||
|
return s.handler;
|
||
|
});
|
||
|
return { handlers, params };
|
||
|
}
|
||
|
}
|
||
|
exports.Node = Node;
|