UNPKG

@zenweb/router

Version:

Zenweb Router module

257 lines (256 loc) 7.21 kB
"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;