92 lines
3.1 KiB
Plaintext
92 lines
3.1 KiB
Plaintext
import { createNullObject } from "../utils.js";
|
|
//#region src/router/reg-exp-router/node.ts
|
|
const LABEL_REG_EXP_STR = "[^/]+";
|
|
const ONLY_WILDCARD_REG_EXP_STR = ".*";
|
|
const TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
|
|
const PATH_ERROR = Symbol();
|
|
const regExpMetaChars = /* @__PURE__ */ new Set(".\\+*[^]$()");
|
|
/**
|
|
* Sort order:
|
|
* 1. literal
|
|
* 2. special pattern (e.g. :label{[0-9]+})
|
|
* 3. common label pattern (e.g. :label)
|
|
* 4. wildcard
|
|
*/
|
|
function compareKey(a, b) {
|
|
if (a.length === 1) return b.length === 1 ? a < b ? -1 : 1 : -1;
|
|
if (b.length === 1) return 1;
|
|
if (a === ".*" || a === "(?:|/.*)") return b === "(?:|/.*)" ? -1 : 1;
|
|
else if (b === ".*" || b === "(?:|/.*)") return -1;
|
|
if (a === "[^/]+") return 1;
|
|
else if (b === "[^/]+") return -1;
|
|
return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
|
|
}
|
|
var Node = class Node {
|
|
#index;
|
|
#varIndex;
|
|
#children = createNullObject();
|
|
insert(tokens, index, paramMap, context, isStatic) {
|
|
let node = this;
|
|
for (let i = 0, len = tokens.length; i < len; i++) {
|
|
const token = tokens[i];
|
|
const pattern = token.length === 1 ? token === "*" ? i === len - 1 ? [
|
|
"",
|
|
"",
|
|
".*"
|
|
] : [
|
|
"",
|
|
"",
|
|
LABEL_REG_EXP_STR
|
|
] : null : token === "/*" ? [
|
|
"",
|
|
"",
|
|
TAIL_WILDCARD_REG_EXP_STR
|
|
] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
|
|
let nextNode;
|
|
if (pattern) {
|
|
const name = pattern[1];
|
|
let regexpStr = pattern[2] || "[^/]+";
|
|
if (name && pattern[2]) {
|
|
if (regexpStr === ".*") throw PATH_ERROR;
|
|
regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
|
|
if (/\((?!\?:)/.test(regexpStr)) throw PATH_ERROR;
|
|
if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) throw PATH_ERROR;
|
|
}
|
|
nextNode = node.#children[regexpStr];
|
|
if (!nextNode) {
|
|
if (regexpStr !== ".*" && regexpStr !== "(?:|/.*)") {
|
|
for (const k in node.#children) if ((regexpStr.length > 1 || k.length > 1) && k !== ".*" && k !== "(?:|/.*)") throw PATH_ERROR;
|
|
}
|
|
nextNode = node.#children[regexpStr] = new Node();
|
|
}
|
|
if (name !== "") {
|
|
nextNode.#varIndex ??= context.varIndex++;
|
|
paramMap.push([name, nextNode.#varIndex]);
|
|
}
|
|
} else {
|
|
nextNode = node.#children[token];
|
|
if (!nextNode) {
|
|
for (const k in node.#children) if (k.length > 1 && k !== ".*" && k !== "(?:|/.*)") throw PATH_ERROR;
|
|
nextNode = node.#children[token] = new Node();
|
|
}
|
|
}
|
|
node = nextNode;
|
|
}
|
|
if (node.#index !== void 0) throw PATH_ERROR;
|
|
node.#index = isStatic ? -1 : index;
|
|
}
|
|
buildRegExpStr() {
|
|
const strList = Object.keys(this.#children).sort(compareKey).map((k) => {
|
|
const c = this.#children[k];
|
|
const childStr = c.buildRegExpStr();
|
|
return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr;
|
|
}).filter(Boolean);
|
|
if (typeof this.#index === "number" && this.#index !== -1) strList.unshift(`#${this.#index}`);
|
|
if (strList.length === 0) return "";
|
|
if (strList.length === 1) return strList[0];
|
|
return "(?:" + strList.join("|") + ")";
|
|
}
|
|
};
|
|
//#endregion
|
|
export { LABEL_REG_EXP_STR, Node, ONLY_WILDCARD_REG_EXP_STR, PATH_ERROR, TAIL_WILDCARD_REG_EXP_STR };
|