From f73bbe9bff3df3bb9e029a3d993c3af6a2e1190a Mon Sep 17 00:00:00 2001 From: fraxken Date: Sat, 16 Mar 2024 19:21:03 +0100 Subject: [PATCH 1/3] chore: use dependabot groups --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8c907bc..8e15c4b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,14 @@ updates: versioning-strategy: widen schedule: interval: "weekly" + groups: + development-dependencies: + dependency-type: "development" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" + groups: + github-actions: + patterns: + - "*" From 2490a5e2c4108a7d3f28ace4cf7b656d3141f1c0 Mon Sep 17 00:00:00 2001 From: fraxken Date: Sat, 16 Mar 2024 19:21:24 +0100 Subject: [PATCH 2/3] docs: use new github blockquotes & node.js namespace --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a674aaa..56ef500 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Modern FileSystem (fs) utilities to lazy walk directories Asynchronously (but al - Enforce usage of Symbols for CONSTANTS. - Synchronous API. +> [!NOTE] > Performance over some of the features is a non-goal. ## Requirements @@ -35,7 +36,7 @@ $ yarn add @nodesecure/fs-walk ## Usage example ```js -import path from "path"; +import path from "node:path"; import { walk } from "@nodesecure/fs-walk"; for await (const [dirent, absoluteFileLocation] of walk(".")) { From 68083ca2889f57bf7ce8b2442496a5f005adceaa Mon Sep 17 00:00:00 2001 From: fraxken Date: Sat, 16 Mar 2024 19:24:32 +0100 Subject: [PATCH 3/3] refactor: migrate codebase to TypeScript Update src/constants.ts Co-authored-by: PierreDemailly <39910767+PierreDemailly@users.noreply.github.com> Update test/walk.spec.ts Co-authored-by: PierreDemailly <39910767+PierreDemailly@users.noreply.github.com> --- README.md | 6 ++-- index.d.ts | 1 - index.js | 77 ----------------------------------------------- package.json | 30 ++++++++++-------- src/constants.ts | 5 +++ src/index.ts | 3 ++ src/types.ts | 14 +++++++++ src/walk.ts | 45 +++++++++++++++++++++++++++ src/walkSync.ts | 45 +++++++++++++++++++++++++++ test/walk.js | 54 --------------------------------- test/walk.spec.ts | 68 +++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 20 ++++++++++++ type/walk.d.ts | 11 ------- 13 files changed, 221 insertions(+), 158 deletions(-) delete mode 100644 index.d.ts delete mode 100644 index.js create mode 100644 src/constants.ts create mode 100644 src/index.ts create mode 100644 src/types.ts create mode 100644 src/walk.ts create mode 100644 src/walkSync.ts delete mode 100644 test/walk.js create mode 100644 test/walk.spec.ts create mode 100644 tsconfig.json delete mode 100644 type/walk.d.ts diff --git a/README.md b/README.md index 56ef500..8c2e3f8 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,14 @@ export interface WalkOptions { extensions?: Set; } -export type WalkResult = [dirent: fs.Dirent, absoluteFileLocation: string]; +export type WalkEntry = [dirent: fs.Dirent, absoluteFileLocation: string]; ``` -### walk(directory: string, options?: WalkOptions): AsyncIterableIterator< WalkResult > +### walk(directory: string, options?: WalkOptions): AsyncIterableIterator< WalkEntry > Asynchronous walk. -### walkSync(directory: string, options?: WalkOptions): IterableIterator< WalkResult > +### walkSync(directory: string, options?: WalkOptions): IterableIterator< WalkEntry > Synchronous walk (using readdirSync under the hood instead of opendir). diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index a6487de..0000000 --- a/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./type/walk"; diff --git a/index.js b/index.js deleted file mode 100644 index 949b7a0..0000000 --- a/index.js +++ /dev/null @@ -1,77 +0,0 @@ -// Import Node.js Dependencies -import { opendir } from "fs/promises"; -import { readdirSync } from "fs"; -import path from "path"; - -// CONSTANTS -const kExcludedDirectory = new Set(["node_modules", ".vscode", ".git"]); - -/** - * @example - * import { walk } from "@nodesecure/fs-walk"; - * - * for await (const [dirent, location] of walk(__dirname) { - * if (dirent.isFile()) { - * console.log(location); - * } - * } - */ -export async function* walk(directory, options = Object.create(null)) { - const extensions = options?.extensions ?? null; - const dirents = await opendir(directory); - - for await (const dirent of dirents) { - if (kExcludedDirectory.has(dirent.name)) { - continue; - } - - if (dirent.isFile()) { - if (extensions !== null && !extensions.has(path.extname(dirent.name))) { - continue; - } - - yield [dirent, path.join(directory, dirent.name)]; - } - else if (dirent.isDirectory()) { - const subDirectoryLocation = path.join(directory, dirent.name); - - yield [dirent, subDirectoryLocation]; - yield* walk(subDirectoryLocation, options); - } - } -} - -/** - * @example - * import { walkSync, FILE } from "@nodesecure/fs-walk"; - * - * for (const [type, location] of walkSync(__dirname) { - * if (type === FILE) { - * console.log(location); - * } - * } - */ -export function* walkSync(directory, options = Object.create(null)) { - const extensions = options?.extensions ?? null; - const dirents = readdirSync(directory, { withFileTypes: true }); - - for (const dirent of dirents) { - if (kExcludedDirectory.has(dirent.name)) { - continue; - } - - if (dirent.isFile()) { - if (extensions !== null && !extensions.has(path.extname(dirent.name))) { - continue; - } - - yield [dirent, path.join(directory, dirent.name)]; - } - else if (dirent.isDirectory()) { - const subDirectoryLocation = path.join(directory, dirent.name); - - yield [dirent, subDirectoryLocation]; - yield* walkSync(subDirectoryLocation, options); - } - } -} diff --git a/package.json b/package.json index c8599c0..d991601 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,17 @@ "name": "@nodesecure/fs-walk", "version": "1.0.0", "description": "Modern FileSystem (fs) utilities to lazy walk directories Asynchronously (but also Synchronously)", - "exports": "./index.js", + "exports": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "engines": { + "node": ">=18.0.0" + }, "scripts": { - "lint": "eslint index.js", - "test-only": "node --test", + "build": "tsc", + "prepublishOnly": "npm run build", + "lint": "eslint src/**/*.ts test/**/*.ts", + "test-only": "glob -c \"tsx --test\" \"./test/**/*.spec.ts\"", "test": "npm run lint && npm run test-only", "coverage": "c8 -r html npm test" }, @@ -24,9 +31,7 @@ ], "author": "GENTILHOMME Thomas ", "files": [ - "index.d.ts", - "index.js", - "type" + "dist" ], "license": "MIT", "bugs": { @@ -34,11 +39,12 @@ }, "homepage": "https://github.com/NodeSecure/fs-walk#readme", "devDependencies": { - "@nodesecure/eslint-config": "^1.7.0", - "c8": "^8.0.0" - }, - "type": "module", - "engines": { - "node": ">=18.0.0" + "@nodesecure/eslint-config": "^1.9.0", + "@types/node": "^20.11.28", + "c8": "^8.0.1", + "eslint": "^8.57.0", + "glob": "^10.3.10", + "tsx": "^4.7.1", + "typescript": "^5.4.2" } } diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..7bf05c0 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,5 @@ +export const EXCLUDED_DIRECTORY = new Set([ + "node_modules", + ".vscode", + ".git" +]); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..84b6e1c --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from "./walk.js"; +export * from "./walkSync.js"; +export * from "./types.js"; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..d3ee40e --- /dev/null +++ b/src/types.ts @@ -0,0 +1,14 @@ +// Import Node.js Dependencies +import { Dirent } from "node:fs"; + +export interface WalkOptions { + /** + * Whitelist of extensions + * + * @example + * new Set([".js", ".cjs", ".mjs"]); + */ + extensions?: Set; +} + +export type WalkEntry = [dirent: Dirent, absoluteFileLocation: string]; diff --git a/src/walk.ts b/src/walk.ts new file mode 100644 index 0000000..a8ac0bb --- /dev/null +++ b/src/walk.ts @@ -0,0 +1,45 @@ +// Import Node.js Dependencies +import * as fs from "node:fs/promises"; +import * as path from "node:path"; + +// Import Internal Dependencies +import { EXCLUDED_DIRECTORY } from "./constants.js"; +import type { WalkOptions, WalkEntry } from "./types.js"; + +/** + * @example + * import { walk } from "@nodesecure/fs-walk"; + * + * for await (const [dirent, location] of walk(__dirname) { + * if (dirent.isFile()) { + * console.log(location); + * } + * } + */ +export async function* walk( + directory: string, + options: WalkOptions = Object.create(null) +): AsyncIterableIterator { + const extensions = options?.extensions ?? null; + const dirents = await fs.opendir(directory); + + for await (const dirent of dirents) { + if (EXCLUDED_DIRECTORY.has(dirent.name)) { + continue; + } + + if (dirent.isFile()) { + if (extensions !== null && !extensions.has(path.extname(dirent.name))) { + continue; + } + + yield [dirent, path.join(directory, dirent.name)]; + } + else if (dirent.isDirectory()) { + const subDirectoryLocation = path.join(directory, dirent.name); + + yield [dirent, subDirectoryLocation]; + yield* walk(subDirectoryLocation, options); + } + } +} diff --git a/src/walkSync.ts b/src/walkSync.ts new file mode 100644 index 0000000..03cd237 --- /dev/null +++ b/src/walkSync.ts @@ -0,0 +1,45 @@ +// Import Node.js Dependencies +import * as fs from "node:fs"; +import * as path from "node:path"; + +// Import Internal Dependencies +import { EXCLUDED_DIRECTORY } from "./constants.js"; +import type { WalkOptions, WalkEntry } from "./types.js"; + +/** + * @example + * import { walkSync, FILE } from "@nodesecure/fs-walk"; + * + * for (const [type, location] of walkSync(__dirname) { + * if (type === FILE) { + * console.log(location); + * } + * } + */ +export function* walkSync( + directory: string, + options: WalkOptions = Object.create(null) +): IterableIterator { + const extensions = options?.extensions ?? null; + const dirents = fs.readdirSync(directory, { withFileTypes: true }); + + for (const dirent of dirents) { + if (EXCLUDED_DIRECTORY.has(dirent.name)) { + continue; + } + + if (dirent.isFile()) { + if (extensions !== null && !extensions.has(path.extname(dirent.name))) { + continue; + } + + yield [dirent, path.join(directory, dirent.name)]; + } + else if (dirent.isDirectory()) { + const subDirectoryLocation = path.join(directory, dirent.name); + + yield [dirent, subDirectoryLocation]; + yield* walkSync(subDirectoryLocation, options); + } + } +} diff --git a/test/walk.js b/test/walk.js deleted file mode 100644 index 41b398a..0000000 --- a/test/walk.js +++ /dev/null @@ -1,54 +0,0 @@ -// Import Node.js Dependencies -import test from "node:test"; -import assert from "node:assert"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -// Import Internal Dependencies -import { walk, walkSync } from "../index.js"; - -// CONSTANTS -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -const kRootLocation = path.join(__dirname, ".."); -const kFixturesDir = path.join(__dirname, "fixtures"); -const kExpectedJSFiles = ["index.js", "test/walk.js"].map((fileLocation) => path.normalize(fileLocation)); - -test("should return all JavaScript files of the project (Asynchronously)", async() => { - const files = []; - const options = { extensions: new Set([".js"]) }; - - for await (const [dirent, absoluteFileLocation] of walk( - kRootLocation, - options - )) { - if (dirent.isFile()) { - files.push(path.relative(kRootLocation, absoluteFileLocation)); - } - } - - assert.deepEqual(files.sort(), kExpectedJSFiles); -}); - -test("should return all JavaScript files of the project (Synchronously)", async() => { - const options = { extensions: new Set([".js"]) }; - - const files = [...walkSync(kRootLocation, options)] - .filter(([dirent]) => dirent.isFile()) - .map(([, absoluteFileLocation]) => path.relative(kRootLocation, absoluteFileLocation)); - - assert.deepEqual(files, kExpectedJSFiles); -}); - -test("should return all files in the fixtures directory", async() => { - const files = [...walkSync(kFixturesDir)] - .filter(([dirent]) => dirent.isFile()) - .map(([, absoluteFileLocation]) => path.relative(kRootLocation, absoluteFileLocation)); - - const expectedFiles = [ - "test/fixtures/foobar.txt", - "test/fixtures/test.md" - ].map((fileLocation) => path.normalize(fileLocation)); - - assert.deepEqual(files, expectedFiles); -}); diff --git a/test/walk.spec.ts b/test/walk.spec.ts new file mode 100644 index 0000000..5161154 --- /dev/null +++ b/test/walk.spec.ts @@ -0,0 +1,68 @@ +// Import Node.js Dependencies +import { describe, it } from "node:test"; +import assert from "node:assert"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// Import Internal Dependencies +import { walk, walkSync } from "../src/index.js"; + +// CONSTANTS +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const kRootLocation = path.join(__dirname, ".."); +const kFixturesDir = path.join(__dirname, "fixtures"); + +const kExpectedJSFiles = [ + "src/index.ts", + "src/constants.ts", + "src/types.ts", + "src/walk.ts", + "src/walkSync.ts", + "test/walk.spec.ts" +] + .map((fileLocation) => path.normalize(fileLocation)) + .sort(); + +describe("walk", () => { + it("should return all TypeScript files of the package", async() => { + const files: string[] = []; + const options = { extensions: new Set([".ts"]) }; + + for await (const [dirent, absoluteFileLocation] of walk( + kRootLocation, + options + )) { + if (dirent.isFile()) { + files.push(path.relative(kRootLocation, absoluteFileLocation)); + } + } + + assert.deepEqual(files.sort(), kExpectedJSFiles); + }); +}); + +describe("walkSync", () => { + it("should return all TypeScript files of the package", () => { + const options = { extensions: new Set([".ts"]) }; + + const files = [...walkSync(kRootLocation, options)] + .filter(([dirent]) => dirent.isFile()) + .map(([, absoluteFileLocation]) => path.relative(kRootLocation, absoluteFileLocation)); + + assert.deepEqual(files, kExpectedJSFiles); + }); + + it("should return all files in the fixtures directory", () => { + const files = [...walkSync(kFixturesDir)] + .filter(([dirent]) => dirent.isFile()) + .map(([, absoluteFileLocation]) => path.relative(kRootLocation, absoluteFileLocation)); + + const expectedFiles = [ + "test/fixtures/foobar.txt", + "test/fixtures/test.md" + ].map((fileLocation) => path.normalize(fileLocation)); + + assert.deepEqual(files, expectedFiles); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..249ef33 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "declaration": true, + "strictNullChecks": true, + "noImplicitThis": true, + "noEmitOnError": true, + "noImplicitAny": true, + "target": "ES2022", + "outDir": "dist", + "module": "ES2022", + "moduleResolution": "node", + "skipDefaultLibCheck": true, + "forceConsistentCasingInFileNames": true, + "sourceMap": true, + "rootDir": "./src", + "types": ["node"], + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/type/walk.d.ts b/type/walk.d.ts deleted file mode 100644 index e289a50..0000000 --- a/type/walk.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Dirent } from "fs"; - -export interface WalkOptions { - extensions?: Set; -} - -export type WalkResult = [dirent: Dirent, absoluteFileLocation: string]; - -export declare function walk(directory: string, options?: WalkOptions): AsyncIterableIterator; -export declare function walkSync(directory: string, options?: WalkOptions): IterableIterator; -export {};