miniread 1.111.1 → 1.112.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 (35) hide show
  1. package/dist/transforms/_generated/catalog.js +7 -7
  2. package/dist/transforms-by-id/rename-collection-constructor-variables/manifest.json +3 -3
  3. package/dist/transforms-by-id/rename-constructor-variables/get-constructor-base-name-from-expression.d.ts +3 -0
  4. package/dist/transforms-by-id/rename-constructor-variables/get-constructor-base-name-from-expression.js +30 -0
  5. package/dist/transforms-by-id/rename-constructor-variables/manifest.json +14 -0
  6. package/dist/transforms-by-id/rename-constructor-variables/rename-constructor-variables-transform.d.ts +2 -0
  7. package/dist/transforms-by-id/rename-constructor-variables/rename-constructor-variables-transform.js +74 -0
  8. package/dist/transforms-by-id/rename-constructor-variables/resolve-constructor-name-from-expression.d.ts +3 -0
  9. package/dist/transforms-by-id/rename-constructor-variables/resolve-constructor-name-from-expression.js +77 -0
  10. package/dist/transforms-by-id/rename-constructor-variables/unwrap-transparent-expression.d.ts +3 -0
  11. package/dist/transforms-by-id/rename-constructor-variables/unwrap-transparent-expression.js +29 -0
  12. package/dist/transforms-by-id/rename-file-reader-variables/manifest.json +6 -5
  13. package/dist/transforms-by-id/rename-file-reader-variables/rename-file-reader-variables-transform.js +53 -35
  14. package/package.json +1 -1
  15. package/dist/transforms-by-id/rename-file-reader-variables/is-file-reader-constructor.d.ts +0 -3
  16. package/dist/transforms-by-id/rename-file-reader-variables/is-file-reader-constructor.js +0 -90
  17. package/dist/transforms-by-id/rename-file-reader-variables/queue-file-reader-rename.d.ts +0 -12
  18. package/dist/transforms-by-id/rename-file-reader-variables/queue-file-reader-rename.js +0 -25
  19. package/dist/transforms-by-id/rename-search-parameters-variables/get-member-expression-property-name.d.ts +0 -2
  20. package/dist/transforms-by-id/rename-search-parameters-variables/get-member-expression-property-name.js +0 -20
  21. package/dist/transforms-by-id/rename-search-parameters-variables/get-search-parameters-member-expression.d.ts +0 -3
  22. package/dist/transforms-by-id/rename-search-parameters-variables/get-search-parameters-member-expression.js +0 -26
  23. package/dist/transforms-by-id/rename-search-parameters-variables/is-search-parameters-destructure-usage.d.ts +0 -2
  24. package/dist/transforms-by-id/rename-search-parameters-variables/is-search-parameters-destructure-usage.js +0 -74
  25. package/dist/transforms-by-id/rename-search-parameters-variables/is-search-parameters-for-of-usage.d.ts +0 -2
  26. package/dist/transforms-by-id/rename-search-parameters-variables/is-search-parameters-for-of-usage.js +0 -18
  27. package/dist/transforms-by-id/rename-search-parameters-variables/is-search-parameters-property-access.d.ts +0 -2
  28. package/dist/transforms-by-id/rename-search-parameters-variables/is-search-parameters-property-access.js +0 -30
  29. package/dist/transforms-by-id/rename-search-parameters-variables/is-search-parameters-spread-usage.d.ts +0 -2
  30. package/dist/transforms-by-id/rename-search-parameters-variables/is-search-parameters-spread-usage.js +0 -34
  31. package/dist/transforms-by-id/rename-search-parameters-variables/manifest.json +0 -14
  32. package/dist/transforms-by-id/rename-search-parameters-variables/rename-search-parameters-variables-transform.d.ts +0 -2
  33. package/dist/transforms-by-id/rename-search-parameters-variables/rename-search-parameters-variables-transform.js +0 -89
  34. package/dist/transforms-by-id/rename-search-parameters-variables/search-parameters-property-names.d.ts +0 -1
  35. package/dist/transforms-by-id/rename-search-parameters-variables/search-parameters-property-names.js +0 -15
@@ -44,6 +44,8 @@ import { renameComparisonFlagsTransform } from "../../transforms-by-id/rename-co
44
44
  import renameComparisonFlagsManifest from "../../transforms-by-id/rename-comparison-flags/manifest.json" with { type: "json" };
45
45
  import { renameComparisonFlagsV2Transform } from "../../transforms-by-id/rename-comparison-flags-v2/rename-comparison-flags-v2-transform.js";
46
46
  import renameComparisonFlagsV2Manifest from "../../transforms-by-id/rename-comparison-flags-v2/manifest.json" with { type: "json" };
47
+ import { renameConstructorVariablesTransform } from "../../transforms-by-id/rename-constructor-variables/rename-constructor-variables-transform.js";
48
+ import renameConstructorVariablesManifest from "../../transforms-by-id/rename-constructor-variables/manifest.json" with { type: "json" };
47
49
  import { renameDateNowStartTimesTransform } from "../../transforms-by-id/rename-date-now-start-times/rename-date-now-start-times-transform.js";
48
50
  import renameDateNowStartTimesManifest from "../../transforms-by-id/rename-date-now-start-times/manifest.json" with { type: "json" };
49
51
  import { renameDefaultOptionsParametersV3Transform } from "../../transforms-by-id/rename-default-options-parameters-v3/rename-default-options-parameters-v3-transform.js";
@@ -134,8 +136,6 @@ import { renameReturnObjectBindingsTransform } from "../../transforms-by-id/rena
134
136
  import renameReturnObjectBindingsManifest from "../../transforms-by-id/rename-return-object-bindings/manifest.json" with { type: "json" };
