miniread 1.94.0 → 1.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,8 +18,11 @@ miniread --input ./minified --output ./readable --transforms recommended
18
18
  miniread --input ./minified --output ./readable --dry-run
19
19
  miniread --input ./minified --output ./readable --workers 8
20
20
  miniread --input ./minified --output ./readable --safe-stabilize-top-level-bindings
21
+ miniread --input ./repo --output ./readable --ignore-dirs none
21
22
  ```
22
23
 
24
+ When scanning an input directory, miniread ignores `.git`, `node_modules`, `coverage`, `.turbo`, and `.next` by default. `--ignore-dirs` replaces that default list (for example: `--ignore-dirs build,dist`), and `--ignore-dirs none` disables ignores entirely.
25
+
23
26
  ## Output stability
24
27
 
25
28
  The recommended preset includes `stabilize-top-level-bindings`, which renames program-scope bindings to stable hash-based names (`$h_<hash>`). This produces deterministic output across minifier versions, making diffs more useful.
@@ -14,6 +14,7 @@ export declare const createProgram: (packageMetadata: PackageMetadata) => Comman
14
14
  format: ListFormat;
15
15
  dryRun?: true | undefined;
16
16
  workers: number;
17
+ ignoreDirs?: ReadonlySet<string> | undefined;
17
18
  overwrite?: true | undefined;
18
19
  safeStabilizeTopLevelBindings?: true | undefined;
19
20
  }, {}>;
@@ -1,4 +1,5 @@
1
1
  import { Command, InvalidArgumentError } from "@commander-js/extra-typings";
2
+ import { parseIgnoredDirectories } from "./parse-ignored-directories.js";
2
3
  const parseListFormat = (value) => {
3
4
  if (value === "text" || value === "tsv" || value === "json")
4
5
  return value;
@@ -26,6 +27,7 @@ export const createProgram = (packageMetadata) => {
26
27
  .option("--format <format>", "Output format for --list-transforms: text, tsv, json", parseListFormat, "text")
27
28
  .option("--dry-run", "Show what would be changed without writing files")
28
29
  .option("-w, --workers <n>", "Number of parallel workers", parsePositiveInt, 4)
30
+ .option("--ignore-dirs <list>", "Comma-separated directory names to skip during --input directory scans. Replaces default ignores (.git,node_modules,coverage,.turbo,.next). Use 'none' to disable.", parseIgnoredDirectories)
29
31
  .option("-f, --overwrite", "Overwrite existing files in output directory")
30
32
  .option("--safe-stabilize-top-level-bindings", "Make stabilize-top-level-bindings bail out on dynamic-name hazards (safer; output more likely runnable)");
31
33
  program.addHelpText("after", `
@@ -9,5 +9,8 @@ type FindInputFilesError = {
9
9
  errors: string[];
10
10
  };
11
11
  type FindInputFilesResult = FindInputFilesOk | FindInputFilesError;
12
- export declare const findInputFiles: (input: string) => Promise<FindInputFilesResult>;
12
+ type FindInputFilesOptions = {
13
+ ignoredDirectories?: ReadonlySet<string>;
14
+ };
15
+ export declare const findInputFiles: (input: string, options?: FindInputFilesOptions) => Promise<FindInputFilesResult>;
13
16
  export {};
@@ -7,14 +7,34 @@ const isSourceFile = (fileName) => {
7
7
  fileName.endsWith(".js") ||
8
8
  fileName.endsWith(".jsx")));
9
9
  };
