UNPKG

route-path-match

Version:

compile route path template, match route path params

142 lines (133 loc) 3.88 kB
'use strict'; 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;