index.cjs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. var __create = Object.create;
  2. var __defProp = Object.defineProperty;
  3. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  4. var __getOwnPropNames = Object.getOwnPropertyNames;
  5. var __getProtoOf = Object.getPrototypeOf;
  6. var __hasOwnProp = Object.prototype.hasOwnProperty;
  7. var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
  8. var __export = (target, all) => {
  9. for (var name in all)
  10. __defProp(target, name, { get: all[name], enumerable: true });
  11. };
  12. var __copyProps = (to, from, except, desc) => {
  13. if (from && typeof from === "object" || typeof from === "function") {
  14. for (let key of __getOwnPropNames(from))
  15. if (!__hasOwnProp.call(to, key) && key !== except)
  16. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  17. }
  18. return to;
  19. };
  20. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  21. // If the importer is in node compatibility mode or this is not an ESM
  22. // file that has been converted to a CommonJS file using a Babel-
  23. // compatible transform (i.e. "__esModule" has not been set), then set
  24. // "default" to the CommonJS "module.exports" for node compatibility.
  25. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  26. mod
  27. ));
  28. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  29. // src/index.js
  30. var src_exports = {};
  31. __export(src_exports, {
  32. PathTrie: () => PathTrie
  33. });
  34. module.exports = __toCommonJS(src_exports);
  35. // src/path-trie.js
  36. var import_js_format2 = require("@e22m4u/js-format");
  37. var import_path_to_regexp = require("path-to-regexp");
  38. // src/utils/create-debugger.js
  39. var import_debug = __toESM(require("debug"), 1);
  40. var import_js_format = require("@e22m4u/js-format");
  41. function createDebugger() {
  42. const debug2 = (0, import_debug.default)(`jsPathTrie`);
  43. return function(message, ...args) {
  44. const interpolatedMessage = (0, import_js_format.format)(message, ...args);
  45. return debug2(interpolatedMessage);
  46. };
  47. }
  48. __name(createDebugger, "createDebugger");
  49. // src/path-trie.js
  50. var debug = createDebugger();
  51. var _PathTrie = class _PathTrie {
  52. /**
  53. * Root node.
  54. *
  55. * @type {Node}
  56. * @private
  57. */
  58. _root = {
  59. token: "",
  60. regexp: void 0,
  61. names: [],
  62. value: void 0,
  63. children: {}
  64. };
  65. /**
  66. * Add value.
  67. *
  68. * @param {string} pathTemplate
  69. * @param {*} value
  70. * @returns {this}
  71. */
  72. add(pathTemplate, value) {
  73. if (typeof pathTemplate !== "string")
  74. throw new import_js_format2.Errorf(
  75. "The first argument of PathTrie.add should be a String, but %v given.",
  76. pathTemplate
  77. );
  78. if (value == null)
  79. throw new import_js_format2.Errorf(
  80. "The second argument of PathTrie.add is required, but %v given.",
  81. value
  82. );
  83. debug("Adding the value to %v.", pathTemplate);
  84. const tokens = pathTemplate.split("/").filter(Boolean);
  85. this._createNode(tokens, 0, value, this._root);
  86. return this;
  87. }
  88. /**
  89. * Match value.
  90. *
  91. * @param {string} path
  92. * @returns {ResolvedValue|undefined}
  93. */
  94. match(path) {
  95. if (typeof path !== "string")
  96. throw new import_js_format2.Errorf(
  97. "The first argument of PathTrie.match should be a String, but %v given.",
  98. path
  99. );
  100. debug("Matching a value with the path %v.", path);
  101. const tokens = path.split("/").filter(Boolean);
  102. const params = {};
  103. const result = this._matchNode(tokens, 0, params, this._root);
  104. if (!result || !result.node.value) return;
  105. return { value: result.node.value, params };
  106. }
  107. /**
  108. * Create node.
  109. *
  110. * @param {string[]} tokens
  111. * @param {number} index
  112. * @param {*} value
  113. * @param {Node} parent
  114. * @returns {Node}
  115. * @private
  116. */
  117. _createNode(tokens, index, value, parent) {
  118. if (tokens.length === 0 && index === 0) {
  119. if (parent.value == null) {
  120. parent.value = value;
  121. } else if (parent.value !== value) {
  122. throw new import_js_format2.Errorf('The duplicate path "" has a different value.');
  123. }
  124. debug("The value has set to the root node.");
  125. return parent;
  126. }
  127. const token = tokens[index];
  128. if (token == null)
  129. throw new import_js_format2.Errorf(
  130. "Invalid index %v has passed to the PathTrie._createNode.",
  131. index
  132. );
  133. const isLast = tokens.length - 1 === index;
  134. let child = parent.children[token];
  135. if (isLast && child != null) {
  136. debug("The node %v already exist.", token);
  137. if (child.value == null) {
  138. child.value = value;
  139. } else if (child.value !== value) {
  140. throw new import_js_format2.Errorf(
  141. "The duplicate path %v has a different value.",
  142. "/" + tokens.join("/")
  143. );
  144. }
  145. return child;
  146. }
  147. debug("The node %v does not exist.", token);
  148. child = {
  149. token,
  150. regexp: void 0,
  151. names: [],
  152. value: void 0,
  153. children: {}
  154. };
  155. if (isLast) {
  156. debug("The node %v is last.", token);
  157. child.value = value;
  158. }
  159. if (token.indexOf(":") > -1) {
  160. debug("The node %v has parameters.", token);
  161. const modifiers = /([?*+{}])/.exec(token);
  162. if (modifiers)
  163. throw new import_js_format2.Errorf(
  164. "The symbol %v is not supported in path %v.",
  165. modifiers[0],
  166. "/" + tokens.join("/")
  167. );
  168. let regexp, keys;
  169. try {
  170. const regexpAndKeys = (0, import_path_to_regexp.pathToRegexp)(token);
  171. regexp = regexpAndKeys.regexp;
  172. keys = regexpAndKeys.keys;
  173. } catch (error) {
  174. if (error.message.indexOf("Missing parameter") > -1)
  175. throw new import_js_format2.Errorf(
  176. 'The symbol ":" should be used to define path parameters, but no parameters found in the path %v.',
  177. "/" + tokens.join("/")
  178. );
  179. throw error;
  180. }
  181. if (Array.isArray(keys) && keys.length) {
  182. child.names = keys.map((p) => `${p.name}`);
  183. child.regexp = regexp;
  184. } else {
  185. throw new import_js_format2.Errorf(
  186. 'The symbol ":" should be used to define path parameters, but no parameters found in the path %v.',
  187. "/" + tokens.join("/")
  188. );
  189. }
  190. debug("Found parameters are %l.", child.names);
  191. }
  192. parent.children[token] = child;
  193. debug("The node %v has created.", token);
  194. if (isLast) return child;
  195. return this._createNode(tokens, index + 1, value, child);
  196. }
  197. /**
  198. * Match node.
  199. *
  200. * @param {string[]} tokens
  201. * @param {number} index
  202. * @param {object} params
  203. * @param {Node} parent
  204. * @returns {ResolvedNode|undefined}
  205. * @private
  206. */
  207. _matchNode(tokens, index, params, parent) {
  208. if (tokens.length === 0 && index === 0) {
  209. if (parent.value) {
  210. debug(
  211. "The path %v matched with the root node.",
  212. "/" + tokens.join("/")
  213. );
  214. return { node: parent, params };
  215. }
  216. return;
  217. }
  218. const token = tokens[index];
  219. if (token == null)
  220. throw new import_js_format2.Errorf(
  221. "Invalid index %v has passed to the PathTrie._matchNode.",
  222. index
  223. );
  224. const resolvedNodes = this._matchChildrenNodes(token, parent);
  225. debug("%v nodes matches the token %v.", resolvedNodes.length, token);
  226. if (!resolvedNodes.length) return;
  227. const isLast = tokens.length - 1 === index;
  228. if (isLast) {
  229. debug("The token %v is last.", token);
  230. for (const child of resolvedNodes) {
  231. debug("The node %v matches the token %v.", child.node.token, token);
  232. if (child.node.value) {
  233. debug("The node %v has a value.", child.node.token);
  234. const paramNames = Object.keys(child.params);
  235. if (paramNames.length) {
  236. paramNames.forEach((name) => {
  237. debug(
  238. "The node %v has parameter %v with the value %v.",
  239. child.node.token,
  240. name,
  241. child.params[name]
  242. );
  243. });
  244. } else {
  245. debug("The node %v has no parameters.", child.node.token);
  246. }
  247. Object.assign(params, child.params);
  248. return { node: child.node, params };
  249. }
  250. }
  251. } else {
  252. for (const child of resolvedNodes) {
  253. const result = this._matchNode(tokens, index + 1, params, child.node);
  254. if (result) {
  255. debug("A value has found for the path %v.", "/" + tokens.join("/"));
  256. const paramNames = Object.keys(child.params);
  257. if (paramNames.length) {
  258. paramNames.forEach((name) => {
  259. debug(
  260. "The node %v has parameter %v with the value %v.",
  261. child.node.token,
  262. name,
  263. child.params[name]
  264. );
  265. });
  266. } else {
  267. debug("The node %v has no parameters.", child.node.token);
  268. }
  269. Object.assign(params, child.params);
  270. return result;
  271. }
  272. }
  273. }
  274. debug("No matched nodes with the path %v.", "/" + tokens.join("/"));
  275. return void 0;
  276. }
  277. /**
  278. * Match children nodes.
  279. *
  280. * @param {string} token
  281. * @param {Node} parent
  282. * @returns {ResolvedNode[]}
  283. * @private
  284. */
  285. _matchChildrenNodes(token, parent) {
  286. const resolvedNodes = [];
  287. let child = parent.children[token];
  288. if (child) {
  289. resolvedNodes.push({ node: child, params: {} });
  290. return resolvedNodes;
  291. }
  292. for (const key in parent.children) {
  293. child = parent.children[key];
  294. if (!child.names || !child.regexp) continue;
  295. const match = child.regexp.exec(token);
  296. if (match) {
  297. const resolved = { node: child, params: {} };
  298. let i = 0;
  299. for (const name of child.names) {
  300. const val = match[++i];
  301. resolved.params[name] = decodeURIComponent(val);
  302. }
  303. resolvedNodes.push(resolved);
  304. }
  305. }
  306. return resolvedNodes;
  307. }
  308. };
  309. __name(_PathTrie, "PathTrie");
  310. var PathTrie = _PathTrie;
  311. // Annotate the CommonJS export names for ESM import in node:
  312. 0 && (module.exports = {
  313. PathTrie
  314. });