From ebf0e7acdd3474a282e8c8e1b6a0cfcc006eed16 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:14:54 +0100 Subject: [PATCH] feat: update readdir/readdir-glob Both have new major versions so we need a bit more code to adopt them. Basically, the "found files" (i.e. those we traversed but haven't yet checked if we ignore) no longer come from `tiny-readdir` as a result. Instead, we have to listen to `Dirent` emit and build our own set. --- package-lock.json | 85 +++++++++++++++++++++++------------------------ package.json | 4 +-- src/utils.ts | 52 ++++++++++++++++++++++++++--- 3 files changed, 91 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index 742f692..33e8967 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,8 +27,8 @@ "smol-toml": "^1.7.0", "specialist": "^2.0.0", "tiny-editorconfig": "^1.0.2", - "tiny-readdir": "^2.7.4", - "tiny-readdir-glob": "^1.23.2", + "tiny-readdir": "^3.1.1", + "tiny-readdir-glob": "^2.0.0", "tiny-spinner": "^2.0.5", "worktank": "^3.0.2", "zeptomatch": "^2.1.0", @@ -4583,6 +4583,16 @@ "tiny-readdir": "^2.2.0" } }, + "node_modules/json-archive/node_modules/tiny-readdir": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-2.7.4.tgz", + "integrity": "sha512-721U+zsYwDirjr8IM6jqpesD/McpZooeFi3Zc6mcjy1pse2C+v19eHPFRqz4chGXZFw7C3KITDjAtHETc2wj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "promise-make-counter": "^1.0.2" + } + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -6381,23 +6391,22 @@ "integrity": "sha512-RnIDHQ+r9zMuslQWVoRxfKVOumteeheQqbwNYJyQxzM2vzx/vdN5xAeL64F3rQOpfbVdxFkhM4zPDyfq7SxsBQ==" }, "node_modules/tiny-readdir": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-2.7.4.tgz", - "integrity": "sha512-721U+zsYwDirjr8IM6jqpesD/McpZooeFi3Zc6mcjy1pse2C+v19eHPFRqz4chGXZFw7C3KITDjAtHETc2wj7Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-3.1.1.tgz", + "integrity": "sha512-SiNW3Kp5xshlA3McA2C/tjU7K8+VJc9ZNe09foB8ut8lMY7bR9NKPo5ARHDOj24mV69/utNy8gqDjF93FcFlFg==", + "license": "MIT", "dependencies": { "promise-make-counter": "^1.0.2" } }, "node_modules/tiny-readdir-glob": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/tiny-readdir-glob/-/tiny-readdir-glob-1.23.2.tgz", - "integrity": "sha512-+47FIdgzEtZj03mOyq9iAljlZZNleqSEwe3i6Uzkzec5axbMg32Vp78U2fLo4TiCMv9gzjnno7yJn34z5pXECw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tiny-readdir-glob/-/tiny-readdir-glob-2.0.0.tgz", + "integrity": "sha512-1zAGi76CspOE4MjMrsx2OeLpvjiHyprFTuJ1O3k5C1qU09GpltOL+zx5rw9CaVQ/qkpkmCfc09TS2fV52GJrrw==", + "license": "MIT", "dependencies": { - "tiny-readdir": "^2.7.0", - "zeptomatch": "^2.0.1", - "zeptomatch-explode": "^1.0.1", - "zeptomatch-is-static": "^1.0.1", - "zeptomatch-unescape": "^1.0.1" + "tiny-readdir": "^3.1.1", + "zeptomatch": "^2.1.0" } }, "node_modules/tiny-spinner": { @@ -7004,20 +7013,10 @@ "resolved": "https://registry.npmjs.org/zeptomatch-escape/-/zeptomatch-escape-1.0.1.tgz", "integrity": "sha512-kAc5HzvnF66djCYDqpsS46Y/FKi+4pe/KJRmTmm/hwmoaNYzmm6bBY07cdkxmJCdY018S5UeQn4yP+9X2x1MbQ==" }, - "node_modules/zeptomatch-explode": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/zeptomatch-explode/-/zeptomatch-explode-1.0.1.tgz", - "integrity": "sha512-7cUQASLLRGZ20+zEQcEgQ9z/gH1+jSfrNg4KfRJSxF1QU2fpymAwWvnAxl69GD5pr3IV0V9vo3ke2np//Nh4tQ==" - }, "node_modules/zeptomatch-is-static": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/zeptomatch-is-static/-/zeptomatch-is-static-1.0.1.tgz", "integrity": "sha512-bN9q7H/UdXhkub01WE7b7Grg07jLldNnIWG2T1IpBq5NtvcQ4DwFbNiGGapnbKHUdWiCNjg/bIvixV88nj9gog==" - }, - "node_modules/zeptomatch-unescape": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/zeptomatch-unescape/-/zeptomatch-unescape-1.0.1.tgz", - "integrity": "sha512-xhSFkKV0aQ03e/eiN4VhOTwJhcqfH7SMiGHrWKw9gXi+0EVJAxJ8Gt4ehozYsYLhUXL1JFbP1g3EE6ZmkStB0g==" } }, "dependencies": { @@ -10243,6 +10242,17 @@ "dev": true, "requires": { "tiny-readdir": "^2.2.0" + }, + "dependencies": { + "tiny-readdir": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-2.7.4.tgz", + "integrity": "sha512-721U+zsYwDirjr8IM6jqpesD/McpZooeFi3Zc6mcjy1pse2C+v19eHPFRqz4chGXZFw7C3KITDjAtHETc2wj7Q==", + "dev": true, + "requires": { + "promise-make-counter": "^1.0.2" + } + } } }, "json-parse-even-better-errors": { @@ -11569,23 +11579,20 @@ "integrity": "sha512-RnIDHQ+r9zMuslQWVoRxfKVOumteeheQqbwNYJyQxzM2vzx/vdN5xAeL64F3rQOpfbVdxFkhM4zPDyfq7SxsBQ==" }, "tiny-readdir": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-2.7.4.tgz", - "integrity": "sha512-721U+zsYwDirjr8IM6jqpesD/McpZooeFi3Zc6mcjy1pse2C+v19eHPFRqz4chGXZFw7C3KITDjAtHETc2wj7Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-3.1.1.tgz", + "integrity": "sha512-SiNW3Kp5xshlA3McA2C/tjU7K8+VJc9ZNe09foB8ut8lMY7bR9NKPo5ARHDOj24mV69/utNy8gqDjF93FcFlFg==", "requires": { "promise-make-counter": "^1.0.2" } }, "tiny-readdir-glob": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/tiny-readdir-glob/-/tiny-readdir-glob-1.23.2.tgz", - "integrity": "sha512-+47FIdgzEtZj03mOyq9iAljlZZNleqSEwe3i6Uzkzec5axbMg32Vp78U2fLo4TiCMv9gzjnno7yJn34z5pXECw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tiny-readdir-glob/-/tiny-readdir-glob-2.0.0.tgz", + "integrity": "sha512-1zAGi76CspOE4MjMrsx2OeLpvjiHyprFTuJ1O3k5C1qU09GpltOL+zx5rw9CaVQ/qkpkmCfc09TS2fV52GJrrw==", "requires": { - "tiny-readdir": "^2.7.0", - "zeptomatch": "^2.0.1", - "zeptomatch-explode": "^1.0.1", - "zeptomatch-is-static": "^1.0.1", - "zeptomatch-unescape": "^1.0.1" + "tiny-readdir": "^3.1.1", + "zeptomatch": "^2.1.0" } }, "tiny-spinner": { @@ -12037,20 +12044,10 @@ "resolved": "https://registry.npmjs.org/zeptomatch-escape/-/zeptomatch-escape-1.0.1.tgz", "integrity": "sha512-kAc5HzvnF66djCYDqpsS46Y/FKi+4pe/KJRmTmm/hwmoaNYzmm6bBY07cdkxmJCdY018S5UeQn4yP+9X2x1MbQ==" }, - "zeptomatch-explode": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/zeptomatch-explode/-/zeptomatch-explode-1.0.1.tgz", - "integrity": "sha512-7cUQASLLRGZ20+zEQcEgQ9z/gH1+jSfrNg4KfRJSxF1QU2fpymAwWvnAxl69GD5pr3IV0V9vo3ke2np//Nh4tQ==" - }, "zeptomatch-is-static": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/zeptomatch-is-static/-/zeptomatch-is-static-1.0.1.tgz", "integrity": "sha512-bN9q7H/UdXhkub01WE7b7Grg07jLldNnIWG2T1IpBq5NtvcQ4DwFbNiGGapnbKHUdWiCNjg/bIvixV88nj9gog==" - }, - "zeptomatch-unescape": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/zeptomatch-unescape/-/zeptomatch-unescape-1.0.1.tgz", - "integrity": "sha512-xhSFkKV0aQ03e/eiN4VhOTwJhcqfH7SMiGHrWKw9gXi+0EVJAxJ8Gt4ehozYsYLhUXL1JFbP1g3EE6ZmkStB0g==" } } } diff --git a/package.json b/package.json index dc43720..a764597 100644 --- a/package.json +++ b/package.json @@ -54,8 +54,8 @@ "smol-toml": "^1.7.0", "specialist": "^2.0.0", "tiny-editorconfig": "^1.0.2", - "tiny-readdir": "^2.7.4", - "tiny-readdir-glob": "^1.23.2", + "tiny-readdir": "^3.1.1", + "tiny-readdir-glob": "^2.0.0", "tiny-spinner": "^2.0.5", "worktank": "^3.0.2", "zeptomatch": "^2.1.0", diff --git a/src/utils.ts b/src/utils.ts index 65fcd63..9bdcca6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -11,6 +11,7 @@ import url from "node:url"; import resolveTimeout from "promise-resolve-timeout"; import { exit } from "specialist"; import readdir from "tiny-readdir"; +import type { Dirent } from "tiny-readdir"; import readdirGlob from "tiny-readdir-glob"; import zeptomatch from "zeptomatch"; import zeptomatchEscape from "zeptomatch-escape"; @@ -18,6 +19,8 @@ import zeptomatchIsStatic from "zeptomatch-is-static"; import type { ContextOptions, FormatOptions, FunctionMaybe, Key, LogLevel, Options, PrettierConfigWithOverrides, PrettierPlugin } from "./types.js"; import type { PluginsOptions } from "./types.js"; +type DirentWithParentPath = Dirent & { parentPath: string }; + function castArray(value: T | T[]): T[] { return isArray(value) ? value : [value]; } @@ -106,12 +109,53 @@ async function getFoldersChildrenPaths(foldersPaths: string[]): Promise { + return ignoreRe.test(path.relative(rootPath, targetPath)); + }; + + // These are the files and directories that were found during glob traversal. + // They haven't yet been filtered by the `ignore` function so may not + // equal the result files. + const filesFound: string[] = []; + const filesFoundNames = new Set(); + const filesFoundNamesToPaths: Record = {}; + const directoriesFound: string[] = []; + + const onDirents = (dirents: Dirent[]): undefined => { + for (const dirent of dirents) { + const direntName = dirent.name; + // TODO (jg): remove this cast once tiny-readdir knows about + // the `parentPath` property on Dirent objects + const direntPath = fastJoinedPath((dirent as DirentWithParentPath).parentPath, direntName); + if (ignore(direntPath)) continue; + if (dirent.isFile()) { + filesFound.push(direntPath); + filesFoundNames.add(direntName); + if (!Object.hasOwn(filesFoundNamesToPaths, direntName)) { + filesFoundNamesToPaths[direntName] = []; + } + filesFoundNamesToPaths[direntName].push(direntPath); + } else if (dirent.isDirectory()) { + directoriesFound.push(direntPath); + } + } + }; + + // Globs are matched against paths relative to the root, which are never prefixed with "./" + const globsNormalized = globs.map((glob) => glob.replace(/^(!*)(?:\.\/)+/, "$1")); + + const result = await readdirGlob(globsNormalized, { cwd: rootPath, followSymlinks: false, - ignore: `**/{.git,.sl,.svn,.hg,.DS_Store,Thumbs.db${withNodeModules ? "" : ",node_modules"}}`, + ignore, + onDirents, }); + + return { ...result, filesFound, filesFoundNames, filesFoundNamesToPaths, directoriesFound }; } async function getModule(modulePath: string): Promise { @@ -263,7 +307,7 @@ async function getTargetsPaths( const directoriesResults = await Promise.all(targetDirectories.map((targetPath) => getDirectoryPaths(targetPath, withNodeModules))); const directoriesResultsFiles = directoriesResults.map((result) => result.files); - const directoriesResultsFilesFoundNames = directoriesResults.map((result) => [...result.filesNames]); + const directoriesResultsFilesFoundNames = directoriesResults.map((result) => uniq(result.files.map((filePath) => path.basename(filePath)))); const foundFiles = uniqChunks(globResultFiles, ...directoriesResultsFiles); const foundFilesNames = uniqChunks(globResultFilesFoundNames, ...directoriesResultsFilesFoundNames);