route-path-match
Version:
compile route path template, match route path params
142 lines (133 loc) • 3.88 kB
JavaScript
;
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
/**
* @export parseToRule
* @export matchByRule
* @export parseToMatch: default
*/
/**
匹配规则
- 其中 ( ) ? 等3个字符为正则的含义及用法,即分组与可选
- 变量使用形如 :id 或 ::id 前者匹配除/之外的字符,后者匹配任意字符
- 指定变量的正则形如 :id<[0-9]+|all> 其中 < > 字符会被替换为 ( ) 其中包裹的内容会被识别为该变量的正则表达式。其中的反斜杠需要转义,如 \\d+
- 变量名只支持使用驼峰形式,比如 :groupId
使用示例
- /assets/::filePath
- /:lang/:group/:gid
- (/:lang)?/:group(/:gid)?
- (/:lang)?/:group/:gid(:ext<.js|.json>)?
*/
/**
* @desc 从 路径模板 转换为 路径匹配规则
* @param {string} pathTpl - 路径匹配模板,形如 /:group/:id
* @return {object} pathMatcherRule - 路由匹配规则
* @return {array<string>} pathMatcherRule.names - 变量名称列表
* @return {regexp|null} pathMatcherRule.regexp - 用来匹配的正则
* @return {string} pathMatcherRule.string - 路径模板字符串
*/
function parseToRule(pathTpl) {
let startsMode = false;
if (pathTpl.startsWith('^')) {
pathTpl = pathTpl.slice(1);
startsMode = true;
}
let regexpMode = false;
const names = [];
const regexpString = pathTpl.replace(/([\/\[\]^$.*+{}])|(\()|:(:)?([a-z][a-z0-9]*)(?:<(.*?)>)?/gi, (matched, keychar, patten, multi, name, regTpl, index, fullTpl) => {
if (keychar) {
return '\\' + keychar;
}
else if (patten) {
regexpMode = true;
return '(?:';
}
else {
regexpMode = true;
names.push(name);
if (!regTpl) {
if (multi) {
regTpl = '.+';
}
else {
const nIndex = index + matched.length;
const pIndex = fullTpl.indexOf(':', nIndex);
const sIndex = fullTpl.indexOf('/', nIndex);
const hasNextPlactholder = pIndex !== - 1 && (sIndex === - 1 || pIndex < sIndex);
regTpl = '[^\\/]+' + (hasNextPlactholder ? '?': '');
}
}
else {
regTpl = regTpl.replace(/\(/g, '(?:');
}
return `(${regTpl})`;
}
});
if (!regexpMode && regexpString.includes('?')) {
regexpMode = true;
}
let flag;
let value;
{
if (regexpMode) {
flag = 'r';
value = new RegExp(`^${regexpString}${startsMode ? '': '$'}`);
}
else {
flag = startsMode ? '^': '=';
value = pathTpl;
}
}
return {
raw: pathTpl,
flag,
value,
names
};
}
/**
* @desc 从 路径匹配规则 匹配到 变量对象
* @param {object} pathMatcherRule - 路由匹配规则
* @param {array<string>} pathMatcherRule.names - 路由匹配正则
* @param {regexp|null} [pathMatcherRule.regexp] - 路由匹配正则
* @param {string} [pathMatcherRule.string] - 路由匹配的模板字符串
* @param {string} path - pathname of route 不包含问号的部分
* @return {object<string>|undefined} - params
*/
function matchByRule(pathMatcherRule, path) {
const { flag, value, names } = pathMatcherRule;
switch (flag) {
case 'r': {
const matched = path.match(value);
if (matched) {
const params = {};
for (let i = 0; i < names.length; i++) {
let name = names[i];
params[name] = matched[i + 1];
}
return params;
}
break;
}
case '^':
if (path.startsWith(value)) {
return {};
}
break;
default:
if (path === value) {
return {};
}
}
}
/**
* @desc 从 路径模板 转换为 路径匹配方法
* @param {string} pathTpl - 路由匹配模板语法,形如 /:group/:id 不包含问号的部分
* @return {function} - match
*/
function parseToMatch(...args) {
const pathMatcherRule = parseToRule(...args);
return matchByRule.bind(null, pathMatcherRule);
}
exports.default = parseToMatch;
exports.matchByRule = matchByRule;
exports.parseToRule = parseToRule;