From 7238f906f5da9bfde30690839566581dd48da078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Minelli?= <michael@minelli.me> Date: Wed, 9 Aug 2023 12:32:06 +0200 Subject: [PATCH] Add RecursiveFilesStats --- helpers/recursiveFilesStats/README.md | 148 ++++++++++++++++++ .../RecursiveFilesStats.ts | 146 +++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 helpers/recursiveFilesStats/README.md create mode 100644 helpers/recursiveFilesStats/RecursiveFilesStats.ts diff --git a/helpers/recursiveFilesStats/README.md b/helpers/recursiveFilesStats/README.md new file mode 100644 index 0000000..f2e98d4 --- /dev/null +++ b/helpers/recursiveFilesStats/README.md @@ -0,0 +1,148 @@ +Source: recursive-readdir-files +=== +Modified for Dojo +=== + +## Usage + +```js +import recursiveReaddirFiles from 'recursive-readdir-files'; + + +const files = await recursiveReaddirFiles(process.cwd(), { + ignored: /\/(node_modules|\.git)/ +}); + +// `files` is an array +console.log(files); +// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ +// [ +// { +// dev: 16777233, +// mode: 33188, +// nlink: 1, +// uid: 501, +// gid: 20, +// rdev: 0, +// blksize: 4096, +// ino: 145023089, +// size: 89, +// blocks: 8, +// atimeMs: 1649303678077.934, +// mtimeMs: 1649303676847.1777, +// ctimeMs: 1649303676847.1777, +// birthtimeMs: 1649301118132.6782, +// atime: 2022-04-07T03:54:38.078Z, +// mtime: 2022-04-07T03:54:36.847Z, +// ctime: 2022-04-07T03:54:36.847Z, +// birthtime: 2022-04-07T03:11:58.133Z, +// name: 'watch.ts', +// path: '/Users/xxx/watch.ts', +// ext: 'ts' +// }, +// // ... +// ] +``` + +Or + +```js +recursiveReaddirFiles(process.cwd(), { + ignored: /\/(node_modules|\.git)/ +}, (filepath, state) => { + console.log(filepath); + // 👉 /Users/xxx/watch.ts + console.log(state.isFile()); // 👉 true + console.log(state.isDirectory()); // 👉 false + console.log(state); + // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ + // { + // dev: 16777233, + // mode: 33188, + // nlink: 1, + // uid: 501, + // gid: 20, + // rdev: 0, + // blksize: 4096, + // ino: 145023089, + // size: 89, + // blocks: 8, + // atimeMs: 1649303678077.934, + // mtimeMs: 1649303676847.1777, + // ctimeMs: 1649303676847.1777, + // birthtimeMs: 1649301118132.6782, + // atime: 2022-04-07T03:54:38.078Z, + // mtime: 2022-04-07T03:54:36.847Z, + // ctime: 2022-04-07T03:54:36.847Z, + // birthtime: 2022-04-07T03:11:58.133Z, + // name: 'watch.ts', + // path: '/Users/xxx/watch.ts', + // ext: 'ts' + // } +}) +``` + +## Options + +```ts +export interface RecursiveReaddirFilesOptions { + /** + * Ignore files + * @example `/\/(node_modules|\.git)/` + */ + ignored?: RegExp; + /** + * Specifies a list of `glob` patterns that match files to be included in compilation. + * @example `/(\.json)$/` + */ + include?: RegExp; + /** + * Specifies a list of files to be excluded from compilation. + * @example `/(package\.json)$/` + */ + exclude?: RegExp; + /** Provide filtering methods to filter data. */ + filter?: (item: IFileDirStat) => boolean; + /** Do not give the absolute path but the relative one from the root folder */ + replacePathByRelativeOne?: boolean; + /** Remove stats that are not necessary for transfert */ + liteStats?: boolean; +} +``` + +## Result + +```ts +import fs from 'node:fs'; + + +export interface IFileDirStat extends Partial<fs.Stats> { + /** + * @example `/a/sum.jpg` => `sum.jpg` + */ + name: string; + /** + * @example `/basic/src/utils/sum.ts` + */ + path: string; + /** + * @example `/a/b.jpg` => `jpg` + */ + ext?: string; +} + + +declare type Callback = (filepath: string, stat: IFileDirStat) => void; +export default function recursiveReaddirFiles(rootPath: string, options?: RecursiveReaddirFilesOptions, callback?: Callback): Promise<IFileDirStat[]>; +export { recursiveReaddirFiles }; +export declare const getStat: (filepath: string) => Promise<IFileDirStat>; +/** + * Get ext + * @param {String} filePath `/a/b.jpg` => `jpg` + */ +export declare const getExt: (filePath: string) => string; +``` + +## License + +Licensed under the MIT License. diff --git a/helpers/recursiveFilesStats/RecursiveFilesStats.ts b/helpers/recursiveFilesStats/RecursiveFilesStats.ts new file mode 100644 index 0000000..5a97fda --- /dev/null +++ b/helpers/recursiveFilesStats/RecursiveFilesStats.ts @@ -0,0 +1,146 @@ +import fs from 'node:fs'; +import path from 'node:path'; + + +export interface RecursiveReaddirFilesOptions { + /** + * Ignore files + * @example `/\/(node_modules|\.git)/` + */ + ignored?: RegExp; + /** + * Specifies a list of `glob` patterns that match files to be included in compilation. + * @example `/(\.json)$/` + */ + include?: RegExp; + /** + * Specifies a list of files to be excluded from compilation. + * @example `/(package\.json)$/` + */ + exclude?: RegExp; + /** Provide filtering methods to filter data. */ + filter?: (item: IFileDirStat) => boolean; + /** Do not give the absolute path but the relative one from the root folder */ + replacePathByRelativeOne?: boolean; + /** Remove stats that are not necessary for transfert */ + liteStats?: boolean; +} + + +export interface IFileDirStat extends Partial<fs.Stats> { + /** + * @example `/a/sum.jpg` => `sum.jpg` + */ + name: string; + /** + * @example `/basic/src/utils/sum.ts` + */ + path: string; + /** + * @example `/a/b.jpg` => `jpg` + */ + ext?: string; +} + + +type Callback = (filepath: string, stat: IFileDirStat) => void; + + +class RecursiveFilesStats { + async explore(rootPath: string, options: RecursiveReaddirFilesOptions = {}, callback?: Callback): Promise<IFileDirStat[]> { + return this.getFiles(`${ path.resolve(rootPath) }/`, rootPath, options, [], callback); + } + + private async getFiles(absoluteBasePath: string, rootPath: string, options: RecursiveReaddirFilesOptions = {}, files: IFileDirStat[] = [], callback?: Callback): Promise<IFileDirStat[]> { + const { + ignored, include, exclude, filter + } = options; + const filesData = await fs.promises.readdir(rootPath); + const fileDir: IFileDirStat[] = filesData.map((file) => ({ + name: file, path: path.join(rootPath, file) + })).filter((item) => { + if ( include && include.test(item.path) ) { + return true; + } + if ( exclude && exclude.test(item.path) ) { + return false; + } + if ( ignored ) { + return !ignored.test(item.path); + } + return true; + }); + if ( callback ) { + fileDir.map(async (item: IFileDirStat) => { + const stat = await this.getStat(item.path, absoluteBasePath, options); + if ( stat.isDirectory!() ) { + await this.getFiles(absoluteBasePath, item.path, options, [], callback); + } + callback(item.path, stat); + }); + } else { + await Promise.all(fileDir.map(async (item: IFileDirStat) => { + const stat = await this.getStat(item.path, absoluteBasePath, options); + if ( stat.isDirectory!() ) { + const arr = await this.getFiles(absoluteBasePath, item.path, options, []); + files = files.concat(arr); + } else if ( stat.isFile!() ) { + files.push(stat); + } + })); + } + return files.filter((item) => { + if ( filter && typeof filter === 'function' ) { + return filter(item); + } + return true; + }); + } + + private async getStat(filepath: string, absoluteRootPath: string, options: RecursiveReaddirFilesOptions): Promise<IFileDirStat> { + const stat = (await fs.promises.stat(filepath)) as IFileDirStat; + stat.ext = ''; + if ( stat.isFile!() ) { + stat.ext = this.getExt(filepath); + stat.name = path.basename(filepath); + stat.path = path.resolve(filepath); + } + + if ( options.replacePathByRelativeOne && stat.path ) { + stat.path = stat.path.replace(absoluteRootPath, ''); + } + + if ( options.liteStats ) { + delete stat.dev; + delete stat.nlink; + delete stat.uid; + delete stat.gid; + delete stat.rdev; + delete stat.blksize; + delete stat.ino; + delete stat.blocks; + delete stat.atimeMs; + delete stat.mtimeMs; + delete stat.ctimeMs; + delete stat.birthtimeMs; + delete stat.atime; + //delete stat.mtime; + delete stat.ctime; + //delete stat.birthtime; + //delete stat.mode; + } + + return stat; + }; + + /** + * Get ext + * @param {String} filePath `/a/b.jpg` => `jpg` + */ + private getExt(filePath: string): string { + return path.extname(filePath).replace(/^\./, '').toLowerCase(); + } +} + + +export default new RecursiveFilesStats(); -- GitLab