page-node.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import type { IPageHasId } from '@growi/core';
  2. import type { ParseRangeResult } from '@growi/core/dist/remark-plugins';
  3. import {
  4. getParentPath as getParentPathCore,
  5. removeTrailingSlash,
  6. } from '@growi/core/dist/utils/path-utils';
  7. import type { PageNode } from '../../interfaces/page-node';
  8. import { getDepthOfPath } from '../../utils/depth-utils';
  9. function getParentPath(path: string) {
  10. return removeTrailingSlash(decodeURIComponent(getParentPathCore(path)));
  11. }
  12. /**
  13. * generate PageNode instances for target page and the ancestors
  14. *
  15. * @param {any} pathToNodeMap
  16. * @param {any} rootPagePath
  17. * @param {any} pagePath
  18. * @returns
  19. * @memberof Lsx
  20. */
  21. function generatePageNode(
  22. pathToNodeMap: Record<string, PageNode>,
  23. rootPagePath: string,
  24. pagePath: string,
  25. depthRange?: ParseRangeResult | null,
  26. ): PageNode | null {
  27. // exclude rootPagePath itself
  28. if (pagePath === rootPagePath) {
  29. return null;
  30. }
  31. const depthStartToProcess =
  32. getDepthOfPath(rootPagePath) + (depthRange?.start ?? 0); // at least 1
  33. const currentPageDepth = getDepthOfPath(pagePath);
  34. // return by the depth restriction
  35. // '/' will also return null because the depth is 0
  36. if (currentPageDepth < depthStartToProcess) {
  37. return null;
  38. }
  39. // return when already registered
  40. if (pathToNodeMap[pagePath] != null) {
  41. return pathToNodeMap[pagePath];
  42. }
  43. // generate node
  44. const node = { pagePath, children: [] };
  45. pathToNodeMap[pagePath] = node;
  46. /*
  47. * process recursively for ancestors
  48. */
  49. // get or create parent node
  50. const parentPath = getParentPath(pagePath);
  51. const parentNode = generatePageNode(
  52. pathToNodeMap,
  53. rootPagePath,
  54. parentPath,
  55. depthRange,
  56. );
  57. // associate to patent
  58. if (parentNode != null) {
  59. parentNode.children.push(node);
  60. }
  61. return node;
  62. }
  63. export function generatePageNodeTree(
  64. rootPagePath: string,
  65. pages: IPageHasId[],
  66. depthRange?: ParseRangeResult | null,
  67. ): PageNode[] {
  68. const pathToNodeMap: Record<string, PageNode> = {};
  69. for (const page of pages) {
  70. const node = generatePageNode(
  71. pathToNodeMap,
  72. rootPagePath,
  73. page.path,
  74. depthRange,
  75. ); // this will not be null
  76. // exclude rootPagePath itself
  77. if (node == null) {
  78. continue;
  79. }
  80. // set the Page substance
  81. node.page = page;
  82. }
  83. // return root objects
  84. const rootNodes: PageNode[] = [];
  85. for (const pagePath in pathToNodeMap) {
  86. const parentPath = getParentPath(pagePath);
  87. // pick up what parent doesn't exist
  88. if (parentPath === '/' || !(parentPath in pathToNodeMap)) {
  89. rootNodes.push(pathToNodeMap[pagePath]);
  90. }
  91. }
  92. return rootNodes;
  93. }