miniread 1.114.0 → 1.115.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "recommended": {
3
- "diffSizePercent": 26.05643119023321,
4
- "notes": "Measured with baseline none: 26.06% of original diff."
3
+ "diffSizePercent": 25.50270249839362,
4
+ "notes": "Average test/baseline diff size ratio for the recommended preset with baseline set to none."
5
5
  }
6
6
  }
@@ -105,8 +105,6 @@ export const getStableRenamedIndexLoopCounterName = (path) => {
105
105
  continue;
106
106
  if (declarator.init.type !== "NumericLiteral")
107
107
  continue;
108
- if (declarator.init.value !== 0)
109
- continue;
110
108
  if (!isLoopCounterDeclarator(loopCounterName, path))
111
109
  continue;
112
110
  if (stableName !== undefined)
@@ -133,8 +131,6 @@ export const getLoopCounterNameIfEligible = (path) => {
133
131
  continue;
134
132
  if (declarator.init.type !== "NumericLiteral")
135
133
  continue;
136
- if (declarator.init.value !== 0)
137
- continue;
138
134
  const loopCounterName = declarator.id.name;
139
135
  if (!isLoopCounterDeclarator(loopCounterName, path))
140
136
  continue;
@@ -3,9 +3,9 @@
3
3
  "evaluations": {
4
4
  "claude-code-2.1.10:claude-code-2.1.11": {
5
5
  "diffSizePercent": 100,
6
- "evaluatedAt": "2026-02-10T17:29:03.499Z",
7
- "changedLines": 3478,
8
- "durationSeconds": 60.688113084,
6
+ "evaluatedAt": "2026-02-14T13:04:12.173Z",
7
+ "changedLines": 3626,
8
+ "durationSeconds": 180.525304795,
9
9
  "stableNames": 1361
10
10
  }
11
11
  },
@@ -4,7 +4,6 @@ type BindingCandidate = {
4
4
  binding: Binding;
5
5
  currentName: string;
6
6
  baseName: string;
7
- conflicted: boolean;
8
7
  };
9
8
  type CollectReturnObjectBindingCandidatesResult = {
10
9
  candidates: Map<Binding, BindingCandidate>;
@@ -12,7 +11,6 @@ type CollectReturnObjectBindingCandidatesResult = {
12
11
  };
13
12
  type CollectReturnObjectBindingCandidatesOptions = {
14
13
  ast: File;
15
- isScript: boolean;
16
14
  exportedNames: ReadonlySet<string>;
17
15
  };
18
16
  export declare const collectReturnObjectBindingCandidates: (options: CollectReturnObjectBindingCandidatesOptions) => CollectReturnObjectBindingCandidatesResult;
@@ -25,8 +25,6 @@ const collectCandidatesForObjectExpression = (options) => {
25
25
  const propertyName = getPropertyName(property.key);
26
26
  if (!propertyName)
27
27
  continue;
28
- if (propertyName.length <= 1)
29
- continue;
30
28
  if (!isValidBindingIdentifier(propertyName))
31
29
  continue;
32
30
  if (isHashBasedStableName(`$${propertyName}`))
@@ -53,23 +51,18 @@ const collectCandidatesForObjectExpression = (options) => {
53
51
  exportedNames.has(currentName)) {
54
52
  continue;
55
53
  }
56
- const existing = candidates.get(binding);
57
- if (existing) {
58
- if (existing.baseName !== propertyName) {
59
- existing.conflicted = true;
60
- }
54
+ if (candidates.has(binding))
61
55
  continue;
62
- }
63
56
  candidates.set(binding, {
64
57
  binding,
65
58
  currentName,
66
59
  baseName: propertyName,
67
- conflicted: false,
68
60
  });
69
61
  }
70
62
  };
71
63
  export const collectReturnObjectBindingCandidates = (options) => {
72
- const { ast, isScript, exportedNames } = options;
64
+ const { ast, exportedNames } = options;
65
+ const isScript = ast.program.sourceType === "script";
73
66
  let nodesVisited = 0;
74
67
  const candidates = new Map();
75
68
  traverse(ast, {
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "evaluations": {
3
3
  "claude-code-2.1.10:claude-code-2.1.11": {
4
- "diffSizePercent": 99.87715916392636,
5
- "evaluatedAt": "2026-02-12T09:10:04.050Z",
6
- "changedLines": 13347,
7
- "durationSeconds": 44.633210875,
8
- "stableNames": 2444
4
+ "diffSizePercent": 99.88282874097592,
5
+ "evaluatedAt": "2026-02-17T12:48:21.759Z",
6
+ "changedLines": 13827,
7
+ "durationSeconds": 47.550179875,
8
+ "stableNames": 2458
9
9
  }
10
10
  },
11
11
  "recommended": true,
@@ -30,15 +30,12 @@ export const renameReturnObjectBindingsTransform = {
30
30
  const exportedNames = collectExportedNames(fileInfo.ast.program);
31
31
  const { candidates, nodesVisited: fileNodesVisited } = collectReturnObjectBindingCandidates({
32
32
  ast: fileInfo.ast,
33
- isScript,
34
33
  exportedNames,
35
34
  });
36
35
  nodesVisited += fileNodesVisited;
37
36
  const dynamicLookupScopes = collectLocalDynamicLookupScopes(fileInfo.ast);
38
37
  const group = new RenameGroup();
39
38
  for (const candidate of candidates.values()) {
40
- if (candidate.conflicted)
41
- continue;
42
39
  if (candidate.binding.scope.block.type === "Program"
43
40
  ? hasProgramScopeDynamicNameLookup
44
41
  : hasBindingLocalDynamicNameLookup(candidate.binding, candidate.currentName, dynamicLookupScopes)) {
@@ -5,6 +5,7 @@ type DeferredVariableInfo = {
5
5
  enclosingFunctionName: string;
6
6
  enclosingFunctionScopeUid: number;
7
7
  scope: Scope;
8
+ declarationStart: number | undefined;
8
9
  };
9
10
  /**
10
11
  * Collect information about deferred top-level var declarations.
@@ -16,8 +17,8 @@ export declare const collectDeferredVariables: (ast: t.File, programScope: Scope
16
17
  nodesVisited: number;
17
18
  };
18
19
  /**
19
- * Group deferred vars by their enclosing function.
20
- * Returns only vars where the function has exactly ONE deferred var.
20
+ * Group deferred vars by enclosing function and select one deterministic
21
+ * candidate for each function.
21
22
  */
22
- export declare const filterToSingleVariableFunctions: (deferredVariables: DeferredVariableInfo[]) => DeferredVariableInfo[];
23
+ export declare const selectDeferredVariablesForRename: (deferredVariables: DeferredVariableInfo[]) => DeferredVariableInfo[];
23
24
  export {};
@@ -87,6 +87,7 @@ export const collectDeferredVariables = (ast, programScope) => {
87
87
  enclosingFunctionName: info.enclosingFunctionName,
88
88
  enclosingFunctionScopeUid: info.enclosingFunctionScopeUid,
89
89
  scope: binding.scope,
90
+ declarationStart: binding.identifier.start ?? undefined,
90
91
  });
91
92
  }
92
93
  }
@@ -94,10 +95,10 @@ export const collectDeferredVariables = (ast, programScope) => {
94
95
  return { deferredVariables, nodesVisited };
95
96
  };
96
97
  /**
97
- * Group deferred vars by their enclosing function.
98
- * Returns only vars where the function has exactly ONE deferred var.
98
+ * Group deferred vars by enclosing function and select one deterministic
99
+ * candidate for each function.
99
100
  */
100
- export const filterToSingleVariableFunctions = (deferredVariables) => {
101
+ export const selectDeferredVariablesForRename = (deferredVariables) => {
101
102
  // Group by enclosing function identity (name + scope UID).
102
103
  const byFunction = new Map();
103
104
  for (const info of deferredVariables) {
@@ -110,12 +111,33 @@ export const filterToSingleVariableFunctions = (deferredVariables) => {
110
111
  byFunction.set(functionIdentity, [info]);
111
112
  }
112
113
  }
113
- // Only keep vars where function has exactly one
114
+ // Keep one candidate per function:
115
+ // - Skip already-stable bindings (`$...`) to avoid churn.
116
+ // - For multi-candidate functions, pick the earliest declaration position.
117
+ // This tie-break is independent of minified identifier text.
114
118
  const result = [];
115
119
  for (const variables of byFunction.values()) {
116
- if (variables.length === 1 && variables[0]) {
117
- result.push(variables[0]);
120
+ const unstabilizedVariables = variables.filter((variableInfo) => !isStableRenamed(variableInfo.variableName));
121
+ if (unstabilizedVariables.length === 0)
122
+ continue;
123
+ if (unstabilizedVariables.length === 1 && unstabilizedVariables[0]) {
124
+ result.push(unstabilizedVariables[0]);
125
+ continue;
118
126
  }
127
+ const sortedByDeclarationStart = unstabilizedVariables.toSorted((left, right) => {
128
+ const leftStart = left.declarationStart ?? Number.POSITIVE_INFINITY;
129
+ const rightStart = right.declarationStart ?? Number.POSITIVE_INFINITY;
130
+ return leftStart - rightStart;
131
+ });
132
+ const first = sortedByDeclarationStart[0];
133
+ const second = sortedByDeclarationStart[1];
134
+ if (!first)
135
+ continue;
136
+ const firstStart = first.declarationStart ?? Number.POSITIVE_INFINITY;
137
+ const secondStart = second?.declarationStart ?? Number.POSITIVE_INFINITY;
138
+ if (firstStart === secondStart)
139
+ continue;
140
+ result.push(first);
119
141
  }
120
142
  return result;
121
143
  };
@@ -1,12 +1,12 @@
1
1
  {
2
- "notes": "Stabilizes top-level var declarations that are initialized inside stable-named functions (lazy init pattern). Only applies when the enclosing function has exactly one such deferred var to avoid order-dependent naming. Renames ~750 vars but diff reduction is minimal (0.03%) because most vars already have consistent minified names between versions. The stabilization helps when minifiers do vary names.",
2
+ "notes": "Stabilizes top-level var declarations initialized inside stable-named functions (lazy init pattern). For multi-deferred functions, picks a deterministic candidate by declaration order and skips already-stable `$...` bindings to avoid churn from minifier-dependent variable names.",
3
3
  "evaluations": {
4
4
  "claude-code-2.1.10:claude-code-2.1.11": {
5
- "diffSizePercent": 100.13606984918925,
6
- "evaluatedAt": "2026-02-10T06:27:28.231Z",
7
- "changedLines": 126,
8
- "durationSeconds": 39.356983667,
9
- "stableNames": 1367
5
+ "diffSizePercent": 100.24568167214727,
6
+ "evaluatedAt": "2026-02-17T12:58:15.137Z",
7
+ "changedLines": 448,
8
+ "durationSeconds": 44.418337541,
9
+ "stableNames": 1380
10
10
  }
11
11
  },
12
12
  "recommended": true,
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  import { parse } from "@babel/parser";
3
3
  import { getFilesToProcess, } from "../../core/types.js";
4
4
  import { generateCode } from "../../core/generate-code.js";
5
- import { collectDeferredVariables, filterToSingleVariableFunctions, } from "./collect-deferred-variables.js";
5
+ import { collectDeferredVariables, selectDeferredVariablesForRename, } from "./collect-deferred-variables.js";
6
6
  import { extractHashFromStableName, renameBindingInPlace, } from "./rename-helpers.js";
7
7
  import { canRenameBindingSafely } from "../../transforms/shared/can-rename-binding-safely.js";
8
8
  const require = createRequire(import.meta.url);
@@ -11,8 +11,8 @@ const traverse = require("@babel/traverse").default;
11
11
  export const stabilizeDeferredTopLevelBindingsTransform = {
12
12
  id: "stabilize-deferred-top-level-bindings",
13
13
  description: "Renames top-level var declarations without initializers (deferred bindings) to stable names " +
14
- "derived from their enclosing stable function. Only applies when the function has exactly one " +
15
- "such deferred var to avoid order-dependent naming.",
14
+ "derived from their enclosing stable function. For functions with multiple deferred vars, " +
15
+ "chooses a deterministic candidate by declaration order and skips already-stable bindings.",
16
16
  scope: "file",
17
17
  parallelizable: true,
18
18
  transform(context) {
@@ -33,8 +33,8 @@ export const stabilizeDeferredTopLevelBindingsTransform = {
33
33
  // Collect deferred vars
34
34
  const { deferredVariables, nodesVisited: fileNodesVisited } = collectDeferredVariables(fileInfo.ast, programScope);
35
35
  nodesVisited += fileNodesVisited;
36
- // Filter to vars where function has exactly one deferred var
37
- const eligibleVariables = filterToSingleVariableFunctions(deferredVariables);
36
+ // Select one deterministic deferred var candidate per function.
37
+ const eligibleVariables = selectDeferredVariablesForRename(deferredVariables);
38
38
  // Apply renames. Track used names for consistency with stabilize-deferred-stable-rhs
39
39
  // and to avoid duplicate `$v_...` targets when different functions produce
40
40
  // the same derived hash.
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.114.0",
5
+ "version": "1.115.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",