10
- const findAllFiles = async (directory) => {
10
+ const ignoredDirectoryNames = new Set([
11
+ ".git",
12
+ "node_modules",
13
+ "coverage",
14
+ ".turbo",
15
+ ".next",
16
+ ]);
17
+ const normalizeIgnoredDirectoryNames = (directoryNames) => {
18
+ const normalized = new Set();
19
+ for (const directoryName of directoryNames) {
20
+ const normalizedDirectoryName = directoryName.trim().toLowerCase();
21
+ if (normalizedDirectoryName === "")
22
+ continue;
23
+ normalized.add(normalizedDirectoryName);
24
+ }
25
+ return normalized;
26
+ };
27
+ const findAllFiles = async (directory, ignoredDirectories) => {
11
28
  const sourceFiles = [];
12
29
  const otherFiles = [];
13
30
  const entries = await fs.readdir(directory, { withFileTypes: true });
14
31
  for (const entry of entries) {
15
32
  const fullPath = path.join(directory, entry.name);
16
33
  if (entry.isDirectory()) {
17
- const subResult = await findAllFiles(fullPath);
34
+ // Directory matching should be case-insensitive across filesystems.
35
+ if (ignoredDirectories.has(entry.name.toLowerCase()))
36
+ continue;
37
+ const subResult = await findAllFiles(fullPath, ignoredDirectories);
18
38
  sourceFiles.push(...subResult.sourceFiles);
19
39
  otherFiles.push(...subResult.otherFiles);
20
40
  continue;
@@ -29,7 +49,7 @@ const findAllFiles = async (directory) => {
29
49
  }
30
50
  return { sourceFiles, otherFiles };
31
51
  };
32
- export const findInputFiles = async (input) => {
52
+ export const findInputFiles = async (input, options = {}) => {
33
53
  let inputStat;
34
54
  try {
35
55
  inputStat = await fs.stat(input);
@@ -40,9 +60,10 @@ export const findInputFiles = async (input) => {
40
60
  }
41
61
  let sourceFilePaths;
42
62
  let otherFilePaths;
63
+ const normalizedIgnoredDirectories = normalizeIgnoredDirectoryNames(options.ignoredDirectories ?? ignoredDirectoryNames);
43
64
  try {
44
65
  if (inputStat.isDirectory()) {
45
- const allFiles = await findAllFiles(input);
66
+ const allFiles = await findAllFiles(input, normalizedIgnoredDirectories);
46
67
  sourceFilePaths = allFiles.sourceFiles;
47
68
  otherFilePaths = allFiles.otherFiles;
48
69
  }
@@ -0,0 +1 @@
1
+ export declare const parseIgnoredDirectories: (value: string) => ReadonlySet<string>;
@@ -0,0 +1,18 @@
1
+ import { InvalidArgumentError } from "@commander-js/extra-typings";
2
+ export const parseIgnoredDirectories = (value) => {
3
+ const normalizedValue = value.trim();
4
+ if (normalizedValue.toLowerCase() === "none") {
5
+ return new Set();
6
+ }
7
+ if (normalizedValue === "") {
8
+ throw new InvalidArgumentError("must not be empty; use 'none' to disable ignored directories");
9
+ }
10
+ const directories = normalizedValue
11
+ .split(",")
12
+ .map((directoryName) => directoryName.trim())
13
+ .filter((directoryName) => directoryName !== "");
14
+ if (directories.length === 0) {
15
+ throw new InvalidArgumentError("must contain at least one directory name or 'none'");
16
+ }
17
+ return new Set(directories);
18
+ };
@@ -25,6 +25,7 @@ export const runInputMode = async (options) => {
25
25
  input: options.input,
26
26
  output: options.output,
27
27
  transforms: transformsResult.transforms,
28
+ ignoredDirectories: options.ignoreDirs,
28
29
  transformOptions: {
29
30
  ...getDefaultTransformOptions(),
30
31
  unsafeStabilizeTopLevelBindings: shouldUnsafeStabilize(options.safeStabilizeTopLevelBindings),
@@ -3,6 +3,7 @@ export type RunnerOptions = {
3
3
  input: string;
4
4
  output: string;
5
5
  transforms: Transform[];
6
+ ignoredDirectories?: ReadonlySet<string>;
6
7
  transformOptions?: Record<string, unknown>;
7
8
  dryRun: boolean;
8
9
  workers: number;
@@ -11,8 +11,8 @@ const logVerbose = (verbose, message) => {
11
11
  console.error(message);
12
12
  };
13
13
  export const runTransforms = async (options) => {
14
- const { input, output, transforms, transformOptions = {}, dryRun, workers, overwrite, verbose, } = options;
15
- const inputFilesResult = await findInputFiles(input);
14
+ const { input, output, transforms, ignoredDirectories, transformOptions = {}, dryRun, workers, overwrite, verbose, } = options;
15
+ const inputFilesResult = await findInputFiles(input, { ignoredDirectories });
16
16
  if (!inputFilesResult.ok) {
17
17
  return {
18
18
  filesProcessed: 0,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "miniread",
3
3
  "author": "Łukasz Jerciński",
4
4
  "license": "MIT",
5
- "version": "1.94.0",
5
+ "version": "1.95.0",
6
6
  "description": "Transform minified JavaScript/TypeScript into a more readable form using deterministic AST-based transforms.",
7
7
  "repository": {
8
8
  "type": "git",