@neodx/vfs
Version:
Simple virtual file system - working dir context, lazy changes, different modes, integrations and moreover
1 lines • 6.24 kB
Source Map (JSON)
{"version":3,"file":"scan.cjs","sources":["../../../src/plugins/scan.ts"],"sourcesContent":["import {\n combineAbortSignals,\n compact,\n concurrently,\n False,\n isDefined,\n isTypeOfString,\n some,\n True,\n tryCreateTimeoutSignal\n} from '@neodx/std';\nimport { join } from 'pathe';\nimport type { VfsDirent } from '../backend/shared.ts';\nimport type { BaseVfs } from '../core/types.ts';\nimport { createVfsPlugin } from '../create-vfs-plugin.ts';\n\nexport interface ScanPluginApi {\n scan(path?: string, params?: ScanVfsParams): Promise<string[]>;\n scan(params?: ScanVfsParams): Promise<string[]>;\n scan(path: string, params: ScanVfsParams & { withFileTypes: true }): Promise<ScannedItem[]>;\n scan(params: ScanVfsParams & { withFileTypes: true }): Promise<ScannedItem[]>;\n}\n\nexport interface ScanVfsParams {\n /** Path to scan. */\n path?: string;\n /**\n * Should return false if the scanning for the current directory should be stopped.\n * @default False\n */\n barrier?: ScanVfsDirentChecker;\n /**\n * Should return true if the item should be included in the result.\n * @default True\n */\n filter?: ScanVfsDirentChecker;\n /** Custom abort signal. */\n signal?: AbortSignal;\n /** Timeout in milliseconds. */\n timeout?: number;\n /** Maximum depth to scan. */\n maxDepth?: number;\n\n /** If true, the result will contain information about the path, depth, relativePath and dirent. */\n withFileTypes?: boolean;\n /** Optional cache for optimizing multiple scans under relatively static conditions. */\n cache?: ScanVfsCache;\n}\n\nexport interface ScannedItem {\n /** Current scanning depth. */\n depth: number;\n /** Relative path to file or directory */\n relativePath: string;\n dirent: VfsDirent;\n}\n\nexport type ScanVfsDirentChecker = (item: ScannedItem) => boolean;\nexport type ScanVfsCache = ReturnType<typeof createScanVfsCache>;\n\nexport function scan() {\n return createVfsPlugin<ScanPluginApi>('scan', vfs => {\n async function scanImpl(pathOrParams?: string | ScanVfsParams, params?: ScanVfsParams) {\n return await scanVfs(\n vfs,\n isTypeOfString(pathOrParams) ? { ...params, path: pathOrParams } : pathOrParams\n );\n }\n\n vfs.scan = scanImpl as ScanPluginApi['scan'];\n return vfs;\n });\n}\n\nexport async function scanVfs(\n vfs: BaseVfs,\n params: ScanVfsParams & { withFileTypes: true }\n): Promise<ScannedItem[]>;\nexport async function scanVfs(vfs: BaseVfs, params?: ScanVfsParams): Promise<string[]>;\nexport async function scanVfs(\n vfs: BaseVfs,\n {\n path = '.',\n cache = createScanVfsCache(),\n filter = True,\n signal: manualSignal,\n barrier: manualBarrier = False,\n timeout,\n maxDepth,\n withFileTypes\n }: ScanVfsParams = {}\n) {\n const signal = combineAbortSignals([manualSignal, tryCreateTimeoutSignal(timeout)]);\n const barrier = some(\n ...compact([\n manualBarrier,\n isDefined(maxDepth) && (({ depth }: ScannedItem) => depth >= maxDepth)\n ])\n );\n const result: ScannedItem[] = [];\n\n async function iterate(params: Pick<ScannedItem, 'relativePath' | 'depth'>) {\n signal.throwIfAborted();\n Object.freeze(params);\n const resolved = vfs.resolve(path, params.relativePath);\n const children = await (cache.visited[resolved] ??= vfs.readDir(resolved, {\n withFileTypes: true\n }));\n\n await concurrently(\n children,\n async dirent => {\n signal.throwIfAborted();\n const scanned = {\n relativePath: join(params.relativePath, dirent.name),\n dirent,\n depth: params.depth + 1\n };\n\n if (filter(scanned)) result.push(scanned);\n if (dirent.isDirectory() && !barrier(scanned)) await iterate(scanned);\n },\n 10\n );\n }\n\n await iterate({ relativePath: '.', depth: 0 });\n return withFileTypes ? result : result.map(item => item.relativePath);\n}\n\nexport const createScanVfsCache = () => {\n const visited = {} as Record<string, Promise<VfsDirent[]>>;\n\n return {\n visited,\n clear() {\n for (const key of Object.keys(visited)) {\n delete visited[key];\n }\n }\n };\n};\n"],"names":["scanVfs","vfs","path","cache","createScanVfsCache","filter","True","signal","manualSignal","barrier","manualBarrier","False","timeout","maxDepth","withFileTypes","combineAbortSignals","tryCreateTimeoutSignal","some","compact","isDefined","depth","result","iterate","params","throwIfAborted","Object","freeze","resolved","resolve","relativePath","children","visited","readDir","concurrently","dirent","scanned","join","name","push","isDirectory","map","item","clear","key","keys","createVfsPlugin","scanImpl","pathOrParams","isTypeOfString","scan"],"mappings":"wGA+EO,eAAeA,EACpBC,CAAY,CACZ,CACEC,KAAAA,EAAO,GAAG,CACVC,MAAAA,EAAQC,GAAoB,CAC5BC,OAAAA,EAASC,EAAIA,IAAA,CACbC,OAAQC,CAAY,CACpBC,QAASC,EAAgBC,EAAKA,KAAA,CAC9BC,QAAAA,CAAO,CACPC,SAAAA,CAAQ,CACRC,cAAAA,CAAa,CACC,CAAG,EAAE,EAErB,IAAMP,EAASQ,EAAAA,mBAAoB,CAAA,CAACP,EAAcQ,EAAAA,sBAAuBJ,CAAAA,GAAS,EAC5EH,EAAUQ,UACXC,SAAQ,CAAA,CACTR,EACAS,EAAUN,SAAAA,CAAAA,IAAc,CAAA,CAAC,CAAEO,MAAAA,CAAK,CAAe,GAAKA,GAASP,GAC9D,GAEGQ,EAAwB,EAAE,CAEhC,eAAeC,EAAQC,CAAmD,EACxEhB,EAAOiB,cAAc,GACrBC,OAAOC,MAAM,CAACH,GACd,IAAMI,EAAW1B,EAAI2B,OAAO,CAAC1B,EAAMqB,EAAOM,YAAY,EAChDC,EAAW,MAAO3B,CAAAA,EAAM4B,OAAO,CAACJ,EAAS,GAAK1B,EAAI+B,OAAO,CAACL,EAAU,CACxEb,cAAe,CAAA,CAChB,EAAA,CAED,OAAMmB,EAAAA,YAAAA,CACJH,EACA,MAAMI,IACJ3B,EAAOiB,cAAc,GACrB,IAAMW,EAAU,CACdN,aAAcO,EAAAA,IAAKb,CAAAA,EAAOM,YAAY,CAAEK,EAAOG,IAAI,EACnDH,OAAAA,EACAd,MAAOG,EAAOH,KAAK,CAAG,CACxB,EAEIf,EAAO8B,IAAUd,EAAOiB,IAAI,CAACH,GAC7BD,EAAOK,WAAW,IAAM,CAAC9B,EAAQ0B,IAAU,MAAMb,EAAQa,EAE/D,EAAA,GAEJ,CAGA,OADA,MAAMb,EAAQ,CAAEO,aAAc,IAAKT,MAAO,CAAE,GACrCN,EAAgBO,EAASA,EAAOmB,GAAG,CAACC,AAAAA,GAAQA,EAAKZ,YAAY,CACtE,OAEazB,EAAqB,KAChC,IAAM2B,EAAU,CAAA,EAEhB,MAAO,CACLA,QAAAA,EACAW,QACE,IAAK,IAAMC,KAAOlB,OAAOmB,IAAI,CAACb,GAC5B,OAAOA,CAAO,CAACY,EAAI,AAEvB,CACF,CACF,4CAjFO,WACL,OAAOE,EAAAA,eAAAA,CAA+B,OAAQ5C,AAAAA,IAC5C,eAAe6C,EAASC,CAAqC,CAAExB,CAAsB,EACnF,OAAO,MAAMvB,EACXC,EACA+C,EAAAA,cAAAA,CAAeD,GAAgB,CAAE,GAAGxB,CAAM,CAAErB,KAAM6C,CAAiBA,EAAAA,EAEvE,CAGA,OADA9C,EAAIgD,IAAI,CAAGH,EACJ7C,CACT,EACF"}