miniread 1.72.0 → 1.74.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.
@@ -99,9 +99,15 @@ const manifestData = {
99
99
  evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 99.99622028196697, "evaluatedAt": "2026-01-25T23:12:36.102Z", "changedLines": 354, "durationSeconds": 62.624233667, "stableNames": 1362 } },
100
100
  },
101
101
  "rename-default-options-parameters": {
102
- recommended: true,
102
+ recommended: false,
103
+ notes: "Superseded by rename-default-options-parameters-v2.",
104
+ supersededBy: "rename-default-options-parameters-v2",
103
105
  evaluations: { "legacy:evaluation": { "evaluatedAt": "2026-01-25T19:51:46.527Z", "changedLines": 0, "durationSeconds": 0, "stableNames": 0, "diffSizePercent": 100 }, "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-01-25T23:13:21.099Z", "changedLines": 0, "durationSeconds": 41.827803458, "stableNames": 1357 } },
104
106
  },
107
+ "rename-default-options-parameters-v2": {
108
+ recommended: true,
109
+ evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-02-06T12:59:48.597Z", "changedLines": 774, "durationSeconds": 235.023578515, "stableNames": 1358 } },
110
+ },
105
111
  "rename-deferred-resolve-parameters": {
106
112
  recommended: true,
107
113
  notes: "Measured with baseline none: 0.00%. Renames single-parameter promise executors to $resolve when usage only stores or calls the resolver.",
@@ -140,6 +146,10 @@ const manifestData = {
140
146
  notes: "Measured with baseline none: 100.00%. Added to recommended for readability.",
141
147
  evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-02-05T18:46:50.023Z", "changedLines": 4, "durationSeconds": 165.60063108, "stableNames": 1358 } },
142
148
  },
149
+ "rename-file-reader-variables": {
150
+ recommended: true,
151
+ evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-02-05T18:45:30.224Z", "changedLines": 56, "durationSeconds": 179.39064335899997, "stableNames": 1358 } },
152
+ },
143
153
  "rename-fs-sync-variables": {
144
154
  recommended: true,
145
155
  notes: "Auto-added by evaluation script. Note: rename transforms can slightly increase diff size in isolation (longer identifiers), but the goal here is stability/readability and improved alignment when composed with other transforms in the recommended preset. Safety: skips files with obvious dynamic name-lookup patterns (e.g. free `eval` / `Function` / `with`) since string-based lookups are not rewritten by renames.",
@@ -422,11 +432,12 @@ export const recommendedTransformIds = [
422
432
  "rename-client-aliases",
423
433
  "rename-comparison-flags",
424
434
  "rename-date-now-start-times",
425
- "rename-default-options-parameters",
435
+ "rename-default-options-parameters-v2",
426
436
  "rename-deferred-resolve-parameters",
427
437
  "rename-destructured-aliases",
428
438
  "rename-rest-parameters",
429
439
  "rename-execfile-arguments",
440
+ "rename-file-reader-variables",
430
441
  "rename-error-first-callback-parameters",
431
442
  "rename-error-variables",
432
443
  "rename-event-parameters",
@@ -20,6 +20,7 @@ import { renameClientAliasesTransform } from "../rename-client-aliases/rename-cl
20
20
  import { renameComparisonFlagsTransform } from "../rename-comparison-flags/rename-comparison-flags-transform.js";
21
21
  import { renameDateNowStartTimesTransform } from "../rename-date-now-start-times/rename-date-now-start-times-transform.js";
22
22
  import { renameDefaultOptionsParametersTransform } from "../rename-default-options-parameters/rename-default-options-parameters-transform.js";
23
+ import { renameDefaultOptionsParametersV2Transform } from "../rename-default-options-parameters-v2/rename-default-options-parameters-v2-transform.js";
23
24
  import { renameDeferredResolveParametersTransform } from "../rename-deferred-resolve-parameters/rename-deferred-resolve-parameters-transform.js";
24
25
  import { renameDestructuredAliasesTransform } from "../rename-destructured-aliases/rename-destructured-aliases-transform.js";
25
26
  import { renameDocumentFragmentVariablesTransform } from "../rename-document-fragment-variables/rename-document-fragment-variables-transform.js";
@@ -28,6 +29,7 @@ import { renameErrorVariablesTransform } from "../rename-error-variables/rename-
28
29
  import { renameEventParametersTransform } from "../rename-event-parameters/rename-event-parameters-transform.js";
29
30
  import { renameExecfileArgumentsTransform } from "../rename-execfile-arguments/rename-execfile-arguments-transform.js";
30
31
  import { renameFileExtensionVariablesTransform } from "../rename-file-extension-variables/rename-file-extension-variables-transform.js";
32
+ import { renameFileReaderVariablesTransform } from "../rename-file-reader-variables/rename-file-reader-variables-transform.js";
31
33
  import { renameFsSyncVariablesTransform } from "../rename-fs-sync-variables/rename-fs-sync-variables-transform.js";
32
34
  import { renameHttpMethodParametersTransform } from "../rename-http-method-parameters/rename-http-method-parameters-transform.js";
33
35
  import { renameHttpServerParametersTransform } from "../rename-http-server-parameters/rename-http-server-parameters-transform.js";
@@ -99,6 +101,7 @@ export const transformRegistry = {
99
101
  [renameComparisonFlagsTransform.id]: renameComparisonFlagsTransform,
100
102
  [renameDateNowStartTimesTransform.id]: renameDateNowStartTimesTransform,
101
103
  [renameDefaultOptionsParametersTransform.id]: renameDefaultOptionsParametersTransform,
104
+ [renameDefaultOptionsParametersV2Transform.id]: renameDefaultOptionsParametersV2Transform,
102
105
  [renameDeferredResolveParametersTransform.id]: renameDeferredResolveParametersTransform,
103
106
  [renameDestructuredAliasesTransform.id]: renameDestructuredAliasesTransform,
104
107
  [renameDocumentFragmentVariablesTransform.id]: renameDocumentFragmentVariablesTransform,
@@ -107,6 +110,7 @@ export const transformRegistry = {
107
110
  [renameEventParametersTransform.id]: renameEventParametersTransform,
108
111
  [renameExecfileArgumentsTransform.id]: renameExecfileArgumentsTransform,
109
112
  [renameFileExtensionVariablesTransform.id]: renameFileExtensionVariablesTransform,
113
+ [renameFileReaderVariablesTransform.id]: renameFileReaderVariablesTransform,
110
114
  [renameFsSyncVariablesTransform.id]: renameFsSyncVariablesTransform,
111
115
  [renameHttpMethodParametersTransform.id]: renameHttpMethodParametersTransform,
112
116
  [renameHttpServerParametersTransform.id]: renameHttpServerParametersTransform,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "recommended": {
3
- "diffSizePercent": 33.4134388841768,
4
- "notes": "Measured with baseline none: 33.41% of original diff."
3
+ "diffSizePercent": 30.450475921213833,
4
+ "notes": "Measured with baseline none: 30.45% of original diff."
5
5
  }
6
6
  }
@@ -4,4 +4,4 @@
4
4
  * This lets us tune transform interactions intentionally instead of relying on
5
5
  * alphabetical ID sorting.
6
6
  */
7
- export declare const recommendedTransformOrder: readonly ["stabilize-top-level-bindings", "stabilize-nested-bindings", "stabilize-deferred-top-level-bindings", "stabilize-deferred-stable-rhs", "expand-boolean-literals", "expand-sequence-expressions-v5", "expand-special-number-literals", "expand-typeof-undefined-comparisons", "expand-undefined-literals", "remove-redundant-else", "rename-arguments-length-flags", "rename-asap-wrappers", "rename-awaiter-parameters", "rename-awaiter-helper-functions", "rename-buffer-variables", "rename-to-buffer-results", "rename-catch-parameters", "rename-promise-catch-parameters", "rename-char-code-at", "rename-charcode-variables-v2", "rename-client-aliases", "rename-comparison-flags", "rename-date-now-start-times", "rename-default-options-parameters", "rename-deferred-resolve-parameters", "rename-destructured-aliases", "rename-rest-parameters", "rename-execfile-arguments", "rename-error-first-callback-parameters", "rename-error-variables", "rename-event-parameters", "rename-http-server-parameters", "rename-fs-sync-variables", "rename-http-method-parameters", "rename-interval-ids", "rename-loop-index-variables-v3", "rename-loop-length-variables", "rename-document-fragment-variables", "rename-object-keys-variables", "rename-object-keys-iterator-variables", "rename-parameters-to-match-properties-v2", "rename-promise-executor-parameters-v2", "rename-range-parameters", "rename-read-file-lines", "rename-regex-builders", "rename-regex-source-parameters", "rename-search-parameters-variables", "rename-file-extension-variables", "rename-string-split-variables", "rename-this-aliases", "rename-timeout-ids", "rename-typeof-variables", "rename-typescript-helper-aliases", "rename-uint8array-concat-variables", "rename-url-parameters", "rename-url-variables", "rename-use-reference-guards-v2", "rename-use-reference-sets", "rename-worker-handles", "rename-zod-check-parameters", "simplify-boolean-negations", "simplify-string-trim", "split-variable-declarations", "use-optional-chaining", "use-object-property-shorthand", "use-object-shorthand", "replace-dynamic-require-eval"];
7
+ export declare const recommendedTransformOrder: readonly ["stabilize-top-level-bindings", "stabilize-nested-bindings", "stabilize-deferred-top-level-bindings", "stabilize-deferred-stable-rhs", "expand-boolean-literals", "expand-sequence-expressions-v5", "expand-special-number-literals", "expand-typeof-undefined-comparisons", "expand-undefined-literals", "remove-redundant-else", "rename-arguments-length-flags", "rename-asap-wrappers", "rename-awaiter-parameters", "rename-awaiter-helper-functions", "rename-buffer-variables", "rename-to-buffer-results", "rename-catch-parameters", "rename-promise-catch-parameters", "rename-char-code-at", "rename-charcode-variables-v2", "rename-client-aliases", "rename-comparison-flags", "rename-date-now-start-times", "rename-default-options-parameters-v2", "rename-deferred-resolve-parameters", "rename-destructured-aliases", "rename-rest-parameters", "rename-execfile-arguments", "rename-file-reader-variables", "rename-error-first-callback-parameters", "rename-error-variables", "rename-event-parameters", "rename-http-server-parameters", "rename-fs-sync-variables", "rename-http-method-parameters", "rename-interval-ids", "rename-loop-index-variables-v3", "rename-loop-length-variables", "rename-document-fragment-variables", "rename-object-keys-variables", "rename-object-keys-iterator-variables", "rename-parameters-to-match-properties-v2", "rename-promise-executor-parameters-v2", "rename-range-parameters", "rename-read-file-lines", "rename-regex-builders", "rename-regex-source-parameters", "rename-search-parameters-variables", "rename-file-extension-variables", "rename-string-split-variables", "rename-this-aliases", "rename-timeout-ids", "rename-typeof-variables", "rename-typescript-helper-aliases", "rename-uint8array-concat-variables", "rename-url-parameters", "rename-url-variables", "rename-use-reference-guards-v2", "rename-use-reference-sets", "rename-worker-handles", "rename-zod-check-parameters", "simplify-boolean-negations", "simplify-string-trim", "split-variable-declarations", "use-optional-chaining", "use-object-property-shorthand", "use-object-shorthand", "replace-dynamic-require-eval"];
@@ -37,11 +37,12 @@ export const recommendedTransformOrder = [
37
37
  "rename-client-aliases",
38
38
  "rename-comparison-flags",
39
39
  "rename-date-now-start-times",
40
- "rename-default-options-parameters",
40
+ "rename-default-options-parameters-v2",
41
41
  "rename-deferred-resolve-parameters",
42
42
  "rename-destructured-aliases",
43
43
  "rename-rest-parameters",
44
44
  "rename-execfile-arguments",
45
+ "rename-file-reader-variables",
45
46
  "rename-error-first-callback-parameters",
46
47
  "rename-error-variables",
47
48
  "rename-event-parameters",
@@ -1,5 +1,7 @@
1
1
  {
2
- "recommended": true,
2
+ "recommended": false,
3
+ "supersededBy": "rename-default-options-parameters-v2",
4
+ "notes": "Superseded by rename-default-options-parameters-v2.",
3
5
  "evaluations": {
4
6
  "legacy:evaluation": {
5
7
  "evaluatedAt": "2026-01-25T19:51:46.527Z",
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { Function as BabelFunction } from "@babel/types";
3
+ export declare const collectDefaultOptionsParameterNames: (path: NodePath<BabelFunction>) => string[];
@@ -0,0 +1,64 @@
1
+ import * as t from "@babel/types";
2
+ import { getDefaultedParameterName } from "./get-defaulted-parameter-name.js";
3
+ const getDefaultOptionsParameterNamesFromChain = (statement, parameterNames, allowUndefinedCheck) => {
4
+ let matchedParameterName;
5
+ let current = statement;
6
+ while (current) {
7
+ const parameterName = getDefaultedParameterName(current, parameterNames, {
8
+ allowUndefinedCheck,
9
+ });
10
+ if (!parameterName)
11
+ return undefined;
12
+ if (!matchedParameterName) {
13
+ matchedParameterName = parameterName;
14
+ }
15
+ else if (matchedParameterName !== parameterName) {
16
+ return undefined;
17
+ }
18
+ if (!current.alternate) {
19
+ current = undefined;
20
+ continue;
21
+ }
22
+ if (!t.isIfStatement(current.alternate))
23
+ return undefined;
24
+ current = current.alternate;
25
+ }
26
+ return matchedParameterName;
27
+ };
28
+ export const collectDefaultOptionsParameterNames = (path) => {
29
+ const allowUndefinedCheck = !path.scope.hasBinding("undefined", true);
30
+ const body = path.node.body;
31
+ if (!t.isBlockStatement(body))
32
+ return [];
33
+ const parameterNames = new Set();
34
+ for (const parameter of path.node.params) {
35
+ if (!t.isIdentifier(parameter))
36
+ continue;
37
+ parameterNames.add(parameter.name);
38
+ }
39
+ let sawIfStatement = false;
40
+ let matchedParameterName;
41
+ for (const statement of body.body) {
42
+ if (t.isExpressionStatement(statement) &&
43
+ t.isStringLiteral(statement.expression)) {
44
+ continue;
45
+ }
46
+ if (!t.isIfStatement(statement))
47
+ break;
48
+ const chainMatch = getDefaultOptionsParameterNamesFromChain(statement, parameterNames, allowUndefinedCheck);
49
+ if (!chainMatch)
50
+ break;
51
+ sawIfStatement = true;
52
+ if (!matchedParameterName) {
53
+ matchedParameterName = chainMatch;
54
+ continue;
55
+ }
56
+ if (matchedParameterName !== chainMatch)
57
+ return [];
58
+ }
59
+ if (!sawIfStatement)
60
+ return [];
61
+ if (!matchedParameterName)
62
+ return [];
63
+ return [matchedParameterName];
64
+ };
@@ -0,0 +1,4 @@
1
+ import type { IfStatement } from "@babel/types";
2
+ export declare const getDefaultedParameterName: (statement: IfStatement, parameterNames: Set<string>, options: {
3
+ allowUndefinedCheck: boolean;
4
+ }) => string | undefined;
@@ -0,0 +1,117 @@
1
+ import * as t from "@babel/types";
2
+ const isEmptyObjectAssignment = (expression, parameterName) => {
3
+ if (!t.isAssignmentExpression(expression, { operator: "=" }))
4
+ return false;
5
+ if (!t.isIdentifier(expression.left, { name: parameterName }))
6
+ return false;
7
+ if (!t.isObjectExpression(expression.right))
8
+ return false;
9
+ return expression.right.properties.length === 0;
10
+ };
11
+ const hasEmptyObjectAssignment = (statement, parameterName) => {
12
+ if (t.isReturnStatement(statement) || t.isThrowStatement(statement)) {
13
+ return false;
14
+ }
15
+ if (t.isExpressionStatement(statement)) {
16
+ return isEmptyObjectAssignment(statement.expression, parameterName);
17
+ }
18
+ if (t.isBlockStatement(statement)) {
19
+ for (const child of statement.body) {
20
+ if (t.isReturnStatement(child) || t.isThrowStatement(child))
21
+ return false;
22
+ if (!t.isExpressionStatement(child))
23
+ continue;
24
+ if (isEmptyObjectAssignment(child.expression, parameterName))
25
+ return true;
26
+ }
27
+ }
28
+ return false;
29
+ };
30
+ const getUndefinedCheckedParameterName = (test) => {
31
+ if (!t.isBinaryExpression(test, { operator: "===" }))
32
+ return undefined;
33
+ const { left, right } = test;
34
+ if (t.isIdentifier(left) && t.isIdentifier(right, { name: "undefined" })) {
35
+ return left.name;
36
+ }
37
+ if (t.isIdentifier(left, { name: "undefined" }) && t.isIdentifier(right)) {
38
+ return right.name;
39
+ }
40
+ return undefined;
41
+ };
42
+ const getFalsyCheckedParameterName = (test) => {
43
+ if (!t.isUnaryExpression(test, { operator: "!" }))
44
+ return undefined;
45
+ if (!t.isIdentifier(test.argument))
46
+ return undefined;
47
+ return test.argument.name;
48
+ };
49
+ const getTypeofFunctionCheckedParameterName = (test) => {
50
+ if (!t.isBinaryExpression(test))
51
+ return undefined;
52
+ if (test.operator !== "===" && test.operator !== "==")
53
+ return undefined;
54
+ const leftTypeofTarget = t.isUnaryExpression(test.left, {
55
+ operator: "typeof",
56
+ })
57
+ ? test.left.argument
58
+ : undefined;
59
+ const rightTypeofTarget = t.isUnaryExpression(test.right, {
60
+ operator: "typeof",
61
+ })
62
+ ? test.right.argument
63
+ : undefined;
64
+ const leftFunctionLiteral = t.isStringLiteral(test.left, {
65
+ value: "function",
66
+ })
67
+ ? test.left
68
+ : undefined;
69
+ const rightFunctionLiteral = t.isStringLiteral(test.right, {
70
+ value: "function",
71
+ })
72
+ ? test.right
73
+ : undefined;
74
+ if (leftFunctionLiteral &&
75
+ rightTypeofTarget &&
76
+ t.isIdentifier(rightTypeofTarget)) {
77
+ return rightTypeofTarget.name;
78
+ }
79
+ if (rightFunctionLiteral &&
80
+ leftTypeofTarget &&
81
+ t.isIdentifier(leftTypeofTarget)) {
82
+ return leftTypeofTarget.name;
83
+ }
84
+ return undefined;
85
+ };
86
+ export const getDefaultedParameterName = (statement, parameterNames, options) => {
87
+ if (options.allowUndefinedCheck) {
88
+ const undefinedCheckedName = getUndefinedCheckedParameterName(statement.test);
89
+ if (undefinedCheckedName) {
90
+ if (!parameterNames.has(undefinedCheckedName))
91
+ return undefined;
92
+ if (!hasEmptyObjectAssignment(statement.consequent, undefinedCheckedName)) {
93
+ return undefined;
94
+ }
95
+ return undefinedCheckedName;
96
+ }
97
+ }
98
+ const falsyCheckedName = getFalsyCheckedParameterName(statement.test);
99
+ if (falsyCheckedName) {
100
+ if (!parameterNames.has(falsyCheckedName))
101
+ return undefined;
102
+ if (!hasEmptyObjectAssignment(statement.consequent, falsyCheckedName)) {
103
+ return undefined;
104
+ }
105
+ return falsyCheckedName;
106
+ }
107
+ const typeofCheckedName = getTypeofFunctionCheckedParameterName(statement.test);
108
+ if (typeofCheckedName) {
109
+ if (!parameterNames.has(typeofCheckedName))
110
+ return undefined;
111
+ if (!hasEmptyObjectAssignment(statement.consequent, typeofCheckedName)) {
112
+ return undefined;
113
+ }
114
+ return typeofCheckedName;
115
+ }
116
+ return undefined;
117
+ };
@@ -0,0 +1,12 @@
1
+ {
2
+ "recommended": true,
3
+ "evaluations": {
4
+ "claude-code-2.1.10:claude-code-2.1.11": {
5
+ "diffSizePercent": 100,
6
+ "evaluatedAt": "2026-02-06T12:59:48.597Z",
7
+ "changedLines": 774,
8
+ "durationSeconds": 235.023578515,
9
+ "stableNames": 1358
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,2 @@
1
+ import { type Transform } from "../../core/types.js";
2
+ export declare const renameDefaultOptionsParametersV2Transform: Transform;
@@ -0,0 +1,43 @@
1
+ import { createRequire } from "node:module";
2
+ import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
3
+ import { getFilesToProcess, } from "../../core/types.js";
4
+ import { collectDefaultOptionsParameterNames } from "./collect-default-options-parameter-names.js";
5
+ const require = createRequire(import.meta.url);
6
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
7
+ const traverse = require("@babel/traverse").default;
8
+ const BASE_NAME = "options";
9
+ export const renameDefaultOptionsParametersV2Transform = {
10
+ id: "rename-default-options-parameters-v2",
11
+ description: "Renames defaulted options-like parameters into stable options names",
12
+ scope: "file",
13
+ parallelizable: true,
14
+ transform(context) {
15
+ let nodesVisited = 0;
16
+ let transformationsApplied = 0;
17
+ for (const fileInfo of getFilesToProcess(context)) {
18
+ const group = new RenameGroup();
19
+ traverse(fileInfo.ast, {
20
+ Function(path) {
21
+ nodesVisited++;
22
+ const parameterNames = collectDefaultOptionsParameterNames(path);
23
+ for (const parameterName of parameterNames) {
24
+ if (parameterName === BASE_NAME)
25
+ continue;
26
+ if (isStableRenamed(parameterName))
27
+ continue;
28
+ group.add({
29
+ scope: path.scope,
30
+ currentName: parameterName,
31
+ baseName: BASE_NAME,
32
+ });
33
+ }
34
+ },
35
+ });
36
+ transformationsApplied += group.apply();
37
+ }
38
+ return Promise.resolve({
39
+ nodesVisited,
40
+ transformationsApplied,
41
+ });
42
+ },
43
+ };
@@ -0,0 +1,12 @@
1
+ {
2
+ "recommended": true,
3
+ "evaluations": {
4
+ "claude-code-2.1.10:claude-code-2.1.11": {
5
+ "diffSizePercent": 100,
6
+ "evaluatedAt": "2026-02-05T18:45:30.224Z",
7
+ "changedLines": 56,
8
+ "durationSeconds": 179.39064335899997,
9
+ "stableNames": 1358
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,2 @@
1
+ import { type Transform } from "../../core/types.js";
2
+ export declare const renameFileReaderVariablesTransform: Transform;
@@ -0,0 +1,111 @@
1
+ import { createRequire } from "node:module";
2
+ import { collectExportedNames } from "../../core/collect-exported-names.js";
3
+ import { RenameGroup, isStableRenamed } from "../../core/stable-naming.js";
4
+ import { getFilesToProcess, } from "../../core/types.js";
5
+ const require = createRequire(import.meta.url);
6
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
7
+ const traverse = require("@babel/traverse").default;
8
+ const isTypeOnlyImportSpecifier = (binding, node) => {
9
+ const declaration = binding.path.parent;
10
+ if (declaration.type !== "ImportDeclaration") {
11
+ return false;
12
+ }
13
+ if (node.type === "ImportSpecifier" && node.importKind === "type") {
14
+ return true;
15
+ }
16
+ return declaration.importKind === "type";
17
+ };
18
+ const isTypeOnlyFileReaderBinding = (binding) => {
19
+ const node = binding.path.node;
20
+ const declaration = binding.path.parent;
21
+ if (node.type === "ImportSpecifier" ||
22
+ node.type === "ImportDefaultSpecifier") {
23
+ return isTypeOnlyImportSpecifier(binding, node);
24
+ }
25
+ if (declaration.type === "VariableDeclaration" && declaration.declare) {
26
+ return true;
27
+ }
28
+ if (node.type === "TSDeclareFunction" ||
29
+ (node.type === "ClassDeclaration" && node.declare === true) ||
30
+ (node.type === "FunctionDeclaration" && node.declare === true)) {
31
+ return true;
32
+ }
33
+ return (node.type === "TSTypeAliasDeclaration" ||
34
+ node.type === "TSInterfaceDeclaration");
35
+ };
36
+ const hasRuntimeFileReaderShadowing = (path) => {
37
+ const binding = path.scope.getBinding("FileReader");
38
+ // Ambient declarations like `declare class FileReader` typically do not
39
+ // produce bindings in Babel's TS scope model, so `binding` is often absent.
40
+ if (!binding) {
41
+ return false;
42
+ }
43
+ // Keep using getBinding so we can inspect and ignore type-only TS bindings.
44
+ if (isTypeOnlyFileReaderBinding(binding)) {
45
+ return false;
46
+ }
47
+ return true;
48
+ };
49
+ const isFileReaderConstructor = (path) => {
50
+ const { callee } = path.node;
51
+ if (callee.type !== "Identifier")
52
+ return false;
53
+ if (callee.name !== "FileReader")
54
+ return false;
55
+ if (hasRuntimeFileReaderShadowing(path))
56
+ return false;
57
+ return true;
58
+ };
59
+ export const renameFileReaderVariablesTransform = {
60
+ id: "rename-file-reader-variables",
61
+ description: "Rename variables initialized with new FileReader() to stable fileReader-derived names",
62
+ scope: "file",
63
+ parallelizable: true,
64
+ transform(context) {
65
+ let nodesVisited = 0;
66
+ let transformationsApplied = 0;
67
+ for (const fileInfo of getFilesToProcess(context)) {
68
+ const renameGroup = new RenameGroup();
69
+ const exportedNames = collectExportedNames(fileInfo.ast.program);
70
+ traverse(fileInfo.ast, {
71
+ VariableDeclarator(path) {
72
+ nodesVisited++;
73
+ const { id } = path.node;
74
+ if (id.type !== "Identifier") {
75
+ return;
76
+ }
77
+ const initPath = path.get("init");
78
+ if (Array.isArray(initPath) || !initPath.isNewExpression()) {
79
+ return;
80
+ }
81
+ if (!isFileReaderConstructor(initPath)) {
82
+ return;
83
+ }
84
+ const currentName = id.name;
85
+ if (isStableRenamed(currentName)) {
86
+ return;
87
+ }
88
+ // Preserve already-semantic names (e.g. myFileReader) to avoid needless churn.
89
+ if (currentName.toLowerCase().includes("filereader")) {
90
+ return;
91
+ }
92
+ const binding = path.scope.getBinding(currentName);
93
+ if (!binding?.constant) {
94
+ return;
95
+ }
96
+ if (binding.scope.block.type === "Program" &&
97
+ exportedNames.has(currentName)) {
98
+ return;
99
+ }
100
+ renameGroup.add({
101
+ scope: path.scope,
102
+ currentName,
103
+ baseName: "fileReader",
104
+ });
105
+ },
106
+ });
107
+ transformationsApplied += renameGroup.apply();
108
+ }
109
+ return Promise.resolve({ nodesVisited, transformationsApplied });
110
+ },
111
+ };
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.72.0",
5
+ "version": "1.74.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",