From 688e83fc70900e78481a80f680ed77376d9828b1 Mon Sep 17 00:00:00 2001 From: fraxken Date: Sat, 16 Mar 2024 19:24:32 +0100 Subject: [PATCH] refactor: migrate codebase to TypeScript --- README.md | 6 ++-- index.d.ts | 1 - index.js | 77 ----------------------------------------------- package.json | 30 ++++++++++-------- src/constants.ts | 6 ++++ src/index.ts | 3 ++ src/types.ts | 14 +++++++++ src/walk.ts | 45 +++++++++++++++++++++++++++ src/walkSync.ts | 45 +++++++++++++++++++++++++++ test/walk.js | 54 --------------------------------- test/walk.spec.ts | 70 ++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 20 ++++++++++++ type/walk.d.ts | 11 ------- 13 files changed, 224 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..742af18 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,6 @@ + +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..61fea87 --- /dev/null +++ b/test/walk.spec.ts @@ -0,0 +1,70 @@ +// 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 {};