page-node.ts 2.5 KB

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