index.cjs 9.6 KB

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