UNPKG

@neodx/vfs

Version:

Simple virtual file system - working dir context, lazy changes, different modes, integrations and moreover

1 lines 6.14 kB
{"version":3,"file":"scan.mjs","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":["scan","createVfsPlugin","vfs","scanImpl","pathOrParams","params","scanVfs","isTypeOfString","path","cache","createScanVfsCache","filter","True","signal","manualSignal","barrier","manualBarrier","False","timeout","maxDepth","withFileTypes","combineAbortSignals","tryCreateTimeoutSignal","some","compact","isDefined","depth","result","iterate","throwIfAborted","Object","freeze","resolved","resolve","relativePath","children","visited","readDir","concurrently","dirent","scanned","join","name","push","isDirectory","map","item","clear","key","keys"],"mappings":"2QA4DO,SAASA,IACd,OAAOC,EAA+B,OAAQC,AAAAA,IAC5C,eAAeC,EAASC,CAAqC,CAAEC,CAAsB,EACnF,OAAO,MAAMC,EACXJ,EACAK,EAAeH,GAAgB,CAAE,GAAGC,CAAM,CAAEG,KAAMJ,CAAiBA,EAAAA,EAEvE,CAGA,OADAF,EAAIF,IAAI,CAAGG,EACJD,CACT,EACF,CAOO,eAAeI,EACpBJ,CAAY,CACZ,CACEM,KAAAA,EAAO,GAAG,CACVC,MAAAA,EAAQC,GAAoB,CAC5BC,OAAAA,EAASC,CAAI,CACbC,OAAQC,CAAY,CACpBC,QAASC,EAAgBC,CAAK,CAC9BC,QAAAA,CAAO,CACPC,SAAAA,CAAQ,CACRC,cAAAA,CAAa,CACC,CAAG,EAAE,EAErB,IAAMP,EAASQ,EAAoB,CAACP,EAAcQ,EAAuBJ,GAAS,EAC5EH,EAAUQ,KACXC,EAAQ,CACTR,EACAS,EAAUN,IAAc,CAAA,CAAC,CAAEO,MAAAA,CAAK,CAAe,GAAKA,GAASP,GAC9D,GAEGQ,EAAwB,EAAE,CAEhC,eAAeC,EAAQvB,CAAmD,EACxEQ,EAAOgB,cAAc,GACrBC,OAAOC,MAAM,CAAC1B,GACd,IAAM2B,EAAW9B,EAAI+B,OAAO,CAACzB,EAAMH,EAAO6B,YAAY,EAChDC,EAAW,MAAO1B,CAAAA,EAAM2B,OAAO,CAACJ,EAAS,GAAK9B,EAAImC,OAAO,CAACL,EAAU,CACxEZ,cAAe,CAAA,CAChB,EAAA,CAED,OAAMkB,EACJH,EACA,MAAMI,IACJ1B,EAAOgB,cAAc,GACrB,IAAMW,EAAU,CACdN,aAAcO,EAAKpC,EAAO6B,YAAY,CAAEK,EAAOG,IAAI,EACnDH,OAAAA,EACAb,MAAOrB,EAAOqB,KAAK,CAAG,CACxB,EAEIf,EAAO6B,IAAUb,EAAOgB,IAAI,CAACH,GAC7BD,EAAOK,WAAW,IAAM,CAAC7B,EAAQyB,IAAU,MAAMZ,EAAQY,EAE/D,EAAA,GAEJ,CAGA,OADA,MAAMZ,EAAQ,CAAEM,aAAc,IAAKR,MAAO,CAAE,GACrCN,EAAgBO,EAASA,EAAOkB,GAAG,CAACC,AAAAA,GAAQA,EAAKZ,YAAY,CACtE,KAEaxB,EAAqB,KAChC,IAAM0B,EAAU,CAAA,EAEhB,MAAO,CACLA,QAAAA,EACAW,QACE,IAAK,IAAMC,KAAOlB,OAAOmB,IAAI,CAACb,GAC5B,OAAOA,CAAO,CAACY,EAAI,AAEvB,CACF,CACF"}