miniread 1.3.0 → 1.4.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.
Files changed (55) hide show
  1. package/dist/cli/execute-transforms.d.ts +9 -0
  2. package/dist/cli/execute-transforms.js +39 -0
  3. package/dist/cli/run-transforms.js +17 -27
  4. package/dist/core/types.d.ts +6 -0
  5. package/dist/core/types.js +11 -1
  6. package/dist/scripts/evaluate/run-evaluations.js +7 -2
  7. package/dist/transforms/expand-boolean-literals/expand-boolean-literals-transform.d.ts +1 -1
  8. package/dist/transforms/expand-boolean-literals/expand-boolean-literals-transform.js +2 -2
  9. package/dist/transforms/expand-return-sequence/expand-return-sequence-transform.d.ts +1 -1
  10. package/dist/transforms/expand-return-sequence/expand-return-sequence-transform.js +2 -2
  11. package/dist/transforms/expand-sequence-expressions/expand-sequence-expressions-transform.d.ts +1 -1
  12. package/dist/transforms/expand-sequence-expressions/expand-sequence-expressions-transform.js +2 -2
  13. package/dist/transforms/expand-sequence-expressions-v2/expand-sequence-expressions-v2-transform.d.ts +1 -1
  14. package/dist/transforms/expand-sequence-expressions-v2/expand-sequence-expressions-v2-transform.js +2 -2
  15. package/dist/transforms/expand-sequence-expressions-v3/expand-sequence-expressions-v3-transform.d.ts +1 -1
  16. package/dist/transforms/expand-sequence-expressions-v3/expand-sequence-expressions-v3-transform.js +2 -2
  17. package/dist/transforms/expand-special-number-literals/expand-special-number-literals-transform.js +2 -2
  18. package/dist/transforms/expand-throw-sequence/expand-throw-sequence-transform.d.ts +1 -1
  19. package/dist/transforms/expand-throw-sequence/expand-throw-sequence-transform.js +2 -2
  20. package/dist/transforms/expand-undefined-literals/expand-undefined-literals-transform.d.ts +1 -1
  21. package/dist/transforms/expand-undefined-literals/expand-undefined-literals-transform.js +2 -2
  22. package/dist/transforms/rename-catch-parameters/rename-catch-parameters-transform.d.ts +1 -1
  23. package/dist/transforms/rename-catch-parameters/rename-catch-parameters-transform.js +2 -2
  24. package/dist/transforms/rename-destructured-aliases/rename-destructured-aliases-transform.d.ts +1 -1
  25. package/dist/transforms/rename-destructured-aliases/rename-destructured-aliases-transform.js +2 -2
  26. package/dist/transforms/rename-event-parameters/can-rename-binding-safely.d.ts +2 -0
  27. package/dist/transforms/rename-event-parameters/can-rename-binding-safely.js +28 -0
  28. package/dist/transforms/rename-event-parameters/event-parameter-usage.d.ts +9 -0
  29. package/dist/transforms/rename-event-parameters/event-parameter-usage.js +72 -0
  30. package/dist/transforms/rename-event-parameters/event-property-names.d.ts +3 -0
  31. package/dist/transforms/rename-event-parameters/event-property-names.js +43 -0
  32. package/dist/transforms/rename-event-parameters/get-member-expression-for-reference.d.ts +3 -0
  33. package/dist/transforms/rename-event-parameters/get-member-expression-for-reference.js +10 -0
  34. package/dist/transforms/rename-event-parameters/get-target-event-name.d.ts +2 -0
  35. package/dist/transforms/rename-event-parameters/get-target-event-name.js +20 -0
  36. package/dist/transforms/rename-event-parameters/is-valid-binding-identifier.d.ts +1 -0
  37. package/dist/transforms/rename-event-parameters/is-valid-binding-identifier.js +10 -0
  38. package/dist/transforms/rename-event-parameters/process-event-handler-function.d.ts +5 -0
  39. package/dist/transforms/rename-event-parameters/process-event-handler-function.js +59 -0
  40. package/dist/transforms/rename-event-parameters/rename-event-parameters-transform.d.ts +2 -0
  41. package/dist/transforms/rename-event-parameters/rename-event-parameters-transform.js +36 -0
  42. package/dist/transforms/rename-event-parameters/would-shadow-referenced-outer-binding.d.ts +3 -0
  43. package/dist/transforms/rename-event-parameters/would-shadow-referenced-outer-binding.js +21 -0
  44. package/dist/transforms/rename-loop-index-variables/rename-loop-index-variables-transform.d.ts +1 -1
  45. package/dist/transforms/rename-loop-index-variables/rename-loop-index-variables-transform.js +2 -2
  46. package/dist/transforms/rename-promise-executor-parameters/rename-promise-executor-parameters-transform.d.ts +1 -1
  47. package/dist/transforms/rename-promise-executor-parameters/rename-promise-executor-parameters-transform.js +2 -2
  48. package/dist/transforms/rename-timeout-ids/rename-timeout-ids-transform.js +2 -2
  49. package/dist/transforms/rename-use-reference-guards/rename-use-reference-guards-transform.d.ts +1 -1
  50. package/dist/transforms/rename-use-reference-guards/rename-use-reference-guards-transform.js +2 -2
  51. package/dist/transforms/split-variable-declarations/split-variable-declarations-transform.d.ts +1 -1
  52. package/dist/transforms/split-variable-declarations/split-variable-declarations-transform.js +2 -2
  53. package/dist/transforms/transform-registry.js +2 -0
  54. package/package.json +1 -1
  55. package/transform-manifest.json +9 -0
