@zenweb/router
Version:
Zenweb Router module
257 lines (256 loc) • 7.21 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParamPath = void 0;
exports.parseParams = parseParams;
const utils_1 = require("./utils");
/**
* 是否为参数名允许的字符
* @param char 字符
* @returns
*/
function isParmaChar(char) {
const code = char.charCodeAt(0);
// 0-9
if (code >= 48 && code <= 57)
return true;
// A-Z
if (code >= 65 && code <= 90)
return true;
// a-z
if (code >= 97 && code <= 122)
return true;
// _
if (code === 95)
return true;
return false;
}
class SimpleParamParse {
constructor(name) {
this.name = name;
this.SORT = 1000;
}
parse(input) {
return {
[this.name]: input,
};
}
}
class PrefixParamParse {
constructor(name, prefix) {
this.name = name;
this.prefix = prefix;
this.SORT = 0;
}
parse(input) {
if (input.startsWith(this.prefix)) {
return {
[this.name]: input.substring(this.prefix.length),
};
}
}
}
class SuffixParamParse {
constructor(name, suffix) {
this.name = name;
this.suffix = suffix;
this.SORT = 0;
}
parse(input) {
if (input.endsWith(this.suffix)) {
return {
[this.name]: input.substring(0, input.length - this.suffix.length),
};
}
}
}
class BeetwenParamParse {
constructor(name, prefix, suffix) {
this.name = name;
this.prefix = prefix;
this.suffix = suffix;
this.SORT = 0;
}
parse(input) {
if (input.startsWith(this.prefix) && input.endsWith(this.suffix)) {
return {
[this.name]: input.substring(this.prefix.length, input.length - this.suffix.length),
};
}
}
}
class RegexParamParse {
constructor(regex) {
this.regex = regex;
this.SORT = 50;
}
parse(input) {
const result = this.regex.exec(input);
if (result) {
return (0, utils_1.regexResultToParams)(result);
}
}
}
function parseParams(segment) {
let paramStart = false;
let regexStart = false;
let text = '';
let param = '';
let regex = '';
const values = [];
for (const c of segment) {
if (c === ':') {
if (text)
values.push({ text });
if (param)
values.push({ param });
text = '';
param = '';
paramStart = true;
continue;
}
if (c === '(') {
regexStart = true;
continue;
}
if (c === ')') {
if (param)
values.push({ param, regex });
else
values.push({ regex });
param = '';
regex = '';
regexStart = false;
paramStart = false;
continue;
}
if (regexStart) {
regex += c;
continue;
}
if (paramStart) {
if (c === '{')
continue;
if (isParmaChar(c)) {
param += c;
continue;
}
values.push({ param });
param = '';
paramStart = false;
if (c === '}')
continue;
}
text += c;
}
if (param)
values.push({ param });
if (text)
values.push({ text });
// 简单模式
if (values.length === 1 && values[0].param && !values[0].regex) {
return new SimpleParamParse(values[0].param);
}
// 前缀模式
if (values.length === 2 && values[0].text && values[1].param && !values[1].regex) {
return new PrefixParamParse(values[1].param, values[0].text);
}
// 后缀模式
if (values.length === 2 && values[0].param && !values[0].regex && values[1].text) {
return new SuffixParamParse(values[0].param, values[1].text);
}
// 两端模式
if (values.length === 3 && values[0].text && values[1].param && !values[1].regex && values[2].text) {
return new BeetwenParamParse(values[1].param, values[0].text, values[2].text);
}
// 正则模式
regex = '^';
for (const val of values) {
if (val.param) {
regex += `(?<${val.param}>${val.regex || '.*'})`;
}
else if (val.regex) {
regex += `(${val.regex})`;
}
else {
regex += val.text;
}
}
regex += '$';
return new RegexParamParse(new RegExp(regex));
}
class TrieNode {
constructor() {
this.children = new Map();
}
}
class ParamPath {
constructor() {
this.root = new TrieNode();
}
addRoute(path, handler) {
const segments = path.split('/').filter(Boolean);
let node = this.root;
for (const segment of segments) {
if (segment.includes(':')) {
// 参数节点
const _node = new TrieNode();
_node.paramParse = parseParams(segment);
if (!node.paramNodes) {
node.paramNodes = [_node];
}
else {
node.paramNodes.push(_node);
}
// 调整优先级, 简单匹配放到最后
if (node.paramNodes.length > 1) {
node.paramNodes.sort((a, b) => (a.paramParse?.SORT || 0) - (b.paramParse?.SORT || 0));
}
node = _node;
}
else {
// 静态节点
if (!node.children.has(segment)) {
node.children.set(segment, new TrieNode());
}
node = node.children.get(segment);
}
}
node.handler = handler;
}
match(path) {
const segments = path.split('/').filter(Boolean);
return this.matchNode(this.root, segments, 0, {});
}
matchNode(node, segments, index, params) {
if (index === segments.length) {
if (!node.handler) {
return;
}
return { params, handler: node.handler };
}
const currentSegment = segments[index];
// 1. 尝试静态匹配
if (node.children.has(currentSegment)) {
const result = this.matchNode(node.children.get(currentSegment), segments, index + 1, params);
if (result)
return result;
}
// 2. 尝试参数匹配
if (node.paramNodes?.length) {
for (const _node of node.paramNodes) {
if (!_node.paramParse)
continue;
const _params = _node.paramParse.parse(currentSegment);
if (!_params) {
continue;
}
const result = this.matchNode(_node, segments, index + 1, params);
if (result) {
Object.assign(params, _params);
return result;
}
}
}
}
}
exports.ParamPath = ParamPath;