index.cjs 9.6 KB

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