@@ -0,0 +1,9 @@
1
+ import type PQueue from "p-queue";
2
+ import type { Transform, ProjectGraph } from "../core/types.js";
3
+ type TransformExecutionResult = {
4
+ totalTransformations: number;
5
+ error?: string;
6
+ };
7
+ type VerboseLogger = (message: string) => void;
8
+ export declare const executeTransforms: (transforms: Transform[], projectGraph: ProjectGraph, queue: PQueue, log: VerboseLogger) => Promise<TransformExecutionResult>;
9
+ export {};
@@ -0,0 +1,39 @@
1
+ export const executeTransforms = async (transforms, projectGraph, queue, log) => {
2
+ let totalTransformations = 0;
3
+ for (const transform of transforms) {
4
+ log(`Running transform: ${transform.id}`);
5
+ try {
6
+ if (transform.scope === "file" && transform.parallelizable) {
7
+ // Run transform on each file in parallel
8
+ const fileInfos = [...projectGraph.files.values()];
9
+ const statsPromises = fileInfos.map((fileInfo) => queue.add(() => transform.transform({
10
+ projectGraph,
11
+ currentFile: fileInfo,
12
+ options: {},
13
+ })));
14
+ const allStats = await Promise.all(statsPromises);
15
+ const transformApplied = allStats.reduce((sum, stats) => sum + stats.transformationsApplied, 0);
16
+ totalTransformations += transformApplied;
17
+ log(`Applied ${transformApplied} transformation(s)`);
18
+ }
19
+ else {
20
+ // Run transform on all files at once (project-scoped or non-parallelizable)
21
+ const stats = await transform.transform({
22
+ projectGraph,
23
+ currentFile: undefined,
24
+ options: {},
25
+ });
26
+ totalTransformations += stats.transformationsApplied;
27
+ log(`Applied ${stats.transformationsApplied} transformation(s)`);
28
+ }
29
+ }
30
+ catch (error) {
31
+ const message = error instanceof Error ? error.message : String(error);
32
+ return {
33
+ totalTransformations,
34
+ error: `Transform '${transform.id}' failed: ${message}`,
35
+ };
36
+ }
37
+ }
38
+ return { totalTransformations };
39
+ };
@@ -4,6 +4,7 @@ import PQueue from "p-queue";
4
4
  import { buildProjectGraph } from "../core/project-graph.js";
5
5
  import { printDryRunSummary, writeOutputFiles } from "./output.js";
6
6
  import { generateCode } from "./generate-code.js";