135
137
  import { renameSanitizedStringVariablesTransform } from "../../transforms-by-id/rename-sanitized-string-variables/rename-sanitized-string-variables-transform.js";
136
138
  import renameSanitizedStringVariablesManifest from "../../transforms-by-id/rename-sanitized-string-variables/manifest.json" with { type: "json" };
137
- import { renameSearchParametersVariablesTransform } from "../../transforms-by-id/rename-search-parameters-variables/rename-search-parameters-variables-transform.js";
138
- import renameSearchParametersVariablesManifest from "../../transforms-by-id/rename-search-parameters-variables/manifest.json" with { type: "json" };
139
139
  import { renameSetstateUpdaterParametersTransform } from "../../transforms-by-id/rename-setstate-updater-parameters/rename-setstate-updater-parameters-transform.js";
140
140
  import renameSetstateUpdaterParametersManifest from "../../transforms-by-id/rename-setstate-updater-parameters/manifest.json" with { type: "json" };
141
141
  import { renameStreamDataChunksTransform } from "../../transforms-by-id/rename-stream-data-chunks/rename-stream-data-chunks-transform.js";
@@ -305,6 +305,11 @@ export const generatedTransformCatalog = [
305
305
  transform: renameComparisonFlagsV2Transform,
306
306
  manifest: renameComparisonFlagsV2Manifest,
307
307
  },
