miniread 1.96.1 → 1.98.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 +23 -5
- package/bin/miniread-pick-random-transform +17 -0
- package/dist/cli/find-input-files.d.ts +3 -4
- package/dist/cli/find-input-files.js +19 -19
- package/dist/cli/install-stdout-error-handler.js +4 -13
- package/dist/cli/parse-cli-options.d.ts +6 -0
- package/dist/cli/parse-cli-options.js +48 -0
- package/dist/cli/run-transforms.d.ts +1 -1
- package/dist/cli/run-transforms.js +23 -9
- package/dist/cli.js +102 -25
- package/dist/scripts/pick-random-transform/create-pick-random-transform-command.d.ts +11 -0
- package/dist/scripts/pick-random-transform/create-pick-random-transform-command.js +30 -0
- package/dist/scripts/pick-random-transform/load-transform-performance-entries.d.ts +5 -0
- package/dist/scripts/pick-random-transform/load-transform-performance-entries.js +53 -0
- package/dist/scripts/pick-random-transform/parse-pick-random-transform-cli-options.d.ts +12 -0
- package/dist/scripts/pick-random-transform/parse-pick-random-transform-cli-options.js +20 -0
- package/dist/scripts/pick-random-transform/pick-random-transform-candidates.d.ts +26 -0
- package/dist/scripts/pick-random-transform/pick-random-transform-candidates.js +55 -0
- package/dist/scripts/pick-random-transform/run-pick-random-transform-cli.d.ts +1 -0
- package/dist/scripts/pick-random-transform/run-pick-random-transform-cli.js +73 -0
- package/dist/scripts/pick-random-transform.d.ts +1 -0
- package/dist/scripts/pick-random-transform.js +12 -0
- package/dist/transforms/_generated/manifest.js +10 -38
- package/dist/transforms/_generated/registry.js +2 -12
- package/dist/transforms/dynamic-name-lookup/detect-dynamic-name-lookup.d.ts +5 -0
- package/dist/transforms/dynamic-name-lookup/detect-dynamic-name-lookup.js +18 -0
- package/dist/transforms/{shared/url/is-allowed-url-constructor-in-scope.js → is-allowed-url-constructor-in-scope.js} +1 -2
- package/dist/transforms/{shared/url/is-allowed-url-namespace-binding-in-scope.js → is-allowed-url-namespace-binding-in-scope.js} +1 -2
- package/dist/transforms/{shared/url/is-require-from-url-module.d.ts → is-require-from-url-module.d.ts} +3 -1
- package/dist/transforms/{shared/node/is-node-require-in-scope.js → is-require-from-url-module.js} +80 -0
- package/dist/transforms/recommended-transform-order.d.ts +1 -3
- package/dist/transforms/recommended-transform-order.js +2 -12
- package/dist/transforms/rename-charcode-variables-v2/manifest.json +5 -5
- package/dist/transforms/rename-charcode-variables-v2/rename-charcode-variables-v2-transform.js +1 -29
- package/dist/transforms/rename-object-property-value-variables/get-object-property-value-base-name.d.ts +2 -0
- package/dist/transforms/rename-object-property-value-variables/get-object-property-value-base-name.js +76 -0
- package/dist/transforms/rename-object-property-value-variables/manifest.json +13 -0
- package/dist/transforms/rename-object-property-value-variables/rename-object-property-value-variables-transform.d.ts +2 -0
- package/dist/transforms/rename-object-property-value-variables/rename-object-property-value-variables-transform.js +121 -0
- package/dist/transforms/rename-parameters-to-match-properties-v2/manifest.json +2 -3
- package/dist/transforms/rename-platform-win32-flags/is-safe-process-binding.js +1 -1
- package/dist/transforms/rename-safe-property-accessor-parameters/is-member-access.js +1 -1
- package/dist/transforms/rename-safe-property-accessor-parameters/is-nullish-check.js +1 -1
- package/dist/transforms/rename-safe-property-accessor-parameters/match-safe-property-accessor-factory.js +1 -1
- package/dist/transforms/rename-safe-property-accessor-parameters/unwrap-parentheses.js +7 -0
- package/dist/transforms/rename-search-parameters-variables/rename-search-parameters-variables-transform.js +2 -2
- package/dist/transforms/rename-url-parameters/rename-url-parameters-transform.js +1 -1
- package/dist/transforms/rename-url-variables/rename-url-variables-transform.js +3 -3
- package/dist/transforms/rename-worker-handles/rename-worker-handles-transform.js +1 -1
- package/dist/transforms/{rename-worker-handles → shared}/is-type-only-reference.js +2 -2
- package/dist/transforms/use-optional-chaining/is-nullish-check.js +1 -1
- package/dist/transforms/use-optional-chaining/is-undefined-expression.js +1 -1
- package/dist/transforms/use-optional-chaining/unwrap-parentheses.d.ts +2 -0
- package/package.json +3 -1
- package/dist/cli/create-program.d.ts +0 -24
- package/dist/cli/create-program.js +0 -61
- package/dist/cli/parse-ignored-directories.d.ts +0 -1
- package/dist/cli/parse-ignored-directories.js +0 -18
- package/dist/cli/run-input-mode.d.ts +0 -14
- package/dist/cli/run-input-mode.js +0 -46
- package/dist/cli/run-stdin-mode.d.ts +0 -10
- package/dist/cli/run-stdin-mode.js +0 -22
- package/dist/cli/should-unsafe-stabilize.d.ts +0 -1
- package/dist/cli/should-unsafe-stabilize.js +0 -13
- package/dist/cli/write-transform-summaries.d.ts +0 -2
- package/dist/cli/write-transform-summaries.js +0 -18
- package/dist/transforms/rename-dirent-parameters/get-member-property-name.d.ts +0 -3
- package/dist/transforms/rename-dirent-parameters/get-member-property-name.js +0 -10
- package/dist/transforms/rename-dirent-parameters/is-dirent-callback-parameter-binding.d.ts +0 -2
- package/dist/transforms/rename-dirent-parameters/is-dirent-callback-parameter-binding.js +0 -111
- package/dist/transforms/rename-dirent-parameters/is-dirent-method-call.d.ts +0 -3
- package/dist/transforms/rename-dirent-parameters/is-dirent-method-call.js +0 -26
- package/dist/transforms/rename-dirent-parameters/manifest.json +0 -13
- package/dist/transforms/rename-dirent-parameters/rename-dirent-parameters-transform.d.ts +0 -2
- package/dist/transforms/rename-dirent-parameters/rename-dirent-parameters-transform.js +0 -70
- package/dist/transforms/rename-memoized-instance-variables/manifest.json +0 -12
- package/dist/transforms/rename-memoized-instance-variables/rename-memoized-instance-variables-transform.d.ts +0 -2
- package/dist/transforms/rename-memoized-instance-variables/rename-memoized-instance-variables-transform.js +0 -130
- package/dist/transforms/rename-parameters-to-match-properties-v3/get-assignment-property-name.d.ts +0 -2
- package/dist/transforms/rename-parameters-to-match-properties-v3/get-assignment-property-name.js +0 -33
- package/dist/transforms/rename-parameters-to-match-properties-v3/manifest.json +0 -13
- package/dist/transforms/rename-parameters-to-match-properties-v3/nullish-default-expression.d.ts +0 -3
- package/dist/transforms/rename-parameters-to-match-properties-v3/nullish-default-expression.js +0 -67
- package/dist/transforms/rename-parameters-to-match-properties-v3/rename-parameters-to-match-properties-v3-transform.d.ts +0 -2
- package/dist/transforms/rename-parameters-to-match-properties-v3/rename-parameters-to-match-properties-v3-transform.js +0 -77
- package/dist/transforms/rename-queue-entry-variables/is-property-named.d.ts +0 -2
- package/dist/transforms/rename-queue-entry-variables/is-property-named.js +0 -13
- package/dist/transforms/rename-queue-entry-variables/manifest.json +0 -13
- package/dist/transforms/rename-queue-entry-variables/rename-queue-entry-variables-transform.d.ts +0 -2
- package/dist/transforms/rename-queue-entry-variables/rename-queue-entry-variables-transform.js +0 -128
- package/dist/transforms/rename-regex-exec-results/has-match-access.d.ts +0 -2
- package/dist/transforms/rename-regex-exec-results/has-match-access.js +0 -68
- package/dist/transforms/rename-regex-exec-results/is-regex-value.d.ts +0 -3
- package/dist/transforms/rename-regex-exec-results/is-regex-value.js +0 -92
- package/dist/transforms/rename-regex-exec-results/manifest.json +0 -12
- package/dist/transforms/rename-regex-exec-results/rename-regex-exec-results-transform.d.ts +0 -2
- package/dist/transforms/rename-regex-exec-results/rename-regex-exec-results-transform.js +0 -89
- package/dist/transforms/shared/node/is-node-require-in-scope.d.ts +0 -2
- package/dist/transforms/shared/url/is-require-from-url-module.js +0 -80
- package/dist/transforms/simplify-filter-boolean-coercion/manifest.json +0 -13
- package/dist/transforms/simplify-filter-boolean-coercion/simplify-filter-boolean-coercion-transform.d.ts +0 -2
- package/dist/transforms/simplify-filter-boolean-coercion/simplify-filter-boolean-coercion-transform.js +0 -124
- /package/dist/transforms/{shared/url/get-url-construction-kind.d.ts → get-url-construction-kind.d.ts} +0 -0
- /package/dist/transforms/{shared/url/get-url-construction-kind.js → get-url-construction-kind.js} +0 -0
- /package/dist/transforms/{shared/url/is-allowed-url-constructor-in-scope.d.ts → is-allowed-url-constructor-in-scope.d.ts} +0 -0
- /package/dist/transforms/{shared/url/is-allowed-url-module-import-binding.d.ts → is-allowed-url-module-import-binding.d.ts} +0 -0
- /package/dist/transforms/{shared/url/is-allowed-url-module-import-binding.js → is-allowed-url-module-import-binding.js} +0 -0
- /package/dist/transforms/{shared/url/is-allowed-url-namespace-binding-in-scope.d.ts → is-allowed-url-namespace-binding-in-scope.d.ts} +0 -0
- /package/dist/transforms/{shared → rename-safe-property-accessor-parameters}/unwrap-parentheses.d.ts +0 -0
- /package/dist/transforms/{rename-worker-handles → shared}/is-type-only-reference.d.ts +0 -0
- /package/dist/transforms/{shared/url/url-usage-heuristics.d.ts → url-usage-heuristics.d.ts} +0 -0
- /package/dist/transforms/{shared/url/url-usage-heuristics.js → url-usage-heuristics.js} +0 -0
- /package/dist/transforms/{shared → use-optional-chaining}/unwrap-parentheses.js +0 -0
package/README.md
CHANGED
|
@@ -17,19 +17,21 @@ miniread --list-transforms
|
|
|
17
17
|
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
|
+
miniread --input ./minified --output ./readable --ignore-dirs dist,coverage
|
|
20
21
|
miniread --input ./minified --output ./readable --safe-stabilize-top-level-bindings
|
|
21
|
-
miniread --input ./repo --output ./readable --ignore-dirs none
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
When scanning
|
|
24
|
+
When scanning input directories, `miniread` ignores common heavy folders by
|
|
25
|
+
default: `.git`, `node_modules`, `coverage`, `.next`, `.nuxt`, `dist`, `build`,
|
|
26
|
+
`out`, and `.cache`.
|
|
27
|
+
|
|
28
|
+
Use `--ignore-dirs <comma-separated-names>` to add more directory names to the
|
|
29
|
+
ignore set (matching is case-insensitive).
|
|
25
30
|
|
|
26
31
|
## Output stability
|
|
27
32
|
|
|
28
33
|
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.
|
|
29
34
|
|
|
30
|
-
The `recommended` preset prioritizes readability and may include documented heuristics where edge-case behavioral differences are possible.
|
|
31
|
-
One example is `simplify-filter-boolean-coercion`, which matches `.filter(...)` heuristically; strict-boolean custom filter implementations can behave differently.
|
|
32
|
-
|
|
33
35
|
By default, this transform runs even when it detects code patterns (like `eval`) that could make the output non-runnable. Use `--safe-stabilize-top-level-bindings` or set `MINIREAD_UNSAFE_STABILIZE_TOP_LEVEL_BINDINGS` to a falsy value (`0`, `false`, `no`, or empty string) to skip renaming in such cases.
|
|
34
36
|
|
|
35
37
|
## Examples
|
|
@@ -95,6 +97,22 @@ pnpm run miniread-evaluate -- --compare v1.0:v1.1 --compare v2.0:v2.1 --baseline
|
|
|
95
97
|
| tail -n +2 | sort -k5,5nr | head -10
|
|
96
98
|
```
|
|
97
99
|
|
|
100
|
+
## miniread-pick-random-transform (developer tool)
|
|
101
|
+
|
|
102
|
+
The `miniread-pick-random-transform` CLI picks random transform candidates from the slowest eligible recommended transforms based on `manifest.json` evaluation metrics.
|
|
103
|
+
|
|
104
|
+
### Usage
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pnpm run miniread-pick-random-transform -- --help
|
|
108
|
+
|
|
109
|
+
# Pick 3 random candidates from the 10 slowest eligible transforms
|
|
110
|
+
pnpm run miniread-pick-random-transform
|
|
111
|
+
|
|
112
|
+
# Customize evaluation key and pool size
|
|
113
|
+
pnpm run miniread-pick-random-transform -- --evaluation-key v1.0:v1.1 --top 15 --pick 3
|
|
114
|
+
```
|
|
115
|
+
|
|
98
116
|
## License
|
|
99
117
|
|
|
100
118
|
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point that dynamically imports the compiled TypeScript.
|
|
4
|
+
*
|
|
5
|
+
* Uses top-level await to ensure module evaluation errors are handled
|
|
6
|
+
* properly. Without await, errors during import would surface as unhandled
|
|
7
|
+
* rejections instead of clean CLI failures with appropriate exit codes.
|
|
8
|
+
*/
|
|
9
|
+
try {
|
|
10
|
+
await import("../dist/scripts/pick-random-transform.js");
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error(
|
|
13
|
+
"Failed to start miniread-pick-random-transform:",
|
|
14
|
+
error instanceof Error ? error.message : error,
|
|
15
|
+
);
|
|
16
|
+
process.exitCode = 1;
|
|
17
|
+
}
|
|
@@ -9,8 +9,7 @@ type FindInputFilesError = {
|
|
|
9
9
|
errors: string[];
|
|
10
10
|
};
|
|
11
11
|
type FindInputFilesResult = FindInputFilesOk | FindInputFilesError;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
export declare const findInputFiles: (input: string, options?: FindInputFilesOptions) => Promise<FindInputFilesResult>;
|
|
12
|
+
export declare const findInputFiles: (input: string, options?: {
|
|
13
|
+
ignoredDirectoryNames?: string[];
|
|
14
|
+
}) => Promise<FindInputFilesResult>;
|
|
16
15
|
export {};
|
|
@@ -7,34 +7,31 @@ const isSourceFile = (fileName) => {
|
|
|
7
7
|
fileName.endsWith(".js") ||
|
|
8
8
|
fileName.endsWith(".jsx")));
|
|
9
9
|
};
|
|
10
|
-
const
|
|
10
|
+
const DEFAULT_IGNORED_DIRECTORIES = new Set([
|
|
11
11
|
".git",
|
|
12
12
|
"node_modules",
|
|
13
13
|
"coverage",
|
|
14
|
-
".turbo",
|
|
15
14
|
".next",
|
|
15
|
+
".nuxt",
|
|
16
|
+
"dist",
|
|
17
|
+
"build",
|
|
18
|
+
"out",
|
|
19
|
+
".cache",
|
|
16
20
|
]);
|
|
17
|
-
const
|
|
18
|
-
|
|
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;
|
|
21
|
+
const normalizeDirectoryName = (directoryName) => {
|
|
22
|
+
return directoryName.trim().toLowerCase();
|
|
26
23
|
};
|
|
27
|
-
const findAllFiles = async (directory,
|
|
24
|
+
const findAllFiles = async (directory, ignoredDirectoryNames) => {
|
|
28
25
|
const sourceFiles = [];
|
|
29
26
|
const otherFiles = [];
|
|
30
27
|
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
31
28
|
for (const entry of entries) {
|
|
29
|
+
if (entry.isDirectory() &&
|
|
30
|
+
ignoredDirectoryNames.has(normalizeDirectoryName(entry.name)))
|
|
31
|
+
continue;
|
|
32
32
|
const fullPath = path.join(directory, entry.name);
|
|
33
33
|
if (entry.isDirectory()) {
|
|
34
|
-
|
|
35
|
-
if (ignoredDirectories.has(entry.name.toLowerCase()))
|
|
36
|
-
continue;
|
|
37
|
-
const subResult = await findAllFiles(fullPath, ignoredDirectories);
|
|
34
|
+
const subResult = await findAllFiles(fullPath, ignoredDirectoryNames);
|
|
38
35
|
sourceFiles.push(...subResult.sourceFiles);
|
|
39
36
|
otherFiles.push(...subResult.otherFiles);
|
|
40
37
|
continue;
|
|
@@ -49,7 +46,11 @@ const findAllFiles = async (directory, ignoredDirectories) => {
|
|
|
49
46
|
}
|
|
50
47
|
return { sourceFiles, otherFiles };
|
|
51
48
|
};
|
|
52
|
-
export const findInputFiles = async (input, options
|
|
49
|
+
export const findInputFiles = async (input, options) => {
|
|
50
|
+
const ignoredDirectoryNames = new Set([...DEFAULT_IGNORED_DIRECTORIES].map((directoryName) => normalizeDirectoryName(directoryName)));
|
|
51
|
+
for (const directoryName of options?.ignoredDirectoryNames ?? []) {
|
|
52
|
+
ignoredDirectoryNames.add(normalizeDirectoryName(directoryName));
|
|
53
|
+
}
|
|
53
54
|
let inputStat;
|
|
54
55
|
try {
|
|
55
56
|
inputStat = await fs.stat(input);
|
|
@@ -60,10 +61,9 @@ export const findInputFiles = async (input, options = {}) => {
|
|
|
60
61
|
}
|
|
61
62
|
let sourceFilePaths;
|
|
62
63
|
let otherFilePaths;
|
|
63
|
-
const normalizedIgnoredDirectories = normalizeIgnoredDirectoryNames(options.ignoredDirectories ?? ignoredDirectoryNames);
|
|
64
64
|
try {
|
|
65
65
|
if (inputStat.isDirectory()) {
|
|
66
|
-
const allFiles = await findAllFiles(input,
|
|
66
|
+
const allFiles = await findAllFiles(input, ignoredDirectoryNames);
|
|
67
67
|
sourceFilePaths = allFiles.sourceFiles;
|
|
68
68
|
otherFilePaths = allFiles.otherFiles;
|
|
69
69
|
}
|
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
const isExpectedStdoutCloseError = (error) => {
|
|
2
|
-
if (!(error instanceof Error))
|
|
3
|
-
return false;
|
|
4
|
-
const code = "code" in error && typeof error.code === "string" ? error.code : "";
|
|
5
|
-
// Treat only broken-pipe as expected in shell pipelines (e.g. `... | head`).
|
|
6
|
-
// Other stdout errors should surface so real stream lifecycle bugs are visible.
|
|
7
|
-
return code === "EPIPE";
|
|
8
|
-
};
|
|
9
1
|
export const installStdoutErrorHandler = () => {
|
|
10
2
|
process.stdout.on("error", (error) => {
|
|
11
|
-
if (
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
process.
|
|
15
|
-
return;
|
|
3
|
+
if (error.code === "EPIPE") {
|
|
4
|
+
// Exiting immediately prevents repeated EPIPE writes in closed pipelines.
|
|
5
|
+
// eslint-disable-next-line unicorn/no-process-exit
|
|
6
|
+
process.exit(0);
|
|
16
7
|
}
|
|
17
8
|
throw error;
|
|
18
9
|
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type ListFormat = "text" | "tsv" | "json";
|
|
2
|
+
export declare const parseIgnoredDirectories: (value: string) => string[];
|
|
3
|
+
export declare const parseListFormat: (value: string) => ListFormat;
|
|
4
|
+
export declare const parsePositiveInt: (value: string) => number;
|
|
5
|
+
export declare const normalizeArgvForCommander: (argv: string[]) => string[];
|
|
6
|
+
export declare const shouldUnsafeStabilize: (safeFlag: boolean | undefined) => boolean;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { InvalidArgumentError } from "@commander-js/extra-typings";
|
|
2
|
+
export const parseIgnoredDirectories = (value) => {
|
|
3
|
+
const items = value
|
|
4
|
+
.split(",")
|
|
5
|
+
.map((item) => item.trim())
|
|
6
|
+
.filter((item) => item.length > 0);
|
|
7
|
+
if (items.length === 0) {
|
|
8
|
+
throw new InvalidArgumentError("must contain at least one directory name");
|
|
9
|
+
}
|
|
10
|
+
return items;
|
|
11
|
+
};
|
|
12
|
+
export const parseListFormat = (value) => {
|
|
13
|
+
if (value === "text" || value === "tsv" || value === "json")
|
|
14
|
+
return value;
|
|
15
|
+
throw new InvalidArgumentError(`invalid value '${value}' (expected: text, tsv, json)`);
|
|
16
|
+
};
|
|
17
|
+
export const parsePositiveInt = (value) => {
|
|
18
|
+
const parsed = Number(value);
|
|
19
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
20
|
+
throw new InvalidArgumentError("must be a positive integer");
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
};
|
|
24
|
+
export const normalizeArgvForCommander = (argv) => {
|
|
25
|
+
// Package managers forward args as: [..., "--", "--flag"].
|
|
26
|
+
// Strip only that forwarded sentinel at argv[2], preserving legitimate values
|
|
27
|
+
// like `--input --`.
|
|
28
|
+
if (argv[2] !== "--")
|
|
29
|
+
return argv;
|
|
30
|
+
const executablePath = argv[0];
|
|
31
|
+
const scriptPath = argv[1];
|
|
32
|
+
if (executablePath === undefined || scriptPath === undefined)
|
|
33
|
+
return argv;
|
|
34
|
+
return [executablePath, scriptPath, ...argv.slice(3)];
|
|
35
|
+
};
|
|
36
|
+
const isEnvironmentFalsy = (value) => {
|
|
37
|
+
if (value === undefined)
|
|
38
|
+
return false;
|
|
39
|
+
const trimmed = value.trim().toLowerCase();
|
|
40
|
+
return trimmed === "" || ["0", "false", "no"].includes(trimmed);
|
|
41
|
+
};
|
|
42
|
+
export const shouldUnsafeStabilize = (safeFlag) => {
|
|
43
|
+
if (safeFlag === true)
|
|
44
|
+
return false;
|
|
45
|
+
if (isEnvironmentFalsy(process.env.MINIREAD_UNSAFE_STABILIZE_TOP_LEVEL_BINDINGS))
|
|
46
|
+
return false;
|
|
47
|
+
return true;
|
|
48
|
+
};
|
|
@@ -3,11 +3,11 @@ export type RunnerOptions = {
|
|
|
3
3
|
input: string;
|
|
4
4
|
output: string;
|
|
5
5
|
transforms: Transform[];
|
|
6
|
-
ignoredDirectories?: ReadonlySet<string>;
|
|
7
6
|
transformOptions?: Record<string, unknown>;
|
|
8
7
|
dryRun: boolean;
|
|
9
8
|
workers: number;
|
|
10
9
|
overwrite: boolean;
|
|
10
|
+
ignoreDirectories?: string[];
|
|
11
11
|
verbose: boolean;
|
|
12
12
|
};
|
|
13
13
|
export type RunnerResult = {
|
|
@@ -11,8 +11,10 @@ const logVerbose = (verbose, message) => {
|
|
|
11
11
|
console.error(message);
|
|
12
12
|
};
|
|
13
13
|
export const runTransforms = async (options) => {
|
|
14
|
-
const { input, output, transforms,
|
|
15
|
-
const inputFilesResult = await findInputFiles(input, {
|
|
14
|
+
const { input, output, transforms, transformOptions = {}, dryRun, workers, overwrite, ignoreDirectories, verbose, } = options;
|
|
15
|
+
const inputFilesResult = await findInputFiles(input, {
|
|
16
|
+
ignoredDirectoryNames: ignoreDirectories,
|
|
17
|
+
});
|
|
16
18
|
if (!inputFilesResult.ok) {
|
|
17
19
|
return {
|
|
18
20
|
filesProcessed: 0,
|
|
@@ -27,13 +29,25 @@ export const runTransforms = async (options) => {
|
|
|
27
29
|
}
|
|
28
30
|
// Read all files in parallel
|
|
29
31
|
logVerbose(verbose, "Reading files...");
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
let projectGraph;
|
|
33
|
+
try {
|
|
34
|
+
const readQueue = new PQueue({ concurrency: workers });
|
|
35
|
+
const parsedFiles = await Promise.all(sourceFilePaths.map((filePath) => readQueue.add(async () => {
|
|
36
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
37
|
+
return { path: filePath, content };
|
|
38
|
+
})));
|
|
39
|
+
// Build project graph (includes parsing)
|
|
40
|
+
logVerbose(verbose, "Parsing files with Babel...");
|
|
41
|
+
projectGraph = buildProjectGraph(parsedFiles);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
45
|
+
return {
|
|
46
|
+
filesProcessed: 0,
|
|
47
|
+
totalTransformations: 0,
|
|
48
|
+
errors: [message],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
37
51
|
logVerbose(verbose, `Running ${transforms.length} transform(s) with ${workers} worker(s)...`);
|
|
38
52
|
// Create a queue for parallel processing
|
|
39
53
|
const queue = new PQueue({ concurrency: workers });
|
package/dist/cli.js
CHANGED
|
@@ -1,44 +1,121 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "@commander-js/extra-typings";
|
|
2
3
|
import packageJson from "../package.json" with { type: "json" };
|
|
3
|
-
import {
|
|
4
|
+
import { getTransformsToRun, getTransformSummaries } from "./cli/config.js";
|
|
5
|
+
import { runTransforms, transformStdin } from "./cli/runner.js";
|
|
6
|
+
import { getDefaultTransformOptions } from "./core/default-transform-options.js";
|
|
4
7
|
import { installStdoutErrorHandler } from "./cli/install-stdout-error-handler.js";
|
|
5
|
-
import {
|
|
6
|
-
import { runStdinMode } from "./cli/run-stdin-mode.js";
|
|
7
|
-
import { writeTransformSummaries } from "./cli/write-transform-summaries.js";
|
|
8
|
-
const program = createProgram({
|
|
9
|
-
name: packageJson.name,
|
|
10
|
-
description: packageJson.description,
|
|
11
|
-
version: packageJson.version,
|
|
12
|
-
});
|
|
8
|
+
import { normalizeArgvForCommander, parseIgnoredDirectories, parseListFormat, parsePositiveInt, shouldUnsafeStabilize, } from "./cli/parse-cli-options.js";
|
|
13
9
|
installStdoutErrorHandler();
|
|
14
|
-
const
|
|
10
|
+
const program = new Command()
|
|
11
|
+
.name(packageJson.name)
|
|
12
|
+
.description(packageJson.description)
|
|
13
|
+
.version(packageJson.version)
|
|
14
|
+
.showHelpAfterError("(add --help for additional information)")
|
|
15
|
+
.showSuggestionAfterError()
|
|
16
|
+
.option("-v, --verbose", "Enable verbose output")
|
|
17
|
+
.option("-i, --input <path>", "Input file or directory (default: stdin)")
|
|
18
|
+
.option("-o, --output <path>", "Output directory (required when using --input)")
|
|
19
|
+
.option("-t, --transforms <list>", "Comma-separated list of transforms to run (or preset: recommended, all, none)")
|
|
20
|
+
.option("--list-transforms", "List available transforms")
|
|
21
|
+
.option("--format <format>", "Output format for --list-transforms: text, tsv, json", parseListFormat, "text")
|
|
22
|
+
.option("--dry-run", "Show what would be changed without writing files")
|
|
23
|
+
.option("-w, --workers <n>", "Number of parallel workers", parsePositiveInt, 4)
|
|
24
|
+
.option("-f, --overwrite", "Overwrite existing files in output directory")
|
|
25
|
+
.option("--ignore-dirs <list>", "Comma-separated directory names to ignore while scanning --input directories", parseIgnoredDirectories)
|
|
26
|
+
.option("--safe-stabilize-top-level-bindings", "Make stabilize-top-level-bindings bail out on dynamic-name hazards (safer; output more likely runnable)");
|
|
27
|
+
program.addHelpText("after", `
|
|
28
|
+
Examples:
|
|
29
|
+
# Read from stdin, write to stdout (filter mode)
|
|
30
|
+
cat bundle.min.js | miniread > bundle.js
|
|
31
|
+
|
|
32
|
+
# Process a directory into an output directory
|
|
33
|
+
miniread --input ./minified --output ./readable
|
|
34
|
+
|
|
35
|
+
# Use the recommended preset explicitly
|
|
36
|
+
miniread --input ./minified --output ./readable --transforms recommended
|
|
37
|
+
|
|
38
|
+
# Run all transforms (including non-recommended ones)
|
|
39
|
+
miniread --input ./minified --output ./readable --transforms all
|
|
40
|
+
|
|
41
|
+
# List available transforms (one per line)
|
|
42
|
+
miniread --list-transforms
|
|
43
|
+
|
|
44
|
+
# Pipeline example: find frequent identifiers after transforming
|
|
45
|
+
cat bundle.min.js | miniread | grep -oE '[A-Za-z_][A-Za-z0-9_]+' | sort | uniq -c | sort -rn | head -20
|
|
46
|
+
`);
|
|
47
|
+
program.parse(normalizeArgvForCommander(process.argv));
|
|
48
|
+
const options = program.opts();
|
|
15
49
|
if (options.listTransforms) {
|
|
16
|
-
|
|
50
|
+
const summaries = getTransformSummaries();
|
|
51
|
+
if (options.format === "json") {
|
|
52
|
+
process.stdout.write(JSON.stringify(summaries, undefined, 2) + "\n");
|
|
53
|
+
}
|
|
54
|
+
else if (options.format === "tsv") {
|
|
55
|
+
process.stdout.write("ID\tDESCRIPTION\tSCOPE\tPARALLELIZABLE\n");
|
|
56
|
+
for (const summary of summaries) {
|
|
57
|
+
process.stdout.write(`${summary.id}\t${summary.description}\t${summary.scope}\t${summary.parallelizable}\n`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
for (const summary of summaries) {
|
|
62
|
+
process.stdout.write(`${summary.id}\n`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
17
65
|
}
|
|
18
66
|
else if (options.input) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
process.exit(
|
|
67
|
+
if (!options.output) {
|
|
68
|
+
program.error("--output is required when using --input");
|
|
69
|
+
process.exit(1);
|
|
22
70
|
}
|
|
23
|
-
|
|
71
|
+
const transformsResult = getTransformsToRun(options.transforms);
|
|
72
|
+
if (!transformsResult.ok) {
|
|
73
|
+
program.error(transformsResult.error);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const result = await runTransforms({
|
|
77
|
+
input: options.input,
|
|
78
|
+
output: options.output,
|
|
79
|
+
transforms: transformsResult.transforms,
|
|
80
|
+
transformOptions: {
|
|
81
|
+
...getDefaultTransformOptions(),
|
|
82
|
+
unsafeStabilizeTopLevelBindings: shouldUnsafeStabilize(options.safeStabilizeTopLevelBindings),
|
|
83
|
+
},
|
|
84
|
+
dryRun: options.dryRun ?? false,
|
|
85
|
+
workers: options.workers,
|
|
86
|
+
overwrite: options.overwrite ?? false,
|
|
87
|
+
ignoreDirectories: options.ignoreDirs,
|
|
88
|
+
verbose: options.verbose ?? false,
|
|
89
|
+
});
|
|
90
|
+
if (result.errors.length > 0) {
|
|
24
91
|
console.error("Errors:");
|
|
25
|
-
for (const error of result.
|
|
92
|
+
for (const error of result.errors) {
|
|
26
93
|
console.error(`- ${error}`);
|
|
27
94
|
}
|
|
28
95
|
console.error(`Try '${packageJson.name} --help' for details.`);
|
|
29
96
|
process.exit(1);
|
|
30
97
|
}
|
|
31
|
-
program.error(result.error);
|
|
32
|
-
}
|
|
33
|
-
else if (options.output) {
|
|
34
|
-
program.error("--output cannot be used without --input");
|
|
35
98
|
}
|
|
36
99
|
else {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
process.stdout.write(result.output);
|
|
100
|
+
if (options.output) {
|
|
101
|
+
program.error("--output cannot be used without --input");
|
|
40
102
|
}
|
|
41
|
-
|
|
42
|
-
|
|
103
|
+
const transformsResult = getTransformsToRun(options.transforms);
|
|
104
|
+
if (!transformsResult.ok) {
|
|
105
|
+
program.error(transformsResult.error);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
const result = await transformStdin({
|
|
109
|
+
transforms: transformsResult.transforms,
|
|
110
|
+
transformOptions: {
|
|
111
|
+
...getDefaultTransformOptions(),
|
|
112
|
+
unsafeStabilizeTopLevelBindings: shouldUnsafeStabilize(options.safeStabilizeTopLevelBindings),
|
|
113
|
+
},
|
|
114
|
+
verbose: options.verbose ?? false,
|
|
115
|
+
});
|
|
116
|
+
if (!result.ok) {
|
|
117
|
+
console.error(`Error: ${result.error}`);
|
|
118
|
+
process.exit(1);
|
|
43
119
|
}
|
|
120
|
+
process.stdout.write(result.output);
|
|
44
121
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from "@commander-js/extra-typings";
|
|
2
|
+
export type PickRandomTransformCliRawOptions = {
|
|
3
|
+
verbose: boolean;
|
|
4
|
+
evaluationKey: string;
|
|
5
|
+
transformsDirectory: string;
|
|
6
|
+
top: number;
|
|
7
|
+
pick: number;
|
|
8
|
+
};
|
|
9
|
+
export declare const createPickRandomTransformCommand: (options: {
|
|
10
|
+
version: string;
|
|
11
|
+
}) => Command;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Command, InvalidArgumentError } from "@commander-js/extra-typings";
|
|
2
|
+
const parsePositiveInt = (rawValue) => {
|
|
3
|
+
const value = Number(rawValue);
|
|
4
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
5
|
+
throw new InvalidArgumentError(`invalid value '${rawValue}' (expected a positive integer)`);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
};
|
|
9
|
+
export const createPickRandomTransformCommand = (options) => {
|
|
10
|
+
const program = new Command()
|
|
11
|
+
.name("miniread-pick-random-transform")
|
|
12
|
+
.description("Pick random transform candidates from the slowest recommended transforms.")
|
|
13
|
+
.version(options.version)
|
|
14
|
+
.showHelpAfterError("(add --help for additional information)")
|
|
15
|
+
.showSuggestionAfterError()
|
|
16
|
+
.option("-v, --verbose", "Enable verbose output")
|
|
17
|
+
.option("--evaluation-key <key>", "Evaluation key used to read duration metrics from manifest.json", "claude-code-2.1.10:claude-code-2.1.11")
|
|
18
|
+
.option("--transforms-directory <path>", "Directory containing transform subdirectories", "src/transforms")
|
|
19
|
+
.option("--top <count>", "How many slowest eligible transforms form the candidate pool", parsePositiveInt, 10)
|
|
20
|
+
.option("--pick <count>", "How many transforms to randomly pick from the top pool", parsePositiveInt, 3);
|
|
21
|
+
program.addHelpText("after", `
|
|
22
|
+
Examples:
|
|
23
|
+
# Pick 3 candidates from the 10 slowest recommended transforms
|
|
24
|
+
pnpm run miniread-pick-random-transform
|
|
25
|
+
|
|
26
|
+
# Use a different evaluation key and pool size
|
|
27
|
+
pnpm run miniread-pick-random-transform -- --evaluation-key claude-code-2.1.11:claude-code-2.1.12 --top 15 --pick 3
|
|
28
|
+
`);
|
|
29
|
+
return program;
|
|
30
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const parseManifest = (manifestPath) => {
|
|
4
|
+
const content = readFileSync(manifestPath, "utf8");
|
|
5
|
+
const parsed = JSON.parse(content);
|
|
6
|
+
return {
|
|
7
|
+
recommended: parsed.recommended,
|
|
8
|
+
supersededBy: parsed.supersededBy,
|
|
9
|
+
evaluations: parsed.evaluations,
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const parseNumber = (value) => {
|
|
13
|
+
if (typeof value !== "number")
|
|
14
|
+
return undefined;
|
|
15
|
+
if (!Number.isFinite(value))
|
|
16
|
+
return undefined;
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
export const loadTransformPerformanceEntries = (options) => {
|
|
20
|
+
const { transformsDirectory, evaluationKey } = options;
|
|
21
|
+
const directoryEntries = readdirSync(transformsDirectory, {
|
|
22
|
+
withFileTypes: true,
|
|
23
|
+
});
|
|
24
|
+
const performanceEntries = [];
|
|
25
|
+
for (const directoryEntry of directoryEntries) {
|
|
26
|
+
if (!directoryEntry.isDirectory())
|
|
27
|
+
continue;
|
|
28
|
+
const transformId = directoryEntry.name;
|
|
29
|
+
const manifestPath = path.join(transformsDirectory, transformId, "manifest.json");
|
|
30
|
+
let manifest;
|
|
31
|
+
try {
|
|
32
|
+
manifest = parseManifest(manifestPath);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const code = error instanceof Error && "code" in error ? error.code : undefined;
|
|
36
|
+
if (code === "ENOENT")
|
|
37
|
+
continue;
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
throw new Error(`Failed to parse ${manifestPath}: ${message}`);
|
|
40
|
+
}
|
|
41
|
+
const evaluation = manifest.evaluations?.[evaluationKey];
|
|
42
|
+
performanceEntries.push({
|
|
43
|
+
transformId,
|
|
44
|
+
recommended: manifest.recommended === true,
|
|
45
|
+
supersededBy: typeof manifest.supersededBy === "string"
|
|
46
|
+
? manifest.supersededBy
|
|
47
|
+
: undefined,
|
|
48
|
+
durationSeconds: parseNumber(evaluation?.durationSeconds),
|
|
49
|
+
changedLines: parseNumber(evaluation?.changedLines),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return performanceEntries;
|
|
53
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PickRandomTransformCliRawOptions } from "./create-pick-random-transform-command.js";
|
|
2
|
+
export type PickRandomTransformCliOptions = {
|
|
3
|
+
verbose: boolean;
|
|
4
|
+
evaluationKey: string;
|
|
5
|
+
transformsDirectory: string;
|
|
6
|
+
topCount: number;
|
|
7
|
+
pickCount: number;
|
|
8
|
+
};
|
|
9
|
+
export declare const parsePickRandomTransformCliOptions: (options: {
|
|
10
|
+
cwd: string;
|
|
11
|
+
rawOptions: PickRandomTransformCliRawOptions;
|
|
12
|
+
}) => PickRandomTransformCliOptions;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export const parsePickRandomTransformCliOptions = (options) => {
|
|
3
|
+
const { cwd, rawOptions } = options;
|
|
4
|
+
const evaluationKey = rawOptions.evaluationKey.trim();
|
|
5
|
+
if (evaluationKey.length === 0) {
|
|
6
|
+
throw new Error("--evaluation-key must be a non-empty string");
|
|
7
|
+
}
|
|
8
|
+
const topCount = rawOptions.top;
|
|
9
|
+
const pickCount = rawOptions.pick;
|
|
10
|
+
if (pickCount > topCount) {
|
|
11
|
+
throw new Error("--pick must be less than or equal to --top");
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
verbose: rawOptions.verbose,
|
|
15
|
+
evaluationKey,
|
|
16
|
+
transformsDirectory: path.resolve(cwd, rawOptions.transformsDirectory),
|
|
17
|
+
topCount,
|
|
18
|
+
pickCount,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type RandomIntGenerator = {
|
|
2
|
+
nextInt: (maxExclusive: number) => number;
|
|
3
|
+
};
|
|
4
|
+
export type TransformPerformanceEntry = {
|
|
5
|
+
transformId: string;
|
|
6
|
+
recommended: boolean;
|
|
7
|
+
supersededBy: string | undefined;
|
|
8
|
+
durationSeconds: number | undefined;
|
|
9
|
+
changedLines: number | undefined;
|
|
10
|
+
};
|
|
11
|
+
type TransformCandidate = {
|
|
12
|
+
transformId: string;
|
|
13
|
+
durationSeconds: number;
|
|
14
|
+
changedLines: number;
|
|
15
|
+
};
|
|
16
|
+
type PickRandomTransformCandidatesResult = {
|
|
17
|
+
candidatePool: TransformCandidate[];
|
|
18
|
+
candidates: TransformCandidate[];
|
|
19
|
+
};
|
|
20
|
+
export declare const pickRandomTransformCandidates: (options: {
|
|
21
|
+
entries: TransformPerformanceEntry[];
|
|
22
|
+
topCount: number;
|
|
23
|
+
pickCount: number;
|
|
24
|
+
rng: RandomIntGenerator;
|
|
25
|
+
}) => PickRandomTransformCandidatesResult;
|
|
26
|
+
export {};
|