7
+ import { executeTransforms } from "./execute-transforms.js";
7
8
  const logVerbose = (verbose, message) => {
8
9
  if (!verbose)
9
10
  return;
@@ -75,42 +76,31 @@ export const runTransforms = async (options) => {
75
76
  if (otherFilePaths.length > 0) {
76
77
  logVerbose(verbose, `Found ${otherFilePaths.length} other file(s) to copy`);
77
78
  }
78
- // Read all files
79
+ // Read all files in parallel
79
80
  logVerbose(verbose, "Reading files...");
80
- const parsedFiles = [];
81
- for (const filePath of sourceFilePaths) {
81
+ const parsedFiles = await Promise.all(sourceFilePaths.map(async (filePath) => {
82
82
  const content = await fs.readFile(filePath, "utf8");
83
- parsedFiles.push({ path: filePath, content });
84
- }
83
+ return { path: filePath, content };
84
+ }));
85
85
  // Build project graph (includes parsing)
86
86
  logVerbose(verbose, "Parsing files with Babel...");
87
87
  const projectGraph = buildProjectGraph(parsedFiles);
88
88
  logVerbose(verbose, `Running ${transforms.length} transform(s) with ${workers} worker(s)...`);
89
89
  // Create a queue for parallel processing
90
90
  const queue = new PQueue({ concurrency: workers });
91
- // Track results
92
- let totalTransformations = 0;
93
- // Run transforms sequentially - each mutates the AST in place
94
- for (const transform of transforms) {
95
- logVerbose(verbose, `Running transform: ${transform.id}`);
96
- try {
97
- const stats = await transform.transform({
98
- projectGraph,
99
- currentFile: undefined,
100
- options: {},
101
- });
102
- totalTransformations += stats.transformationsApplied;
103
- logVerbose(verbose, `Applied ${stats.transformationsApplied} transformation(s)`);
104
- }
105
- catch (error) {
106
- const message = error instanceof Error ? error.message : String(error);
107
- return {
108
- filesProcessed: 0,
109
- totalTransformations,
110
- errors: [`Transform '${transform.id}' failed: ${message}`],
111
- };
112
- }
91
+ // Run transforms - parallelize file-scoped parallelizable transforms
92
+ const log = (message) => {
93
+ logVerbose(verbose, message);
94
+ };
95
+ const result = await executeTransforms(transforms, projectGraph, queue, log);
96
+ if (result.error) {
97
+ return {
98
+ filesProcessed: 0,
99
+ totalTransformations: result.totalTransformations,
100
+ errors: [result.error],
101
+ };
113
102
  }
103
+ const totalTransformations = result.totalTransformations;
114
104
  // Generate final code from ASTs using @babel/generator
115
105
  const finalFiles = new Map();
116
106
  for (const [filePath, fileInfo] of projectGraph.files) {
@@ -28,3 +28,9 @@ export type Transform = {
28
28
  parallelizable: boolean;
29
29
  transform: (context: TransformContext) => Promise<TransformStats>;
30
30
  };
31
+ /**
32
+ * Returns the files to process based on the context.
33
+ * If currentFile is set, returns only that file.
34
+ * Otherwise, returns all files from the project graph.
35
+ */
36
+ export declare const getFilesToProcess: (context: TransformContext) => SourceFileInfo[];
@@ -1 +1,11 @@
1
- export {};
1
+ /**
2
+ * Returns the files to process based on the context.
3
+ * If currentFile is set, returns only that file.
4
+ * Otherwise, returns all files from the project graph.
5
+ */
6
+ export const getFilesToProcess = (context) => {
7
+ if (context.currentFile) {
8
+ return [context.currentFile];
9
+ }
10
+ return [...context.projectGraph.files.values()];
11
+ };
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import PQueue from "p-queue";
2
3
  import { evaluatePair } from "./pair-evaluator.js";
3
4
  import { sanitizeFileComponent } from "./sanitize.js";
4
5
  export const runEvaluations = async (options) => {
@@ -6,7 +7,10 @@ export const runEvaluations = async (options) => {
6
7
  const results = [];
7
8
  const errors = [];
8
9
  const outcomes = [];
9
- for (const pair of pairs) {
10
+ // Run pairs in parallel with limited concurrency
11
+ // Each pair spawns multiple subprocesses, so limit to 2 concurrent pairs
12
+ const queue = new PQueue({ concurrency: 2 });
13
+ const evaluationPromises = pairs.map((pair) => queue.add(async () => {
10
14
  const safeFrom = sanitizeFileComponent(pair.from);
11
15
  const safeTo = sanitizeFileComponent(pair.to);
12
16
  const filePrefix = `metrics-${baselineSlug}-vs-${testSlug}_${safeFrom}_${safeTo}`;
@@ -46,6 +50,7 @@ export const runEvaluations = async (options) => {
46
50
  outcomes.push({ ok: false, pair, error: message });
47
51
  console.error(`Error evaluating ${pair.from} -> ${pair.to}: ${message}`);
48
52
  }
49
- }
53
+ }));
54
+ await Promise.all(evaluationPromises);
50
55
  return { results, errors, outcomes };
51
56
  };
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const expandBooleanLiteralsTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  const require = createRequire(import.meta.url);
3
4
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
5
  const traverse = require("@babel/traverse").default;
@@ -10,10 +11,9 @@ export const expandBooleanLiteralsTransform = {
10
11
  scope: "file",
11
12
  parallelizable: true,
12
13
  transform(context) {
13
- const { projectGraph } = context;
14
14
  let nodesVisited = 0;
15
15
  let transformationsApplied = 0;
16
- for (const [, fileInfo] of projectGraph.files) {
16
+ for (const fileInfo of getFilesToProcess(context)) {
17
17
  traverse(fileInfo.ast, {
18
18
  UnaryExpression(path) {
19
19
  nodesVisited++;
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const expandReturnSequenceTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  const require = createRequire(import.meta.url);
3
4
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
5
  const traverse = require("@babel/traverse").default;
@@ -51,10 +52,9 @@ export const expandReturnSequenceTransform = {
51
52
  scope: "file",
52
53
  parallelizable: true,
53
54
  transform(context) {
54
- const { projectGraph } = context;
55
55
  let nodesVisited = 0;
56
56
  let transformationsApplied = 0;
57
- for (const [, fileInfo] of projectGraph.files) {
57
+ for (const fileInfo of getFilesToProcess(context)) {
58
58
  traverse(fileInfo.ast, {
59
59
  ReturnStatement(path) {
60
60
  nodesVisited++;
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const expandSequenceExpressionsTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  import { tryExpandExpressionStatementSequence } from "./expand-expression-statement-sequence.js";
3
4
  import { tryExpandVariableDeclarationSequenceInitializers } from "./expand-variable-declaration-sequence.js";
4
5
  const require = createRequire(import.meta.url);
@@ -10,10 +11,9 @@ export const expandSequenceExpressionsTransform = {
10
11
  scope: "file",
11
12
  parallelizable: true,
12
13
  transform(context) {
13
- const { projectGraph } = context;
14
14
  let nodesVisited = 0;
15
15
  let transformationsApplied = 0;
16
- for (const [, fileInfo] of projectGraph.files) {
16
+ for (const fileInfo of getFilesToProcess(context)) {
17
17
  traverse(fileInfo.ast, {
18
18
  ExpressionStatement(path) {
19
19
  nodesVisited++;
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const expandSequenceExpressionsV2Transform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  import { tryExpandExpressionStatementSequence } from "./expand-expression-statement-sequence.js";
3
4
  import { tryExpandReturnSequence } from "./expand-return-sequence.js";
4
5
  import { tryExpandVariableDeclarationSequenceInitializers } from "./expand-variable-declaration-sequence.js";
@@ -11,10 +12,9 @@ export const expandSequenceExpressionsV2Transform = {
11
12
  scope: "file",
12
13
  parallelizable: true,
13
14
  transform(context) {
14
- const { projectGraph } = context;
15
15
  let nodesVisited = 0;
16
16
  let transformationsApplied = 0;
17
- for (const [, fileInfo] of projectGraph.files) {
17
+ for (const fileInfo of getFilesToProcess(context)) {
18
18
  traverse(fileInfo.ast, {
19
19
  ReturnStatement(path) {
20
20
  nodesVisited++;
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const expandSequenceExpressionsV3Transform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  import { tryExpandExpressionStatementSequence } from "./expand-expression-statement-sequence.js";
3
4
  import { tryExpandReturnSequence } from "./expand-return-sequence.js";
4
5
  import { tryExpandThrowSequence } from "./expand-throw-sequence.js";
@@ -12,10 +13,9 @@ export const expandSequenceExpressionsV3Transform = {
12
13
  scope: "file",
13
14
  parallelizable: true,
14
15
  transform(context) {
15
- const { projectGraph } = context;
16
16
  let nodesVisited = 0;
17
17
  let transformationsApplied = 0;
18
- for (const [, fileInfo] of projectGraph.files) {
18
+ for (const fileInfo of getFilesToProcess(context)) {
19
19
  traverse(fileInfo.ast, {
20
20
  ReturnStatement(path) {
21
21
  nodesVisited++;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess } from "../../core/types.js";
2
3
  const require = createRequire(import.meta.url);
3
4
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
5
  const traverse = require("@babel/traverse").default;
@@ -59,10 +60,9 @@ export const expandSpecialNumberLiteralsTransform = {
59
60
  scope: "file",
60
61
  parallelizable: true,
61
62
  transform(context) {
62
- const { projectGraph } = context;
63
63
  let nodesVisited = 0;
64
64
  let transformationsApplied = 0;
65
- for (const [, fileInfo] of projectGraph.files) {
65
+ for (const fileInfo of getFilesToProcess(context)) {
66
66
  traverse(fileInfo.ast, {
67
67
  BinaryExpression(path) {
68
68
  nodesVisited++;
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const expandThrowSequenceTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  const require = createRequire(import.meta.url);
3
4
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
5
  const traverse = require("@babel/traverse").default;
@@ -99,10 +100,9 @@ export const expandThrowSequenceTransform = {
99
100
  scope: "file",
100
101
  parallelizable: true,
101
102
  transform(context) {
102
- const { projectGraph } = context;
103
103
  let nodesVisited = 0;
104
104
  let transformationsApplied = 0;
105
- for (const [, fileInfo] of projectGraph.files) {
105
+ for (const fileInfo of getFilesToProcess(context)) {
106
106
  traverse(fileInfo.ast, {
107
107
  ThrowStatement(path) {
108
108
  nodesVisited++;
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const expandUndefinedLiteralsTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  const require = createRequire(import.meta.url);
3
4
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
5
  const traverse = require("@babel/traverse").default;
@@ -10,10 +11,9 @@ export const expandUndefinedLiteralsTransform = {
10
11
  scope: "file",
11
12
  parallelizable: true,
12
13
  transform(context) {
13
- const { projectGraph } = context;
14
14
  let nodesVisited = 0;
15
15
  let transformationsApplied = 0;
16
- for (const [, fileInfo] of projectGraph.files) {
16
+ for (const fileInfo of getFilesToProcess(context)) {
17
17
  traverse(fileInfo.ast, {
18
18
  UnaryExpression(path) {
19
19
  nodesVisited++;
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const renameCatchParametersTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  const require = createRequire(import.meta.url);
3
4
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
5
  const traverse = require("@babel/traverse").default;
@@ -36,10 +37,9 @@ export const renameCatchParametersTransform = {
36
37
  scope: "file",
37
38
  parallelizable: true,
38
39
  transform(context) {
39
- const { projectGraph } = context;
40
40
  let nodesVisited = 0;
41
41
  let transformationsApplied = 0;
42
- for (const [, fileInfo] of projectGraph.files) {
42
+ for (const fileInfo of getFilesToProcess(context)) {
43
43
  const renamedCatches = new WeakMap();
44
44
  traverse(fileInfo.ast, {
45
45
  CatchClause(path) {
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const renameDestructuredAliasesTransform: Transform;
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { isIdentifierName, isKeyword, isStrictBindReservedWord, } from "@babel/helper-validator-identifier";
3
+ import { getFilesToProcess, } from "../../core/types.js";
3
4
  import { isBindingContext } from "./binding-context.js";
4
5
  const require = createRequire(import.meta.url);
5
6
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -99,10 +100,9 @@ export const renameDestructuredAliasesTransform = {
99
100
  scope: "file",
100
101
  parallelizable: true,
101
102
  transform(context) {
102
- const { projectGraph } = context;
103
103
  let nodesVisited = 0;
104
104
  const stats = { transformationsApplied: 0 };
105
- for (const [, fileInfo] of projectGraph.files) {
105
+ for (const fileInfo of getFilesToProcess(context)) {
106
106
  traverse(fileInfo.ast, {
107
107
  ObjectPattern(path) {
108
108
  nodesVisited++;
@@ -0,0 +1,2 @@
1
+ import type { Scope } from "@babel/traverse";
2
+ export declare const canRenameBindingSafely: (bindingScope: Scope, fromName: string, toName: string) => boolean;
@@ -0,0 +1,28 @@
1
+ import { isValidBindingIdentifier } from "./is-valid-binding-identifier.js";
2
+ const hasShadowingRisk = (binding, bindingScope, targetName) => binding.referencePaths.some((referencePath) => {
3
+ if (referencePath.scope === bindingScope)
4
+ return false;
5
+ // Only consider bindings between this reference’s scope and the binding scope.
6
+ // This avoids incorrectly blocking safe renames due to unrelated outer bindings.
7
+ const hasBindingOptions = {
8
+ noGlobals: true,
9
+ noUids: true,
10
+ // Present in Babel scope implementation, but missing from our TypeScript types.
11
+ upToScope: bindingScope,
12
+ };
13
+ return referencePath.scope.hasBinding(targetName, hasBindingOptions);
14
+ });
15
+ export const canRenameBindingSafely = (bindingScope, fromName, toName) => {
16
+ if (fromName === toName)
17
+ return false;
18
+ if (!isValidBindingIdentifier(toName))
19
+ return false;
20
+ if (bindingScope.hasOwnBinding(toName))
21
+ return false;
22
+ const binding = bindingScope.getBinding(fromName);
23
+ if (!binding)
24
+ return false;
25
+ if (hasShadowingRisk(binding, bindingScope, toName))
26
+ return false;
27
+ return true;
28
+ };
@@ -0,0 +1,9 @@
1
+ import type { Binding } from "@babel/traverse";
2
+ export type EventParameterUsage = {
3
+ isSafe: boolean;
4
+ propertyNames: Set<string>;
5
+ calledMethods: Set<string>;
6
+ hasNonMemberUsage: boolean;
7
+ };
8
+ export declare const getEventParameterUsage: (binding: Binding) => EventParameterUsage;
9
+ export declare const isHighConfidenceEventParameter: (usage: EventParameterUsage) => boolean;
@@ -0,0 +1,72 @@
1
+ import { allowedEventPropertyNames } from "./event-property-names.js";
2
+ import { getMemberExpressionForReference } from "./get-member-expression-for-reference.js";
3
+ const isUnsafeNonMemberReference = (referencePath) => {
4
+ const parentPath = referencePath.parentPath;
5
+ if ((parentPath.isCallExpression() || parentPath.isOptionalCallExpression()) &&
6
+ referencePath.key === "callee")
7
+ return true;
8
+ if (parentPath.isNewExpression() && referencePath.key === "callee")
9
+ return true;
10
+ if (parentPath.isTaggedTemplateExpression() && referencePath.key === "tag")
11
+ return true;
12
+ if (parentPath.isMemberExpression() && referencePath.key === "property")
13
+ return true;
14
+ if (parentPath.isOptionalMemberExpression() &&
15
+ referencePath.key === "property")
16
+ return true;
17
+ if (parentPath.isUpdateExpression())
18
+ return true;
19
+ if (parentPath.isUnaryExpression() &&
20
+ parentPath.node.operator === "delete" &&
21
+ referencePath.key === "argument")
22
+ return true;
23
+ if (parentPath.isAssignmentExpression() && referencePath.key === "left")
24
+ return true;
25
+ return false;
26
+ };
27
+ export const getEventParameterUsage = (binding) => {
28
+ const propertyNames = new Set();
29
+ const calledMethods = new Set();
30
+ let hasNonMemberUsage = false;
31
+ for (const referencePath of binding.referencePaths) {
32
+ if (!referencePath.isIdentifier())
33
+ return { isSafe: false, propertyNames, calledMethods, hasNonMemberUsage };
34
+ const memberPath = getMemberExpressionForReference(referencePath);
35
+ if (!memberPath) {
36
+ hasNonMemberUsage = true;
37
+ if (isUnsafeNonMemberReference(referencePath)) {
38
+ return {
39
+ isSafe: false,
40
+ propertyNames,
41
+ calledMethods,
42
+ hasNonMemberUsage,
43
+ };
44
+ }
45
+ continue;
46
+ }
47
+ if (memberPath.node.computed)
48
+ return { isSafe: false, propertyNames, calledMethods, hasNonMemberUsage };
49
+ if (memberPath.node.property.type !== "Identifier")
50
+ return { isSafe: false, propertyNames, calledMethods, hasNonMemberUsage };
51
+ const propertyName = memberPath.node.property.name;
52
+ if (!allowedEventPropertyNames.has(propertyName))
53
+ return { isSafe: false, propertyNames, calledMethods, hasNonMemberUsage };
54
+ propertyNames.add(propertyName);
55
+ const parentPath = memberPath.parentPath;
56
+ if ((parentPath.isCallExpression() ||
57
+ parentPath.isOptionalCallExpression()) &&
58
+ parentPath.node.callee === memberPath.node) {
59
+ calledMethods.add(propertyName);
60
+ }
61
+ }
62
+ return { isSafe: true, propertyNames, calledMethods, hasNonMemberUsage };
63
+ };
64
+ export const isHighConfidenceEventParameter = (usage) => {
65
+ if (!usage.isSafe)
66
+ return false;
67
+ if (usage.propertyNames.size === 0)
68
+ return false;
69
+ return (usage.calledMethods.has("preventDefault") ||
70
+ usage.calledMethods.has("stopPropagation") ||
71
+ usage.calledMethods.has("stopImmediatePropagation"));
72
+ };
@@ -0,0 +1,3 @@
1
+ export declare const allowedEventPropertyNames: Set<string>;
2
+ export declare const mouseEventPropertyNames: Set<string>;
3
+ export declare const keyboardEventPropertyNames: Set<string>;
@@ -0,0 +1,43 @@
1
+ export const allowedEventPropertyNames = new Set([
2
+ // Strong signals
3
+ "preventDefault",
4
+ "stopPropagation",
5
+ "stopImmediatePropagation",
6
+ // Common event properties
7
+ "target",
8
+ "currentTarget",
9
+ "nativeEvent",
10
+ "type",
11
+ "defaultPrevented",
12
+ "timeStamp",
13
+ // Common UIEvent / MouseEvent properties
14
+ "clientX",
15
+ "clientY",
16
+ "pageX",
17
+ "pageY",
18
+ "screenX",
19
+ "screenY",
20
+ "button",
21
+ "buttons",
22
+ // KeyboardEvent properties
23
+ "key",
24
+ "code",
25
+ "keyCode",
26
+ "which",
27
+ // Modifier keys
28
+ "altKey",
29
+ "ctrlKey",
30
+ "metaKey",
31
+ "shiftKey",
32
+ ]);
33
+ export const mouseEventPropertyNames = new Set([
34
+ "clientX",
35
+ "clientY",
36
+ "pageX",
37
+ "pageY",
38
+ "screenX",
39
+ "screenY",
40
+ "button",
41
+ "buttons",
42
+ ]);
43
+ export const keyboardEventPropertyNames = new Set(["key", "code", "keyCode"]);
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { Identifier, MemberExpression, OptionalMemberExpression } from "@babel/types";
3
+ export declare const getMemberExpressionForReference: (referencePath: NodePath<Identifier>) => NodePath<MemberExpression | OptionalMemberExpression> | undefined;
@@ -0,0 +1,10 @@
1
+ export const getMemberExpressionForReference = (referencePath) => {
2
+ const parentPath = referencePath.parentPath;
3
+ if (parentPath.isMemberExpression() &&
4
+ parentPath.node.object === referencePath.node)
5
+ return parentPath;
6
+ if (parentPath.isOptionalMemberExpression() &&
7
+ parentPath.node.object === referencePath.node)
8
+ return parentPath;
9
+ return;
10
+ };
@@ -0,0 +1,2 @@
1
+ import type { EventParameterUsage } from "./event-parameter-usage.js";
2
+ export declare const getTargetEventName: (usage: EventParameterUsage) => string;
@@ -0,0 +1,20 @@
1
+ import { keyboardEventPropertyNames, mouseEventPropertyNames, } from "./event-property-names.js";
2
+ export const getTargetEventName = (usage) => {
3
+ let hasMouseProperty = false;
4
+ let hasKeyboardProperty = false;
5
+ for (const name of usage.propertyNames) {
6
+ if (!hasMouseProperty && mouseEventPropertyNames.has(name)) {
7
+ hasMouseProperty = true;
8
+ }
9
+ if (!hasKeyboardProperty && keyboardEventPropertyNames.has(name)) {
10
+ hasKeyboardProperty = true;
11
+ }
12
+ if (hasMouseProperty && hasKeyboardProperty)
13
+ break;
14
+ }
15
+ if (hasKeyboardProperty && !hasMouseProperty)
16
+ return "keyboardEvent";
17
+ if (hasMouseProperty && !hasKeyboardProperty)
18
+ return "mouseEvent";
19
+ return "event";
20
+ };
@@ -0,0 +1 @@
1
+ export declare const isValidBindingIdentifier: (name: string) => boolean;
@@ -0,0 +1,10 @@
1
+ import { isIdentifierName, isKeyword, isStrictBindReservedWord, } from "@babel/helper-validator-identifier";
2
+ export const isValidBindingIdentifier = (name) => {
3
+ if (!isIdentifierName(name))
4
+ return false;
5
+ if (isKeyword(name))
6
+ return false;
7
+ if (isStrictBindReservedWord(name, true))
8
+ return false;
9
+ return true;
10
+ };
@@ -0,0 +1,5 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { ArrowFunctionExpression, FunctionDeclaration, FunctionExpression } from "@babel/types";
3
+ export declare const processEventHandlerFunction: (path: NodePath<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression>, stats: {
4
+ transformationsApplied: number;
5
+ }) => void;
@@ -0,0 +1,59 @@
1
+ import { getEventParameterUsage, isHighConfidenceEventParameter, } from "./event-parameter-usage.js";
2
+ import { getTargetEventName } from "./get-target-event-name.js";
3
+ import { canRenameBindingSafely } from "./can-rename-binding-safely.js";
4
+ import { wouldShadowReferencedOuterBinding } from "./would-shadow-referenced-outer-binding.js";
5
+ const wouldShadowReferencedImplicitGlobal = (functionPath, targetName) => {
6
+ const programScope = functionPath.scope.getProgramParent();
7
+ if (!Object.hasOwn(programScope.globals, targetName))
8
+ return false;
9
+ let foundReference = false;
10
+ functionPath.traverse({
11
+ Identifier(identifierPath) {
12
+ if (foundReference)
13
+ return;
14
+ if (identifierPath.node.name !== targetName)
15
+ return;
16
+ if (!identifierPath.isReferencedIdentifier())
17
+ return;
18
+ if (identifierPath.scope.getBinding(targetName))
19
+ return;
20
+ foundReference = true;
21
+ identifierPath.stop();
22
+ },
23
+ });
24
+ return foundReference;
25
+ };
26
+ export const processEventHandlerFunction = (path, stats) => {
27
+ const parameters = path.node.params;
28
+ if (parameters.length !== 1)
29
+ return;
30
+ const parameter = parameters[0];
31
+ if (!parameter)
32
+ return;
33
+ if (parameter.type !== "Identifier")
34
+ return;
35
+ // 1-2 character parameter names are a strong minification signal (e.g. `e`, `t`, `WA`).
36
+ // Keeping this conservative avoids renaming common intentional abbreviations (e.g. `evt`).
37
+ if (parameter.name.length > 2)
38
+ return;
39
+ const bindingScope = path.scope;
40
+ const binding = bindingScope.getBinding(parameter.name);
41
+ if (!binding)
42
+ return;
43
+ if (!binding.constant)
44
+ return;
45
+ const usage = getEventParameterUsage(binding);
46
+ if (!isHighConfidenceEventParameter(usage))
47
+ return;
48
+ const targetName = getTargetEventName(usage);
49
+ if (parameter.name === targetName)
50
+ return;
51
+ if (wouldShadowReferencedImplicitGlobal(path, targetName))
52
+ return;
53
+ if (wouldShadowReferencedOuterBinding(path, targetName))
54
+ return;
55
+ if (!canRenameBindingSafely(bindingScope, parameter.name, targetName))
56
+ return;
57
+ bindingScope.rename(parameter.name, targetName);
58
+ stats.transformationsApplied++;
59
+ };
@@ -0,0 +1,2 @@
1
+ import type { Transform } from "../../core/types.js";
2
+ export declare const renameEventParametersTransform: Transform;
@@ -0,0 +1,36 @@
1
+ import { createRequire } from "node:module";
2
+ import { processEventHandlerFunction } from "./process-event-handler-function.js";
3
+ const require = createRequire(import.meta.url);
4
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
5
+ const traverse = require("@babel/traverse").default;
6
+ export const renameEventParametersTransform = {
7
+ id: "rename-event-parameters",
8
+ description: "Renames minified single-parameter event handlers to event/mouseEvent/keyboardEvent when usage is high-confidence",
9
+ scope: "file",
10
+ parallelizable: true,
11
+ transform(context) {
12
+ const { projectGraph } = context;
13
+ let nodesVisited = 0;
14
+ const stats = { transformationsApplied: 0 };
15
+ for (const [, fileInfo] of projectGraph.files) {
16
+ traverse(fileInfo.ast, {
17
+ FunctionDeclaration(path) {
18
+ nodesVisited++;
19
+ processEventHandlerFunction(path, stats);
20
+ },
21
+ FunctionExpression(path) {
22
+ nodesVisited++;
23
+ processEventHandlerFunction(path, stats);
24
+ },
25
+ ArrowFunctionExpression(path) {
26
+ nodesVisited++;
27
+ processEventHandlerFunction(path, stats);
28
+ },
29
+ });
30
+ }
31
+ return Promise.resolve({
32
+ nodesVisited,
33
+ transformationsApplied: stats.transformationsApplied,
34
+ });
35
+ },
36
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { ArrowFunctionExpression, FunctionDeclaration, FunctionExpression } from "@babel/types";
3
+ export declare const wouldShadowReferencedOuterBinding: (functionPath: NodePath<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression>, targetName: string) => boolean;
@@ -0,0 +1,21 @@
1
+ export const wouldShadowReferencedOuterBinding = (functionPath, targetName) => {
2
+ const outerBinding = functionPath.scope.getBinding(targetName);
3
+ if (!outerBinding)
4
+ return false;
5
+ let foundReference = false;
6
+ functionPath.traverse({
7
+ Identifier(identifierPath) {
8
+ if (foundReference)
9
+ return;
10
+ if (identifierPath.node.name !== targetName)
11
+ return;
12
+ if (!identifierPath.isReferencedIdentifier())
13
+ return;
14
+ if (identifierPath.scope.getBinding(targetName) !== outerBinding)
15
+ return;
16
+ foundReference = true;
17
+ identifierPath.stop();
18
+ },
19
+ });
20
+ return foundReference;
21
+ };
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const renameLoopIndexVariablesTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  const require = createRequire(import.meta.url);
3
4
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
5
  const traverse = require("@babel/traverse").default;
@@ -94,10 +95,9 @@ export const renameLoopIndexVariablesTransform = {
94
95
  scope: "file",
95
96
  parallelizable: true,
96
97
  transform(context) {
97
- const { projectGraph } = context;
98
98
  let nodesVisited = 0;
99
99
  let transformationsApplied = 0;
100
- for (const [, fileInfo] of projectGraph.files) {
100
+ for (const fileInfo of getFilesToProcess(context)) {
101
101
  const renamedLoops = new WeakMap();
102
102
  traverse(fileInfo.ast, {
103
103
  ForStatement(path) {
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const renamePromiseExecutorParametersTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  import { getExecutorFunctionIfEligible, isAssignedToOnHandlerProperty, isDirectCallOfBinding, wouldShadowReferencedOuterBinding, } from "./promise-executor-heuristics.js";
3
4
  import { canRenameBindingSafely, renameBindingIfSafe, } from "./rename-binding-if-safe.js";
4
5
  const require = createRequire(import.meta.url);
@@ -10,10 +11,9 @@ export const renamePromiseExecutorParametersTransform = {
10
11
  scope: "file",
11
12
  parallelizable: true,
12
13
  transform(context) {
13
- const { projectGraph } = context;
14
14
  let nodesVisited = 0;
15
15
  let transformationsApplied = 0;
16
- for (const [, fileInfo] of projectGraph.files) {
16
+ for (const fileInfo of getFilesToProcess(context)) {
17
17
  traverse(fileInfo.ast, {
18
18
  NewExpression(path) {
19
19
  nodesVisited++;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess } from "../../core/types.js";
2
3
  import { getTargetName } from "../rename-binding/get-target-name.js";
3
4
  const require = createRequire(import.meta.url);
4
5
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -43,10 +44,9 @@ export const renameTimeoutIdsTransform = {
43
44
  scope: "file",
44
45
  parallelizable: true,
45
46
  transform(context) {
46
- const { projectGraph } = context;
47
47
  let nodesVisited = 0;
48
48
  let transformationsApplied = 0;
49
- for (const [, fileInfo] of projectGraph.files) {
49
+ for (const fileInfo of getFilesToProcess(context)) {
50
50
  traverse(fileInfo.ast, {
51
51
  VariableDeclarator(path) {
52
52
  nodesVisited++;
@@ -1,3 +1,3 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  declare const renameUseReferenceGuardsTransform: Transform;
3
3
  export { renameUseReferenceGuardsTransform };
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  import { getReferenceUsage } from "./get-reference-usage.js";
3
4
  import { getTargetName } from "./get-target-name.js";
4
5
  import { isUseReferenceFalseInitializer } from "./is-use-reference-false-initializer.js";
@@ -11,10 +12,9 @@ const renameUseReferenceGuardsTransform = {
11
12
  scope: "file",
12
13
  parallelizable: true,
13
14
  transform(context) {
14
- const { projectGraph } = context;
15
15
  let nodesVisited = 0;
16
16
  let transformationsApplied = 0;
17
- for (const [, fileInfo] of projectGraph.files) {
17
+ for (const fileInfo of getFilesToProcess(context)) {
18
18
  traverse(fileInfo.ast, {
19
19
  VariableDeclarator(path) {
20
20
  nodesVisited++;
@@ -1,2 +1,2 @@
1
- import type { Transform } from "../../core/types.js";
1
+ import { type Transform } from "../../core/types.js";
2
2
  export declare const splitVariableDeclarationsTransform: Transform;
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { getFilesToProcess, } from "../../core/types.js";
2
3
  import { createVariableDeclarationFromDeclarator } from "./create-variable-declaration.js";
3
4
  import { isSafeToSplitVariableDeclaration } from "./is-safe-to-split-variable-declaration.js";
4
5
  const require = createRequire(import.meta.url);
@@ -31,10 +32,9 @@ export const splitVariableDeclarationsTransform = {
31
32
  scope: "file",
32
33
  parallelizable: true,
33
34
  transform(context) {
34
- const { projectGraph } = context;
35
35
  let nodesVisited = 0;
36
36
  let transformationsApplied = 0;
37
- for (const [, fileInfo] of projectGraph.files) {
37
+ for (const fileInfo of getFilesToProcess(context)) {
38
38
  traverse(fileInfo.ast, {
39
39
  ExportNamedDeclaration(path) {
40
40
  nodesVisited++;
@@ -8,6 +8,7 @@ import { expandThrowSequenceTransform } from "./expand-throw-sequence/expand-thr
8
8
  import { expandUndefinedLiteralsTransform } from "./expand-undefined-literals/expand-undefined-literals-transform.js";
9
9
  import { renameCatchParametersTransform } from "./rename-catch-parameters/rename-catch-parameters-transform.js";
10
10
  import { renameDestructuredAliasesTransform } from "./rename-destructured-aliases/rename-destructured-aliases-transform.js";
11
+ import { renameEventParametersTransform } from "./rename-event-parameters/rename-event-parameters-transform.js";
11
12
  import { renameLoopIndexVariablesTransform } from "./rename-loop-index-variables/rename-loop-index-variables-transform.js";
12
13
  import { renamePromiseExecutorParametersTransform } from "./rename-promise-executor-parameters/rename-promise-executor-parameters-transform.js";
13
14
  import { renameTimeoutIdsTransform } from "./rename-timeout-ids/rename-timeout-ids-transform.js";
@@ -24,6 +25,7 @@ export const transformRegistry = {
24
25
  [expandUndefinedLiteralsTransform.id]: expandUndefinedLiteralsTransform,
25
26
  [renameCatchParametersTransform.id]: renameCatchParametersTransform,
26
27
  [renameDestructuredAliasesTransform.id]: renameDestructuredAliasesTransform,
28
+ [renameEventParametersTransform.id]: renameEventParametersTransform,
27
29
  [renameLoopIndexVariablesTransform.id]: renameLoopIndexVariablesTransform,
28
30
  [renamePromiseExecutorParametersTransform.id]: renamePromiseExecutorParametersTransform,
29
31
  [renameTimeoutIdsTransform.id]: renameTimeoutIdsTransform,
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.3.0",
5
+ "version": "1.4.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",
@@ -40,6 +40,15 @@
40
40
  "evaluatedAt": "2026-01-23T07:48:59.087Z",
41
41
  "notes": "Auto-added by evaluation script."
42
42
  },
43
+ {
44
+ "id": "rename-event-parameters",
45
+ "description": "Renames minified single-parameter event handlers to event/mouseEvent/keyboardEvent when usage is high-confidence",
46
+ "scope": "file",
47
+ "parallelizable": true,
48
+ "diffReductionImpact": 0,
49
+ "recommended": true,
50
+ "notes": "Added manually; recommended based on high-confidence heuristics (not yet evaluated)."
51
+ },
43
52
  {
44
53
  "id": "rename-destructured-aliases",
45
54
  "description": "Renames destructuring aliases to match property names (e.g., { foo: x } → { foo })",