308
+ {
309
+ id: renameConstructorVariablesTransform.id,
310
+ transform: renameConstructorVariablesTransform,
311
+ manifest: renameConstructorVariablesManifest,
312
+ },
308
313
  {
309
314
  id: renameDateNowStartTimesTransform.id,
310
315
  transform: renameDateNowStartTimesTransform,
@@ -530,11 +535,6 @@ export const generatedTransformCatalog = [
530
535
  transform: renameSanitizedStringVariablesTransform,
531
536
  manifest: renameSanitizedStringVariablesManifest,
532
537
  },
533
- {
534
- id: renameSearchParametersVariablesTransform.id,
535
- transform: renameSearchParametersVariablesTransform,
536
- manifest: renameSearchParametersVariablesManifest,
537
- },
538
538
  {
539
539
  id: renameSetstateUpdaterParametersTransform.id,
540
540
  transform: renameSetstateUpdaterParametersTransform,
@@ -1,5 +1,6 @@
1
1
  {
2
- "notes": "Renames variables initialized with collection constructors (Set, Map, WeakSet, WeakMap) to stable semantic names.",
2
+ "notes": "Superseded by rename-constructor-variables for constructor-derived variable naming coverage.",
3
+ "supersededBy": "rename-constructor-variables",
3
4
  "evaluations": {
4
5
  "claude-code-2.1.10:claude-code-2.1.11": {
5
6
  "diffSizePercent": 99.96976225573572,
@@ -9,6 +10,5 @@
9
10
  "stableNames": 1364
10
11
  }
11
12
  },
12
- "recommended": true,
13
- "recommendedOrder": 101
13
+ "recommended": false
14
14
  }
@@ -0,0 +1,3 @@
1
+ import type { Scope } from "@babel/traverse";
2
+ import type { Expression } from "@babel/types";
3
+ export declare const getConstructorBaseNameFromExpression: (scope: Scope, expression: Expression | null | undefined) => string | undefined;
@@ -0,0 +1,30 @@
1
+ import * as t from "@babel/types";
2
+ import { isValidBindingIdentifier } from "../../transforms/shared/is-valid-binding-identifier.js";
3
+ import { resolveConstructorNameFromExpression } from "./resolve-constructor-name-from-expression.js";
4
+ import { unwrapTransparentExpression } from "./unwrap-transparent-expression.js";
5
+ const toCamelCaseBaseName = (constructorName) => {
6
+ const uppercasePrefixMatch = constructorName.match(/^[A-Z]+(?=[A-Z][a-z]|[0-9]|$)/u);
7
+ if (!uppercasePrefixMatch) {
8
+ const [firstCharacter = "", ...rest] = constructorName;
9
+ return `${firstCharacter.toLowerCase()}${rest.join("")}`;
10
+ }
11
+ const uppercasePrefix = uppercasePrefixMatch[0];
12
+ return (uppercasePrefix.toLowerCase() +
13
+ constructorName.slice(uppercasePrefix.length));
14
+ };
15
+ export const getConstructorBaseNameFromExpression = (scope, expression) => {
16
+ if (!expression)
17
+ return undefined;
18
+ const unwrappedExpression = unwrapTransparentExpression(expression);
19
+ if (!t.isNewExpression(unwrappedExpression))
20
+ return undefined;
21
+ if (!t.isExpression(unwrappedExpression.callee))
22
+ return undefined;
23
+ const constructorName = resolveConstructorNameFromExpression(scope, unwrappedExpression.callee);
24
+ if (!constructorName)
25
+ return undefined;
26
+ const baseName = toCamelCaseBaseName(constructorName);
27
+ if (!isValidBindingIdentifier(baseName))
28
+ return undefined;
29
+ return baseName;
30
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "notes": "Renames constructor-initialized or constructor-assigned variables by deriving camelCase names from constructor identifiers, member constructors, and local constructor aliases.",
3
+ "evaluations": {
4
+ "claude-code-2.1.10:claude-code-2.1.11": {
5
+ "diffSizePercent": 100.3269456098575,
6
+ "evaluatedAt": "2026-02-13T08:51:30.726Z",
7
+ "changedLines": 15609,
8
+ "durationSeconds": 367.642214666,
9
+ "stableNames": 1804
10
+ }
11
+ },
12
+ "recommended": true,
13
+ "recommendedOrder": 201
14
+ }
@@ -0,0 +1,2 @@
1
+ import { type Transform } from "../../core/types.js";
2
+ export declare const renameConstructorVariablesTransform: Transform;
@@ -0,0 +1,74 @@
1
+ import { createRequire } from "node:module";
2
+ import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
3
+ import { getFilesToProcess, } from "../../core/types.js";
4
+ import { getConstructorBaseNameFromExpression } from "./get-constructor-base-name-from-expression.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 queueConstructorRename = (options) => {
9
+ const { scope, currentName, baseName, group, queuedBindings } = options;
10
+ if (isStableRenamed(currentName))
11
+ return;
12
+ const binding = scope.getBinding(currentName);
13
+ if (!binding)
14
+ return;
15
+ if (queuedBindings.has(binding))
16
+ return;
17
+ queuedBindings.add(binding);
18
+ group.add({
19
+ scope: binding.scope,
20
+ currentName,
21
+ baseName,
22
+ });
23
+ };
24
+ export const renameConstructorVariablesTransform = {
25
+ id: "rename-constructor-variables",
26
+ description: "Renames variables initialized or assigned from constructor calls to constructor-derived names like $dataView and $fooBar",
27
+ scope: "file",
28
+ parallelizable: true,
29
+ transform(context) {
30
+ let nodesVisited = 0;
31
+ let transformationsApplied = 0;
32
+ for (const fileInfo of getFilesToProcess(context)) {
33
+ const group = new RenameGroup();
34
+ const queuedBindings = new Set();
35
+ traverse(fileInfo.ast, {
36
+ VariableDeclarator(path) {
37
+ nodesVisited++;
38
+ const { id } = path.node;
39
+ if (id.type !== "Identifier")
40
+ return;
41
+ const baseName = getConstructorBaseNameFromExpression(path.scope, path.node.init);
42
+ if (!baseName)
43
+ return;
44
+ queueConstructorRename({
45
+ scope: path.scope,
46
+ currentName: id.name,
47
+ baseName,
48
+ group,
49
+ queuedBindings,
50
+ });
51
+ },
52
+ AssignmentExpression(path) {
53
+ nodesVisited++;
54
+ if (path.node.operator !== "=")
55
+ return;
56
+ if (path.node.left.type !== "Identifier")
57
+ return;
58
+ const baseName = getConstructorBaseNameFromExpression(path.scope, path.node.right);
59
+ if (!baseName)
60
+ return;
61
+ queueConstructorRename({
62
+ scope: path.scope,
63
+ currentName: path.node.left.name,
64
+ baseName,
65
+ group,
66
+ queuedBindings,
67
+ });
68
+ },
69
+ });
70
+ transformationsApplied += group.apply();
71
+ }
72
+ return Promise.resolve({ nodesVisited, transformationsApplied });
73
+ },
74
+ };
@@ -0,0 +1,3 @@
1
+ import type { Scope } from "@babel/traverse";
2
+ import type { Expression } from "@babel/types";
3
+ export declare const resolveConstructorNameFromExpression: (scope: Scope, expression: Expression) => string | undefined;
@@ -0,0 +1,77 @@
1
+ import * as t from "@babel/types";
2
+ import { MAX_ALIAS_RESOLUTION_DEPTH, unwrapTransparentExpression, } from "./unwrap-transparent-expression.js";
3
+ const UPPERCASE_CONSTRUCTOR_PATTERN = /^[A-Z][A-Za-z0-9_$]*$/u;
4
+ const getMemberPropertyName = (memberExpression) => {
5
+ if (!memberExpression.computed) {
6
+ if (!t.isIdentifier(memberExpression.property))
7
+ return undefined;
8
+ return memberExpression.property.name;
9
+ }
10
+ if (t.isStringLiteral(memberExpression.property)) {
11
+ return memberExpression.property.value;
12
+ }
13
+ if (t.isTemplateLiteral(memberExpression.property) &&
14
+ memberExpression.property.expressions.length === 0 &&
15
+ memberExpression.property.quasis.length === 1) {
16
+ return memberExpression.property.quasis[0]?.value.cooked ?? undefined;
17
+ }
18
+ return undefined;
19
+ };
20
+ const resolveFromBinding = (binding, visitedBindings, depth) => {
21
+ if (depth > MAX_ALIAS_RESOLUTION_DEPTH)
22
+ return undefined;
23
+ const bindingPath = binding.path;
24
+ if (bindingPath.isClassDeclaration() || bindingPath.isFunctionDeclaration()) {
25
+ return binding.identifier.name;
26
+ }
27
+ if (!bindingPath.isVariableDeclarator() || !binding.constant) {
28
+ return undefined;
29
+ }
30
+ const { init, id } = bindingPath.node;
31
+ if (!init)
32
+ return undefined;
33
+ const unwrappedInit = unwrapTransparentExpression(init);
34
+ if (t.isClassExpression(unwrappedInit) ||
35
+ t.isFunctionExpression(unwrappedInit)) {
36
+ if (unwrappedInit.id?.name)
37
+ return unwrappedInit.id.name;
38
+ return t.isIdentifier(id) ? id.name : undefined;
39
+ }
40
+ return resolveFromExpression(bindingPath.scope, unwrappedInit, visitedBindings, depth + 1);
41
+ };
42
+ const resolveFromIdentifier = (scope, identifierName, visitedBindings, depth) => {
43
+ if (depth > MAX_ALIAS_RESOLUTION_DEPTH)
44
+ return undefined;
45
+ const binding = scope.getBinding(identifierName);
46
+ if (binding) {
47
+ if (visitedBindings.has(binding))
48
+ return undefined;
49
+ visitedBindings.add(binding);
50
+ const resolvedFromBinding = resolveFromBinding(binding, visitedBindings, depth + 1);
51
+ if (resolvedFromBinding)
52
+ return resolvedFromBinding;
53
+ }
54
+ return UPPERCASE_CONSTRUCTOR_PATTERN.test(identifierName)
55
+ ? identifierName
56
+ : undefined;
57
+ };
58
+ const resolveFromExpression = (scope, expression, visitedBindings, depth) => {
59
+ if (depth > MAX_ALIAS_RESOLUTION_DEPTH)
60
+ return undefined;
61
+ const unwrappedExpression = unwrapTransparentExpression(expression);
62
+ if (t.isIdentifier(unwrappedExpression)) {
63
+ return resolveFromIdentifier(scope, unwrappedExpression.name, visitedBindings, depth + 1);
64
+ }
65
+ if (t.isMemberExpression(unwrappedExpression) ||
66
+ t.isOptionalMemberExpression(unwrappedExpression)) {
67
+ return getMemberPropertyName(unwrappedExpression);
68
+ }
69
+ if (t.isClassExpression(unwrappedExpression) ||
70
+ t.isFunctionExpression(unwrappedExpression)) {
71
+ return unwrappedExpression.id?.name;
72
+ }
73
+ return undefined;
74
+ };
75
+ export const resolveConstructorNameFromExpression = (scope, expression) => {
76
+ return resolveFromExpression(scope, expression, new Set(), 0);
77
+ };
@@ -0,0 +1,3 @@
1
+ import type { Expression } from "@babel/types";
2
+ export declare const MAX_ALIAS_RESOLUTION_DEPTH = 8;
3
+ export declare const unwrapTransparentExpression: (expression: Expression) => Expression;
@@ -0,0 +1,29 @@
1
+ import * as t from "@babel/types";
2
+ export const MAX_ALIAS_RESOLUTION_DEPTH = 8;
3
+ const unwrapSingleTransparentExpression = (expression) => {
4
+ if (t.isParenthesizedExpression(expression))
5
+ return expression.expression;
6
+ if (t.isTSAsExpression(expression))
7
+ return expression.expression;
8
+ if (t.isTSSatisfiesExpression(expression))
9
+ return expression.expression;
10
+ if (t.isTSTypeAssertion(expression))
11
+ return expression.expression;
12
+ if (t.isTSNonNullExpression(expression))
13
+ return expression.expression;
14
+ if (t.isTypeCastExpression(expression))
15
+ return expression.expression;
16
+ if (t.isSequenceExpression(expression))
17
+ return expression.expressions.at(-1);
18
+ return undefined;
19
+ };
20
+ export const unwrapTransparentExpression = (expression) => {
21
+ let currentExpression = expression;
22
+ for (let depth = 0; depth < MAX_ALIAS_RESOLUTION_DEPTH; depth += 1) {
23
+ const nextExpression = unwrapSingleTransparentExpression(currentExpression);
24
+ if (!nextExpression)
25
+ return currentExpression;
26
+ currentExpression = nextExpression;
27
+ }
28
+ return currentExpression;
29
+ };
@@ -1,13 +1,14 @@
1
1
  {
2
+ "notes": "Post-pass behavior after rename-constructor-variables: focuses on FileReader event-handler parameter naming for fileReader-like bindings.",
2
3
  "evaluations": {
3
4
  "claude-code-2.1.10:claude-code-2.1.11": {
4
5
  "diffSizePercent": 100,
5
- "evaluatedAt": "2026-02-10T06:09:53.201Z",
6
- "changedLines": 80,
7
- "durationSeconds": 32.424962083,
8
- "stableNames": 1359
6
+ "evaluatedAt": "2026-02-14T07:23:12.948Z",
7
+ "changedLines": 0,
8
+ "durationSeconds": 25.987191208,
9
+ "stableNames": 1357
9
10
  }
10
11
  },
11
12
  "recommended": true,
12
- "recommendedOrder": 50
13
+ "recommendedOrder": 211
13
14
  }
@@ -1,16 +1,51 @@
1
1
  import { createRequire } from "node:module";
2
- import { collectExportedNames } from "../../core/collect-exported-names.js";
2
+ import * as t from "@babel/types";
3
3
  import { RenameGroup } from "../../core/stable-naming.js";
4
4
  import { getFilesToProcess, } from "../../core/types.js";
5
- import { isFileReaderConstructor } from "./is-file-reader-constructor.js";
6
5
  import { queueFileReaderEventRename } from "./queue-file-reader-event-rename.js";
7
- import { queueFileReaderRename } from "./queue-file-reader-rename.js";
8
6
  const require = createRequire(import.meta.url);
9
7
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
10
8
  const traverse = require("@babel/traverse").default;
9
+ const FILE_READER_BINDING_PATTERN = /^\$?fileReader(?:Instance)?\d*$/u;
10
+ const isTrackedFileReaderBindingName = (name) => FILE_READER_BINDING_PATTERN.test(name);
11
+ const getMemberPropertyName = (memberExpression) => {
12
+ if (!memberExpression.computed) {
13
+ if (!t.isIdentifier(memberExpression.property))
14
+ return undefined;
15
+ return memberExpression.property.name;
16
+ }
17
+ if (t.isStringLiteral(memberExpression.property)) {
18
+ return memberExpression.property.value;
19
+ }
20
+ if (t.isTemplateLiteral(memberExpression.property) &&
21
+ memberExpression.property.expressions.length === 0 &&
22
+ memberExpression.property.quasis.length === 1) {
23
+ return memberExpression.property.quasis[0]?.value.cooked ?? undefined;
24
+ }
25
+ return undefined;
26
+ };
27
+ const isFileReaderConstructorExpression = (expression) => {
28
+ if (!t.isNewExpression(expression))
29
+ return false;
30
+ const callee = expression.callee;
31
+ if (t.isIdentifier(callee)) {
32
+ return callee.name === "FileReader";
33
+ }
34
+ if (!t.isMemberExpression(callee) && !t.isOptionalMemberExpression(callee)) {
35
+ return false;
36
+ }
37
+ const propertyName = getMemberPropertyName(callee);
38
+ if (propertyName !== "FileReader")
39
+ return false;
40
+ if (!t.isIdentifier(callee.object))
41
+ return false;
42
+ return (callee.object.name === "globalThis" ||
43
+ callee.object.name === "window" ||
44
+ callee.object.name === "self");
45
+ };
11
46
  export const renameFileReaderVariablesTransform = {
12
47
  id: "rename-file-reader-variables",
13
- description: "Rename variables initialized with new FileReader() to stable fileReader-derived names",
48
+ description: "Post-pass: renames FileReader event-handler parameters for fileReader-like bindings",
14
49
  scope: "file",
15
50
  parallelizable: true,
16
51
  transform(context) {
@@ -18,33 +53,20 @@ export const renameFileReaderVariablesTransform = {
18
53
  let transformationsApplied = 0;
19
54
  for (const fileInfo of getFilesToProcess(context)) {
20
55
  const renameGroup = new RenameGroup();
21
- const exportedNames = collectExportedNames(fileInfo.ast.program);
22
- const queuedBindings = new Set();
23
56
  const queuedEventBindings = new Set();
24
57
  const fileReaderBindings = new Set();
25
58
  let directTransformationsApplied = 0;
26
59
  traverse(fileInfo.ast, {
27
60
  VariableDeclarator(path) {
28
61
  nodesVisited++;
29
- const { id } = path.node;
30
- if (id.type !== "Identifier") {
62
+ if (path.node.id.type !== "Identifier")
31
63
  return;
32
- }
33
- const initPath = path.get("init");
34
- if (Array.isArray(initPath) || !initPath.isNewExpression()) {
64
+ if (!isTrackedFileReaderBindingName(path.node.id.name))
35
65
  return;
36
- }
37
- if (!isFileReaderConstructor(initPath)) {
66
+ const binding = path.scope.getBinding(path.node.id.name);
67
+ if (!binding)
38
68
  return;
39
- }
40
- queueFileReaderRename({
41
- scope: path.scope,
42
- currentName: id.name,
43
- renameGroup,
44
- exportedNames,
45
- queuedBindings,
46
- fileReaderBindings,
47
- });
69
+ fileReaderBindings.add(binding);
48
70
  },
49
71
  AssignmentExpression(path) {
50
72
  nodesVisited++;
@@ -54,22 +76,18 @@ export const renameFileReaderVariablesTransform = {
54
76
  if (!binding) {
55
77
  return;
56
78
  }
57
- const rightPath = path.get("right");
58
- if (operator !== "=" ||
59
- Array.isArray(rightPath) ||
60
- !rightPath.isNewExpression() ||
61
- !isFileReaderConstructor(rightPath)) {
79
+ if (!isTrackedFileReaderBindingName(binding.identifier.name)) {
80
+ return;
81
+ }
82
+ if (operator !== "=") {
62
83
  fileReaderBindings.delete(binding);
63
84
  return;
64
85
  }
65
- queueFileReaderRename({
66
- scope: path.scope,
67
- currentName: left.name,
68
- renameGroup,
69
- exportedNames,
70
- queuedBindings,
71
- fileReaderBindings,
72
- });
86
+ if (isFileReaderConstructorExpression(path.node.right)) {
87
+ fileReaderBindings.add(binding);
88
+ return;
89
+ }
90
+ fileReaderBindings.delete(binding);
73
91
  return;
74
92
  }
75
93
  directTransformationsApplied += queueFileReaderEventRename({
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.111.1",
5
+ "version": "1.112.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",
@@ -1,3 +0,0 @@
1
- import type { NodePath } from "@babel/traverse";
2
- import type { NewExpression } from "@babel/types";
3
- export declare const isFileReaderConstructor: (path: NodePath<NewExpression>) => boolean;
@@ -1,90 +0,0 @@
1
- const GLOBAL_FILE_READER_OBJECTS = new Set(["globalThis", "window", "self"]);
2
- const isTypeOnlyImportSpecifier = (binding, node) => {
3
- const declaration = binding.path.parent;
4
- if (declaration.type !== "ImportDeclaration") {
5
- return false;
6
- }
7
- if (node.type === "ImportSpecifier" && node.importKind === "type") {
8
- return true;
9
- }
10
- return declaration.importKind === "type";
11
- };
12
- const isTypeOnlyFileReaderBinding = (binding) => {
13
- const node = binding.path.node;
14
- const declaration = binding.path.parent;
15
- if (node.type === "ImportSpecifier" ||
16
- node.type === "ImportDefaultSpecifier") {
17
- return isTypeOnlyImportSpecifier(binding, node);
18
- }
19
- if (declaration.type === "VariableDeclaration" && declaration.declare) {
20
- return true;
21
- }
22
- if (node.type === "TSDeclareFunction" ||
23
- (node.type === "ClassDeclaration" && node.declare === true) ||
24
- (node.type === "FunctionDeclaration" && node.declare === true)) {
25
- return true;
26
- }
27
- return (node.type === "TSTypeAliasDeclaration" ||
28
- node.type === "TSInterfaceDeclaration");
29
- };
30
- const hasRuntimeFileReaderShadowing = (path) => {
31
- const binding = path.scope.getBinding("FileReader");
32
- // Ambient declarations like `declare class FileReader` typically do not
33
- // produce bindings in Babel's TS scope model, so `binding` is often absent.
34
- if (!binding) {
35
- return false;
36
- }
37
- // Keep using getBinding so we can inspect and ignore type-only TS bindings.
38
- if (isTypeOnlyFileReaderBinding(binding)) {
39
- return false;
40
- }
41
- return true;
42
- };
43
- const isGlobalFileReaderMemberExpression = (path) => {
44
- const { callee } = path.node;
45
- if (callee.type !== "MemberExpression") {
46
- return false;
47
- }
48
- if (callee.computed) {
49
- if (callee.property.type === "StringLiteral" &&
50
- callee.property.value !== "FileReader") {
51
- return false;
52
- }
53
- if (callee.property.type === "TemplateLiteral" &&
54
- (callee.property.expressions.length > 0 ||
55
- callee.property.quasis[0]?.value.cooked !== "FileReader")) {
56
- return false;
57
- }
58
- if (callee.property.type !== "StringLiteral" &&
59
- callee.property.type !== "TemplateLiteral") {
60
- return false;
61
- }
62
- }
63
- else {
64
- if (callee.property.type !== "Identifier" ||
65
- callee.property.name !== "FileReader") {
66
- return false;
67
- }
68
- }
69
- if (callee.object.type !== "Identifier") {
70
- return false;
71
- }
72
- if (!GLOBAL_FILE_READER_OBJECTS.has(callee.object.name)) {
73
- return false;
74
- }
75
- if (path.scope.hasBinding(callee.object.name, true)) {
76
- return false;
77
- }
78
- return true;
79
- };
80
- export const isFileReaderConstructor = (path) => {
81
- const { callee } = path.node;
82
- if (callee.type === "Identifier") {
83
- if (callee.name !== "FileReader")
84
- return false;
85
- if (hasRuntimeFileReaderShadowing(path))
86
- return false;
87
- return true;
88
- }
89
- return isGlobalFileReaderMemberExpression(path);
90
- };
@@ -1,12 +0,0 @@
1
- import type { Binding, NodePath } from "@babel/traverse";
2
- import type { RenameGroup } from "../../core/stable-naming.js";
3
- type QueueFileReaderRenameParameters = {
4
- scope: NodePath["scope"];
5
- currentName: string;
6
- renameGroup: RenameGroup;
7
- exportedNames: Set<string>;
8
- queuedBindings: Set<Binding>;
9
- fileReaderBindings: Set<Binding>;
10
- };
11
- export declare const queueFileReaderRename: ({ scope, currentName, renameGroup, exportedNames, queuedBindings, fileReaderBindings, }: QueueFileReaderRenameParameters) => void;
12
- export {};
@@ -1,25 +0,0 @@
1
- import { isStableRenamed } from "../../core/stable-naming.js";
2
- const FILE_READER_BASE_NAME = "fileReaderInstance";
3
- export const queueFileReaderRename = ({ scope, currentName, renameGroup, exportedNames, queuedBindings, fileReaderBindings, }) => {
4
- if (isStableRenamed(currentName)) {
5
- return;
6
- }
7
- const binding = scope.getBinding(currentName);
8
- if (!binding) {
9
- return;
10
- }
11
- if (queuedBindings.has(binding)) {
12
- return;
13
- }
14
- if (binding.scope.block.type === "Program" &&
15
- exportedNames.has(currentName)) {
16
- return;
17
- }
18
- queuedBindings.add(binding);
19
- fileReaderBindings.add(binding);
20
- renameGroup.add({
21
- scope: binding.scope,
22
- currentName,
23
- baseName: FILE_READER_BASE_NAME,
24
- });
25
- };
@@ -1,2 +0,0 @@
1
- import type { MemberExpression, OptionalMemberExpression } from "@babel/types";
2
- export declare const getMemberExpressionPropertyName: (expression: MemberExpression | OptionalMemberExpression) => string | undefined;
@@ -1,20 +0,0 @@
1
- export const getMemberExpressionPropertyName = (expression) => {
2
- const key = expression.property;
3
- if (!expression.computed) {
4
- if (key.type === "Identifier")
5
- return key.name;
6
- if (key.type === "StringLiteral")
7
- return key.value;
8
- return undefined;
9
- }
10
- if (key.type === "StringLiteral")
11
- return key.value;
12
- if (key.type === "TemplateLiteral" &&
13
- key.expressions.length === 0 &&
14
- key.quasis.length === 1) {
15
- const quasi = key.quasis[0];
16
- const cooked = quasi?.value.cooked;
17
- return cooked ?? quasi?.value.raw;
18
- }
19
- return undefined;
20
- };
@@ -1,3 +0,0 @@
1
- import type { NodePath } from "@babel/traverse";
2
- import type { MemberExpression, OptionalMemberExpression } from "@babel/types";
3
- export declare const getSearchParametersMemberExpression: (referencePath: NodePath) => NodePath<MemberExpression | OptionalMemberExpression> | undefined;
@@ -1,26 +0,0 @@
1
- export const getSearchParametersMemberExpression = (referencePath) => {
2
- const parent = referencePath.parentPath;
3
- if (!parent)
4
- return;
5
- if (parent.isMemberExpression() &&
6
- parent.node.object === referencePath.node) {
7
- return parent;
8
- }
9
- if (parent.isOptionalMemberExpression() &&
10
- parent.node.object === referencePath.node) {
11
- return parent;
12
- }
13
- if (parent.isTSNonNullExpression() &&
14
- parent.node.expression === referencePath.node) {
15
- const outer = parent.parentPath;
16
- if (!outer)
17
- return;
18
- if (outer.isMemberExpression() && outer.node.object === parent.node) {
19
- return outer;
20
- }
21
- if (outer.isOptionalMemberExpression() &&
22
- outer.node.object === parent.node) {
23
- return outer;
24
- }
25
- }
26
- };
@@ -1,2 +0,0 @@
1
- import type { NodePath } from "@babel/traverse";
2
- export declare const isSearchParametersDestructureUsage: (referencePath: NodePath) => boolean;
@@ -1,74 +0,0 @@
1
- import { searchParametersPropertyNames } from "./search-parameters-property-names.js";
2
- export const isSearchParametersDestructureUsage = (referencePath) => {
3
- const parent = referencePath.parentPath;
4
- if (parent?.isTSNonNullExpression() &&
5
- parent.node.expression === referencePath.node) {
6
- const outer = parent.parentPath;
7
- if (!outer)
8
- return false;
9
- if (outer.isVariableDeclarator()) {
10
- if (outer.node.init !== parent.node)
11
- return false;
12
- const id = outer.node.id;
13
- if (id.type !== "ObjectPattern")
14
- return false;
15
- return isSearchParametersDestructurePattern(id);
16
- }
17
- if (outer.isAssignmentExpression()) {
18
- if (outer.node.operator !== "=")
19
- return false;
20
- if (outer.node.right !== parent.node)
21
- return false;
22
- const left = outer.node.left;
23
- if (left.type !== "ObjectPattern")
24
- return false;
25
- return isSearchParametersDestructurePattern(left);
26
- }
27
- return false;
28
- }
29
- if (parent?.isVariableDeclarator()) {
30
- if (parent.node.init !== referencePath.node)
31
- return false;
32
- const id = parent.node.id;
33
- if (id.type !== "ObjectPattern")
34
- return false;
35
- return isSearchParametersDestructurePattern(id);
36
- }
37
- if (parent?.isAssignmentExpression()) {
38
- if (parent.node.operator !== "=")
39
- return false;
40
- if (parent.node.right !== referencePath.node)
41
- return false;
42
- const left = parent.node.left;
43
- if (left.type !== "ObjectPattern")
44
- return false;
45
- return isSearchParametersDestructurePattern(left);
46
- }
47
- return false;
48
- };
49
- const isSearchParametersDestructurePattern = (pattern) => {
50
- if (pattern.properties.length === 0)
51
- return false;
52
- for (const property of pattern.properties) {
53
- // Rest properties suggest generic object destructuring, so we stay
54
- // conservative and don't treat it as URLSearchParams usage evidence.
55
- if (property.type === "RestElement")
56
- return false;
57
- const keyName = property.computed
58
- ? property.key.type === "StringLiteral"
59
- ? property.key.value
60
- : undefined
61
- : property.key.type === "Identifier"
62
- ? property.key.name
63
- : property.key.type === "StringLiteral"
64
- ? property.key.value
65
- : undefined;
66
- if (!keyName)
67
- return false;
68
- // Be strict: `URLSearchParams` method names overlap with many other APIs, so
69
- // we only treat destructuring as usage evidence when *every* key matches.
70
- if (!searchParametersPropertyNames.has(keyName))
71
- return false;
72
- }
73
- return true;
74
- };
@@ -1,2 +0,0 @@
1
- import type { NodePath } from "@babel/traverse";
2
- export declare const isSearchParametersForOfUsage: (referencePath: NodePath) => boolean;
@@ -1,18 +0,0 @@
1
- export const isSearchParametersForOfUsage = (referencePath) => {
2
- const parent = referencePath.parentPath;
3
- if (!parent)
4
- return false;
5
- if (parent.isForOfStatement()) {
6
- return parent.node.right === referencePath.node;
7
- }
8
- if (parent.isTSNonNullExpression() &&
9
- parent.node.expression === referencePath.node) {
10
- const forOf = parent.parentPath;
11
- if (!forOf)
12
- return false;
13
- if (!forOf.isForOfStatement())
14
- return false;
15
- return forOf.node.right === parent.node;
16
- }
17
- return false;
18
- };
@@ -1,2 +0,0 @@
1
- import type { NodePath } from "@babel/traverse";
2
- export declare const isSearchParametersPropertyAccess: (referencePath: NodePath) => boolean;
@@ -1,30 +0,0 @@
1
- import { getMemberExpressionPropertyName } from "./get-member-expression-property-name.js";
2
- import { getSearchParametersMemberExpression } from "./get-search-parameters-member-expression.js";
3
- import { searchParametersPropertyNames } from "./search-parameters-property-names.js";
4
- export const isSearchParametersPropertyAccess = (referencePath) => {
5
- const memberExpression = getSearchParametersMemberExpression(referencePath);
6
- if (!memberExpression)
7
- return false;
8
- const keyName = getMemberExpressionPropertyName(memberExpression.node);
9
- if (!keyName)
10
- return isSymbolIteratorAccess(memberExpression.node);
11
- return searchParametersPropertyNames.has(keyName);
12
- };
13
- const isSymbolIteratorAccess = (expression) => {
14
- if (!expression.computed)
15
- return false;
16
- const property = expression.property;
17
- if (property.type !== "MemberExpression" &&
18
- property.type !== "OptionalMemberExpression") {
19
- return false;
20
- }
21
- if (property.computed)
22
- return false;
23
- if (property.object.type !== "Identifier")
24
- return false;
25
- if (property.object.name !== "Symbol")
26
- return false;
27
- if (property.property.type !== "Identifier")
28
- return false;
29
- return property.property.name === "iterator";
30
- };
@@ -1,2 +0,0 @@
1
- import type { NodePath } from "@babel/traverse";
2
- export declare const isSearchParametersSpreadUsage: (referencePath: NodePath) => boolean;
@@ -1,34 +0,0 @@
1
- export const isSearchParametersSpreadUsage = (referencePath) => {
2
- const parent = referencePath.parentPath;
3
- if (!parent)
4
- return false;
5
- if (parent.isSpreadElement()) {
6
- const container = parent.parentPath;
7
- if (!container)
8
- return false;
9
- if (!container.isArrayExpression() &&
10
- !container.isCallExpression() &&
11
- !container.isNewExpression()) {
12
- return false;
13
- }
14
- return parent.node.argument === referencePath.node;
15
- }
16
- if (parent.isTSNonNullExpression() &&
17
- parent.node.expression === referencePath.node) {
18
- const spread = parent.parentPath;
19
- if (!spread)
20
- return false;
21
- if (!spread.isSpreadElement())
22
- return false;
23
- const container = spread.parentPath;
24
- if (!container)
25
- return false;
26
- if (!container.isArrayExpression() &&
27
- !container.isCallExpression() &&
28
- !container.isNewExpression()) {
29
- return false;
30
- }
31
- return spread.node.argument === parent.node;
32
- }
33
- return false;
34
- };
@@ -1,14 +0,0 @@
1
- {
2
- "notes": "Measured with baseline none: 100.00% of original diff. Added to recommended for readability.",
3
- "evaluations": {
4
- "claude-code-2.1.10:claude-code-2.1.11": {
5
- "diffSizePercent": 100,
6
- "evaluatedAt": "2026-02-10T06:09:20.069Z",
7
- "changedLines": 40,
8
- "durationSeconds": 37.640948791999996,
9
- "stableNames": 1358
10
- }
11
- },
12
- "recommended": true,
13
- "recommendedOrder": 100
14
- }
@@ -1,2 +0,0 @@
1
- import { type Transform } from "../../core/types.js";
2
- export declare const renameSearchParametersVariablesTransform: Transform;
@@ -1,89 +0,0 @@
1
- import { createRequire } from "node:module";
2
- import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
3
- import { getFilesToProcess, } from "../../core/types.js";
4
- import { getUrlConstructionKind } from "../../transforms/get-url-construction-kind.js";
5
- import { isAllowedUrlConstructorInScope } from "../../transforms/is-allowed-url-constructor-in-scope.js";
6
- import { isSearchParametersDestructureUsage } from "./is-search-parameters-destructure-usage.js";
7
- import { isSearchParametersForOfUsage } from "./is-search-parameters-for-of-usage.js";
8
- import { isSearchParametersPropertyAccess } from "./is-search-parameters-property-access.js";
9
- import { isSearchParametersSpreadUsage } from "./is-search-parameters-spread-usage.js";
10
- const require = createRequire(import.meta.url);
11
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
12
- const traverse = require("@babel/traverse").default;
13
- const hasSearchParametersUsage = (binding) => {
14
- for (const referencePath of binding.referencePaths) {
15
- if (isSearchParametersPropertyAccess(referencePath) ||
16
- isSearchParametersForOfUsage(referencePath) ||
17
- isSearchParametersSpreadUsage(referencePath) ||
18
- isSearchParametersDestructureUsage(referencePath)) {
19
- return true;
20
- }
21
- }
22
- return false;
23
- };
24
- export const renameSearchParametersVariablesTransform = {
25
- id: "rename-search-parameters-variables",
26
- description: "Renames variables initialized with new URLSearchParams(...) to stable search parameter identifiers",
27
- scope: "file",
28
- parallelizable: true,
29
- transform(context) {
30
- let nodesVisited = 0;
31
- let transformationsApplied = 0;
32
- for (const fileInfo of getFilesToProcess(context)) {
33
- const group = new RenameGroup();
34
- const candidatesByScope = new Map();
35
- traverse(fileInfo.ast, {
36
- VariableDeclarator(path) {
37
- nodesVisited += 1;
38
- // Only rename actual bindings. Patterns don't create a stable
39
- // `URLSearchParams` variable to rename, and `URLSearchParams` is
40
- // iterable (not a plain object), so destructuring doesn't map cleanly.
41
- if (path.node.id.type !== "Identifier")
42
- return;
43
- const constructionKind = getUrlConstructionKind(path.scope, path.node.init, "URLSearchParams");
44
- if (!constructionKind)
45
- return;
46
- if (constructionKind === "identifier" &&
47
- !isAllowedUrlConstructorInScope(path.scope, "URLSearchParams")) {
48
- return;
49
- }
50
- const currentName = path.node.id.name;
51
- if (isStableRenamed(currentName))
52
- return;
53
- const binding = path.scope.getBinding(currentName);
54
- if (!binding)
55
- return;
56
- if (!binding.constant)
57
- return;
58
- const candidateName = "searchParameters";
59
- if (candidateName === currentName)
60
- return;
61
- const scopeCandidates = candidatesByScope.get(path.scope) ?? [];
62
- scopeCandidates.push({
63
- scope: path.scope,
64
- currentName,
65
- hasUsage: hasSearchParametersUsage(binding),
66
- });
67
- candidatesByScope.set(path.scope, scopeCandidates);
68
- },
69
- });
70
- for (const scopeCandidates of candidatesByScope.values()) {
71
- const requiresUsageEvidence = scopeCandidates.length > 1;
72
- for (const candidate of scopeCandidates) {
73
- if (requiresUsageEvidence && !candidate.hasUsage)
74
- continue;
75
- group.add({
76
- scope: candidate.scope,
77
- currentName: candidate.currentName,
78
- baseName: "searchParameters",
79
- });
80
- }
81
- }
82
- transformationsApplied += group.apply();
83
- }
84
- return Promise.resolve({
85
- nodesVisited,
86
- transformationsApplied,
87
- });
88
- },
89
- };
@@ -1 +0,0 @@
1
- export declare const searchParametersPropertyNames: Set<string>;
@@ -1,15 +0,0 @@
1
- export const searchParametersPropertyNames = new Set([
2
- "append",
3
- "delete",
4
- "entries",
5
- "forEach",
6
- "get",
7
- "getAll",
8
- "has",
9
- "keys",
10
- "set",
11
- "size",
12
- "sort",
13
- "toString",
14
- "values",
15
- ]);