{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/utils.ts","../src/builder.ts","../src/fn.ts"],"sourcesContent":["export { PathTreeBuilder } from './builder'\nexport { DEFAULT_SEP, ENTRY_NAME, ROOT_NAME } from './constants'\nexport { pathToTree } from './fn'\nexport type { NodeItem, ParseResults, TreeNode } from './types'\n\nexport { getFileNameAndExt, walkPathTree } from './utils'\n","export const ENTRY_NAME = 'index'\nexport const DEFAULT_SEP = '/'\nexport const ROOT_NAME = 'root'\n","import type { NodeItem, ParseResults, TreeNode } from './types'\nimport { DEFAULT_SEP, ENTRY_NAME, ROOT_NAME } from './constants'\n\nconst REGEX_HAVE_PATHS = /.+\\/(.+)\\.(.+)$/\nconst REGEX_NO_PATHS = /(.+)\\.(.+)$/\n\nexport function getFileNameAndExt(path: string, sep = DEFAULT_SEP) {\n  const result = path.includes(sep)\n    ? REGEX_HAVE_PATHS.exec(path)?.slice(1)\n    : REGEX_NO_PATHS.exec(path)?.slice(1)\n\n  // Handle files without extensions (like .gitignore, .env, etc.)\n  if (!result) {\n    const filename = path.includes(sep) ? path.split(sep).pop()! : path\n    return [filename, '']\n  }\n\n  return result\n}\n\nexport function cleanPath(path: string, sep: string) {\n  if (path.startsWith(sep))\n    path = path.slice(1)\n  if (path.endsWith(sep))\n    path = path.slice(0, -1)\n  return path\n}\n\nexport function parsePath<Data>(\n  originalPath: string,\n  sep: string,\n  currentNode: TreeNode<Data>,\n  getData?: (node: NodeItem<Data>) => Data,\n): ParseResults<Data> {\n  const isDirectory = originalPath.endsWith(sep)\n  const path = cleanPath(originalPath, sep)\n  const parts = path.split(sep)\n  const partsLen = parts.length\n\n  let nodeItem: NodeItem<Data> | null = null\n\n  for (let i = 0; i < partsLen; i++) {\n    const part = parts[i]\n    if (i === partsLen - 1 && !isDirectory) { // this is the last part of the path, so it's a file\n      const [filename, ext] = getFileNameAndExt(part, sep)!\n      nodeItem = {\n        path: originalPath,\n        filename,\n        ext,\n        isEntry: filename === ENTRY_NAME,\n        parent: currentNode,\n        file: part,\n      }\n      nodeItem.data = getData?.(nodeItem)\n      currentNode.items.push(nodeItem)\n    }\n    else { // this part is a directory\n      if (!currentNode.subDirectory)\n        currentNode.subDirectory = {}\n      const fixedPart = part === '' ? sep : part\n      if (fixedPart === sep)\n        continue\n      if (!currentNode.subDirectory[fixedPart]) {\n        currentNode.subDirectory[fixedPart] = {\n          name: fixedPart,\n          items: [],\n          subDirectory: {},\n          parent: currentNode,\n          path: `${currentNode.path}${fixedPart}${sep}`,\n          relativePath: `${fixedPart}${sep}`,\n          relativePathName: `${fixedPart}`,\n        }\n      }\n      currentNode = currentNode.subDirectory[fixedPart]\n    }\n  }\n\n  return nodeItem\n    ? {\n        type: 'file',\n        node: nodeItem,\n      }\n    : {\n        type: 'directory',\n        node: currentNode,\n      }\n}\n\nexport function genRoot<T = unknown>(): TreeNode<T> {\n  return {\n    items: [],\n    subDirectory: {},\n    name: ROOT_NAME,\n    path: '/',\n    relativePath: '/',\n    relativePathName: '',\n  }\n}\n\nexport function walkPathTree<Data>(node: TreeNode<Data>, callback: (node: TreeNode<Data>) => void) {\n  callback(node)\n  if (node.subDirectory) {\n    for (const childNode of Object.values(node.subDirectory))\n      walkPathTree(childNode, callback)\n  }\n}\n","import type { NodeItem, ParseResults, TreeNode } from './types'\nimport { DEFAULT_SEP, ROOT_NAME } from './constants'\nimport { genRoot, parsePath } from './utils'\n\nexport class PathTreeBuilder<T extends Record<string, unknown>> {\n  #root: TreeNode<T> = genRoot()\n  #mapping = new Map<string, ParseResults<T>>()\n\n  #sep\n  #getData\n  #autoResolveDir\n\n  /**\n   * @param options\n   * @param options.sep - default `/`\n   * @param options.getData - function to get data from node\n   * @param options.autoResolveDir - will auto resolve directory when adding file path, but not added dir path, default `true`\n   */\n  constructor(options?: {\n    sep?: string\n    getData?: (node: NodeItem<T>) => T\n    autoResolveDir?: boolean\n  }) {\n    const { sep = DEFAULT_SEP, getData, autoResolveDir = true } = options ?? {}\n\n    this.#sep = sep\n    this.#getData = getData\n    this.#autoResolveDir = autoResolveDir\n  }\n\n  /**\n   *\n   * @param path\n   * @param userData pass this will override default getData passed in constructor\n   */\n  addPath(path: string, userData?: T) {\n    const nodeItem = parsePath(\n      path,\n      this.#sep,\n      this.#root,\n      userData\n        ? () => userData\n        : this.#getData,\n    )\n    this.#mapping.set(nodeItem.node.path, nodeItem)\n    if (this.#autoResolveDir)\n      this.#resolveDir(nodeItem)\n  }\n\n  #resolveDir(node: ParseResults<T>) {\n    let dirNode: ParseResults<T>['node'] | undefined = node.node\n    while (dirNode.parent) {\n      dirNode = dirNode?.parent\n      if (!this.#mapping.has(dirNode.path)) {\n        this.#mapping.set(dirNode.path, {\n          node: dirNode,\n          type: 'directory',\n        })\n      }\n    }\n  }\n\n  removePath(path: string) {\n    const nodeItem = this.#mapping.get(path)\n    if (!nodeItem)\n      return\n    const { node, type } = nodeItem\n    if (type === 'file')\n      node.parent?.items.splice(node.parent.items.indexOf(node), 1)\n    else\n      Reflect.deleteProperty(node.parent!.subDirectory!, node.name)\n  }\n\n  /**\n   *\n   * @param path path to get node\n   * @param _isFile pass isFile for better type inference\n   */\n  getNode<const IF extends boolean>(path: string, _isFile: IF = true as IF) {\n    const node = this.#mapping.get(path)\n    if (!node)\n      return null\n    return node.node as IF extends true ? NodeItem<T> : TreeNode<T>\n  }\n\n  getItems(path: string) {\n    if (path === ROOT_NAME)\n      return this.#root.items\n    const node = this.#mapping.get(getStrictPath(path))\n    if (!node)\n      return []\n    return node.type === 'file'\n      ? []\n      : node.node.items\n  }\n\n  getSubDirectories(path: string) {\n    if (path === ROOT_NAME)\n      return this.#root.subDirectory\n    const node = this.getNode(getStrictPath(path), false)\n    if (!node)\n      return null\n    return node.subDirectory\n  }\n\n  get tree() {\n    return this.#root\n  }\n}\n\nfunction getStrictPath(path: string) {\n  return path.startsWith('/') ? path : `/${path}`\n}\n","import type { NodeItem, TreeNode } from './types'\nimport { DEFAULT_SEP } from './constants'\nimport { genRoot, parsePath } from './utils'\n\nexport function pathToTree<Data>(paths: string[], {\n  sep = DEFAULT_SEP,\n  getData,\n}: {\n  sep?: string\n  getData?: (node: NodeItem<Data>) => Data\n} = {}): TreeNode<Data> {\n  const root: TreeNode<Data> = genRoot()\n\n  let pathIndex = 0\n  const pathLen = paths.length\n\n  while (pathIndex < pathLen) {\n    const originalPath = paths[pathIndex]\n    pathIndex += 1\n    const currentNode = root\n    parsePath(originalPath, sep, currentNode, getData)\n  }\n\n  return root\n}\n"],"mappings":"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","names":[]}