index.cjs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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. let child = parent.children[token];
  134. const isLast = tokens.length - 1 === index;
  135. if (child) {
  136. if (!isLast) {
  137. debug("The node %v already exist.", token);
  138. return this._createNode(tokens, index + 1, value, child);
  139. } else {
  140. debug("The node %v already exist.", token);
  141. if (child.value == null) {
  142. debug("The node %v has the same value.", token);
  143. child.value = value;
  144. } else if (child.value !== value) {
  145. throw new import_js_format2.Errorf(
  146. "The duplicate path %v has a different value.",
  147. "/" + tokens.join("/")
  148. );
  149. }
  150. return child;
  151. }
  152. }
  153. debug("The node %v does not exist.", token);
  154. child = {
  155. token,
  156. regexp: void 0,
  157. names: [],
  158. value: void 0,
  159. children: {}
  160. };
  161. if (isLast) {
  162. debug("The node %v is last.", token);
  163. child.value = value;
  164. }
  165. if (token.indexOf(":") > -1) {
  166. debug("The node %v has parameters.", token);
  167. const modifiers = /([?*+{}])/.exec(token);
  168. if (modifiers)
  169. throw new import_js_format2.Errorf(
  170. "The symbol %v is not supported in path %v.",
  171. modifiers[0],
  172. "/" + tokens.join("/")
  173. );
  174. let regexp, keys;
  175. try {
  176. const regexpAndKeys = (0, import_path_to_regexp.pathToRegexp)(token);
  177. regexp = regexpAndKeys.regexp;
  178. keys = regexpAndKeys.keys;
  179. } catch (error) {
  180. if (error.message.indexOf("Missing parameter") > -1)
  181. throw new import_js_format2.Errorf(
  182. 'The symbol ":" should be used to define path parameters, but no parameters found in the path %v.',
  183. "/" + tokens.join("/")
  184. );
  185. throw error;
  186. }
  187. if (Array.isArray(keys) && keys.length) {
  188. child.names = keys.map((p) => `${p.name}`);
  189. child.regexp = regexp;
  190. } else {
  191. throw new import_js_format2.Errorf(
  192. 'The symbol ":" should be used to define path parameters, but no parameters found in the path %v.',
  193. "/" + tokens.join("/")
  194. );
  195. }
  196. debug("Found parameters are %l.", child.names);
  197. }
  198. parent.children[token] = child;
  199. debug("The node %v has created.", token);
  200. if (isLast) return child;
  201. return this._createNode(tokens, index + 1, value, child);
  202. }
  203. /**
  204. * Match node.
  205. *
  206. * @param {string[]} tokens
  207. * @param {number} index
  208. * @param {object} params
  209. * @param {Node} parent
  210. * @returns {ResolvedNode|undefined}
  211. * @private
  212. */
  213. _matchNode(tokens, index, params, parent) {
  214. if (tokens.length === 0 && index === 0) {
  215. if (parent.value) {
  216. debug(
  217. "The path %v matched with the root node.",
  218. "/" + tokens.join("/")
  219. );
  220. return { node: parent, params };
  221. }
  222. return;
  223. }
  224. const token = tokens[index];
  225. if (token == null)
  226. throw new import_js_format2.Errorf(
  227. "Invalid index %v has passed to the PathTrie._matchNode.",
  228. index
  229. );
  230. const resolvedNodes = this._matchChildrenNodes(token, parent);
  231. debug("%v nodes matches the token %v.", resolvedNodes.length, token);
  232. if (!resolvedNodes.length) return;
  233. const isLast = tokens.length - 1 === index;
  234. if (isLast) {
  235. debug("The token %v is last.", token);
  236. for (const child of resolvedNodes) {
  237. debug("The node %v matches the token %v.", child.node.token, token);
  238. if (child.node.value) {
  239. debug("The node %v has a value.", child.node.token);
  240. const paramNames = Object.keys(child.params);
  241. if (paramNames.length) {
  242. paramNames.forEach((name) => {
  243. debug(
  244. "The node %v has parameter %v with the value %v.",
  245. child.node.token,
  246. name,
  247. child.params[name]
  248. );
  249. });
  250. } else {
  251. debug("The node %v has no parameters.", child.node.token);
  252. }
  253. Object.assign(params, child.params);
  254. return { node: child.node, params };
  255. }
  256. }
  257. } else {
  258. for (const child of resolvedNodes) {
  259. const result = this._matchNode(tokens, index + 1, params, child.node);
  260. if (result) {
  261. debug("A value has found for the path %v.", "/" + tokens.join("/"));
  262. const paramNames = Object.keys(child.params);
  263. if (paramNames.length) {
  264. paramNames.forEach((name) => {
  265. debug(
  266. "The node %v has parameter %v with the value %v.",
  267. child.node.token,
  268. name,
  269. child.params[name]
  270. );
  271. });
  272. } else {
  273. debug("The node %v has no parameters.", child.node.token);
  274. }
  275. Object.assign(params, child.params);
  276. return result;
  277. }
  278. }
  279. }
  280. debug("No matched nodes with the path %v.", "/" + tokens.join("/"));
  281. return void 0;
  282. }
  283. /**
  284. * Match children nodes.
  285. *
  286. * @param {string} token
  287. * @param {Node} parent
  288. * @returns {ResolvedNode[]}
  289. * @private
  290. */
  291. _matchChildrenNodes(token, parent) {
  292. const resolvedNodes = [];
  293. let child = parent.children[token];
  294. if (child) {
  295. resolvedNodes.push({ node: child, params: {} });
  296. return resolvedNodes;
  297. }
  298. for (const key in parent.children) {
  299. child = parent.children[key];
  300. if (!child.names || !child.regexp) continue;
  301. const match = child.regexp.exec(token);
  302. if (match) {
  303. const resolved = { node: child, params: {} };
  304. let i = 0;
  305. for (const name of child.names) {
  306. const val = match[++i];
  307. resolved.params[name] = decodeURIComponent(val);
  308. }
  309. resolvedNodes.push(resolved);
  310. }
  311. }
  312. return resolvedNodes;
  313. }
  314. };
  315. __name(_PathTrie, "PathTrie");
  316. var PathTrie = _PathTrie;
  317. // Annotate the CommonJS export names for ESM import in node:
  318. 0 && (module.exports = {
  319. PathTrie
  320. });