router.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // src/router/reg-exp-router/router.ts
  2. import {
  3. MESSAGE_MATCHER_IS_ALREADY_BUILT,
  4. METHOD_NAME_ALL,
  5. UnsupportedPathError
  6. } from "../../router.js";
  7. import { checkOptionalParameter } from "../../utils/url.js";
  8. import { match, emptyParam } from "./matcher.js";
  9. import { PATH_ERROR } from "./node.js";
  10. import { Trie } from "./trie.js";
  11. var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
  12. var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
  13. function buildWildcardRegExp(path) {
  14. return wildcardRegExpCache[path] ??= new RegExp(
  15. path === "*" ? "" : `^${path.replace(
  16. /\/\*$|([.\\+*[^\]$()])/g,
  17. (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)"
  18. )}$`
  19. );
  20. }
  21. function clearWildcardRegExpCache() {
  22. wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
  23. }
  24. function buildMatcherFromPreprocessedRoutes(routes) {
  25. const trie = new Trie();
  26. const handlerData = [];
  27. if (routes.length === 0) {
  28. return nullMatcher;
  29. }
  30. const routesWithStaticPathFlag = routes.map(
  31. (route) => [!/\*|\/:/.test(route[0]), ...route]
  32. ).sort(
  33. ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length
  34. );
  35. const staticMap = /* @__PURE__ */ Object.create(null);
  36. for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
  37. const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
  38. if (pathErrorCheckOnly) {
  39. staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
  40. } else {
  41. j++;
  42. }
  43. let paramAssoc;
  44. try {
  45. paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
  46. } catch (e) {
  47. throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
  48. }
  49. if (pathErrorCheckOnly) {
  50. continue;
  51. }
  52. handlerData[j] = handlers.map(([h, paramCount]) => {
  53. const paramIndexMap = /* @__PURE__ */ Object.create(null);
  54. paramCount -= 1;
  55. for (; paramCount >= 0; paramCount--) {
  56. const [key, value] = paramAssoc[paramCount];
  57. paramIndexMap[key] = value;
  58. }
  59. return [h, paramIndexMap];
  60. });
  61. }
  62. const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
  63. for (let i = 0, len = handlerData.length; i < len; i++) {
  64. for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
  65. const map = handlerData[i][j]?.[1];
  66. if (!map) {
  67. continue;
  68. }
  69. const keys = Object.keys(map);
  70. for (let k = 0, len3 = keys.length; k < len3; k++) {
  71. map[keys[k]] = paramReplacementMap[map[keys[k]]];
  72. }
  73. }
  74. }
  75. const handlerMap = [];
  76. for (const i in indexReplacementMap) {
  77. handlerMap[i] = handlerData[indexReplacementMap[i]];
  78. }
  79. return [regexp, handlerMap, staticMap];
  80. }
  81. function findMiddleware(middleware, path) {
  82. if (!middleware) {
  83. return void 0;
  84. }
  85. for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
  86. if (buildWildcardRegExp(k).test(path)) {
  87. return [...middleware[k]];
  88. }
  89. }
  90. return void 0;
  91. }
  92. var RegExpRouter = class {
  93. name = "RegExpRouter";
  94. #middleware;
  95. #routes;
  96. constructor() {
  97. this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
  98. this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
  99. }
  100. add(method, path, handler) {
  101. const middleware = this.#middleware;
  102. const routes = this.#routes;
  103. if (!middleware || !routes) {
  104. throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
  105. }
  106. if (!middleware[method]) {
  107. ;
  108. [middleware, routes].forEach((handlerMap) => {
  109. handlerMap[method] = /* @__PURE__ */ Object.create(null);
  110. Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
  111. handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
  112. });
  113. });
  114. }
  115. if (path === "/*") {
  116. path = "*";
  117. }
  118. const paramCount = (path.match(/\/:/g) || []).length;
  119. if (/\*$/.test(path)) {
  120. const re = buildWildcardRegExp(path);
  121. if (method === METHOD_NAME_ALL) {
  122. Object.keys(middleware).forEach((m) => {
  123. middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
  124. });
  125. } else {
  126. middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
  127. }
  128. Object.keys(middleware).forEach((m) => {
  129. if (method === METHOD_NAME_ALL || method === m) {
  130. Object.keys(middleware[m]).forEach((p) => {
  131. re.test(p) && middleware[m][p].push([handler, paramCount]);
  132. });
  133. }
  134. });
  135. Object.keys(routes).forEach((m) => {
  136. if (method === METHOD_NAME_ALL || method === m) {
  137. Object.keys(routes[m]).forEach(
  138. (p) => re.test(p) && routes[m][p].push([handler, paramCount])
  139. );
  140. }
  141. });
  142. return;
  143. }
  144. const paths = checkOptionalParameter(path) || [path];
  145. for (let i = 0, len = paths.length; i < len; i++) {
  146. const path2 = paths[i];
  147. Object.keys(routes).forEach((m) => {
  148. if (method === METHOD_NAME_ALL || method === m) {
  149. routes[m][path2] ||= [
  150. ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
  151. ];
  152. routes[m][path2].push([handler, paramCount - len + i + 1]);
  153. }
  154. });
  155. }
  156. }
  157. match = match;
  158. buildAllMatchers() {
  159. const matchers = /* @__PURE__ */ Object.create(null);
  160. Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
  161. matchers[method] ||= this.#buildMatcher(method);
  162. });
  163. this.#middleware = this.#routes = void 0;
  164. clearWildcardRegExpCache();
  165. return matchers;
  166. }
  167. #buildMatcher(method) {
  168. const routes = [];
  169. let hasOwnRoute = method === METHOD_NAME_ALL;
  170. [this.#middleware, this.#routes].forEach((r) => {
  171. const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
  172. if (ownRoute.length !== 0) {
  173. hasOwnRoute ||= true;
  174. routes.push(...ownRoute);
  175. } else if (method !== METHOD_NAME_ALL) {
  176. routes.push(
  177. ...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])
  178. );
  179. }
  180. });
  181. if (!hasOwnRoute) {
  182. return null;
  183. } else {
  184. return buildMatcherFromPreprocessedRoutes(routes);
  185. }
  186. }
  187. };
  188. export {
  189. RegExpRouter
  190. };