miniread 1.113.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.
- package/dist/transforms/preset-stats.json +2 -2
- package/dist/transforms-by-id/rename-array-accumulator-variables/manifest.json +5 -5
- package/dist/transforms-by-id/rename-array-accumulator-variables/rename-array-accumulator-variables-transform.js +14 -12
- package/dist/transforms-by-id/rename-deferred-resolve-parameters-v2/deferred-executor-heuristics.d.ts +2 -1
- package/dist/transforms-by-id/rename-deferred-resolve-parameters-v2/deferred-executor-heuristics.js +22 -8
- package/dist/transforms-by-id/rename-deferred-resolve-parameters-v2/manifest.json +4 -4
- package/dist/transforms-by-id/rename-deferred-resolve-parameters-v2/rename-deferred-resolve-parameters-v2-transform.js +16 -15
- package/dist/transforms-by-id/rename-loop-index-variables-v3/get-loop-counter-name.js +0 -4
- package/dist/transforms-by-id/rename-loop-index-variables-v3/manifest.json +3 -3
- package/dist/transforms-by-id/rename-return-object-bindings/collect-return-object-binding-candidates.d.ts +0 -2
- package/dist/transforms-by-id/rename-return-object-bindings/collect-return-object-binding-candidates.js +3 -10
- package/dist/transforms-by-id/rename-return-object-bindings/manifest.json +5 -5
- package/dist/transforms-by-id/rename-return-object-bindings/rename-return-object-bindings-transform.js +0 -3
- package/dist/transforms-by-id/stabilize-deferred-top-level-bindings/collect-deferred-variables.d.ts +4 -3
- package/dist/transforms-by-id/stabilize-deferred-top-level-bindings/collect-deferred-variables.js +28 -6
- package/dist/transforms-by-id/stabilize-deferred-top-level-bindings/manifest.json +6 -6
- package/dist/transforms-by-id/stabilize-deferred-top-level-bindings/stabilize-deferred-top-level-bindings-transform.js +5 -5
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"recommended": {
|
|
3
|
-
"diffSizePercent":
|
|
4
|
-
"notes": "
|
|
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
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"notes": "Renames variables initialized as empty arrays with .push() usage to stable/readable names.",
|
|
2
|
+
"notes": "Renames variables initialized as empty arrays with .push()/.unshift() usage to stable/readable names.",
|
|
3
3
|
"evaluations": {
|
|
4
4
|
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
5
|
-
"diffSizePercent": 99.
|
|
6
|
-
"evaluatedAt": "2026-02-
|
|
7
|
-
"changedLines":
|
|
8
|
-
"durationSeconds":
|
|
5
|
+
"diffSizePercent": 99.94708394753752,
|
|
6
|
+
"evaluatedAt": "2026-02-17T08:05:01.672Z",
|
|
7
|
+
"changedLines": 9338,
|
|
8
|
+
"durationSeconds": 41.067913833,
|
|
9
9
|
"stableNames": 1360
|
|
10
10
|
}
|
|
11
11
|
},
|
|
@@ -6,22 +6,24 @@ const require = createRequire(import.meta.url);
|
|
|
6
6
|
const traverse = require("@babel/traverse").default;
|
|
7
7
|
const BASE_NAME = "results";
|
|
8
8
|
/**
|
|
9
|
-
* Checks whether a binding reference is
|
|
10
|
-
* Matches: `X.push(...)` where X is the binding.
|
|
9
|
+
* Checks whether a binding reference is an accumulator mutation call on the variable.
|
|
10
|
+
* Matches: `X.push(...)` or `X.unshift(...)` where X is the binding.
|
|
11
11
|
*/
|
|
12
|
-
const
|
|
12
|
+
const isAccumulatorMutationReference = (referencePath) => {
|
|
13
13
|
const memberPath = referencePath.parentPath;
|
|
14
14
|
if (!memberPath?.isMemberExpression())
|
|
15
15
|
return false;
|
|
16
|
-
// Must be X.push (not X[push] or X["push"])
|
|
16
|
+
// Must be X.push/X.unshift (not X[push] or X["push"])
|
|
17
17
|
if (memberPath.node.computed)
|
|
18
18
|
return false;
|
|
19
19
|
if (memberPath.node.object !== referencePath.node)
|
|
20
20
|
return false;
|
|
21
21
|
const property = memberPath.node.property;
|
|
22
|
-
if (property.type !== "Identifier"
|
|
22
|
+
if (property.type !== "Identifier")
|
|
23
23
|
return false;
|
|
24
|
-
|
|
24
|
+
if (property.name !== "push" && property.name !== "unshift")
|
|
25
|
+
return false;
|
|
26
|
+
// The member expression must be called: X.push(...) / X.unshift(...)
|
|
25
27
|
const callPath = memberPath.parentPath;
|
|
26
28
|
if (!callPath.isCallExpression())
|
|
27
29
|
return false;
|
|
@@ -30,15 +32,15 @@ const isPushCallReference = (referencePath) => {
|
|
|
30
32
|
return true;
|
|
31
33
|
};
|
|
32
34
|
/**
|
|
33
|
-
* Checks whether a binding has at least one
|
|
35
|
+
* Checks whether a binding has at least one array mutation call reference,
|
|
34
36
|
* confirming the accumulator pattern.
|
|
35
37
|
*/
|
|
36
|
-
const
|
|
37
|
-
return referencePaths.some((path) =>
|
|
38
|
+
const hasAtLeastOneAccumulatorMutation = (referencePaths) => {
|
|
39
|
+
return referencePaths.some((path) => isAccumulatorMutationReference(path));
|
|
38
40
|
};
|
|
39
41
|
export const renameArrayAccumulatorVariablesTransform = {
|
|
40
42
|
id: "rename-array-accumulator-variables",
|
|
41
|
-
description: "Renames variables initialized as empty arrays with .push() usage to $results/$results2/... or results/results2/...",
|
|
43
|
+
description: "Renames variables initialized as empty arrays with .push()/.unshift() usage to $results/$results2/... or results/results2/...",
|
|
42
44
|
scope: "file",
|
|
43
45
|
parallelizable: true,
|
|
44
46
|
transform(context) {
|
|
@@ -72,8 +74,8 @@ export const renameArrayAccumulatorVariablesTransform = {
|
|
|
72
74
|
// Must have at least one reference
|
|
73
75
|
if (binding.referencePaths.length === 0)
|
|
74
76
|
return;
|
|
75
|
-
// Must have at least one
|
|
76
|
-
if (!
|
|
77
|
+
// Must have at least one array mutation call to confirm accumulator pattern
|
|
78
|
+
if (!hasAtLeastOneAccumulatorMutation(binding.referencePaths))
|
|
77
79
|
return;
|
|
78
80
|
group.add({
|
|
79
81
|
scope: path.scope,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { NodePath } from "@babel/traverse";
|
|
2
2
|
import type { ArrowFunctionExpression, FunctionExpression, NewExpression } from "@babel/types";
|
|
3
3
|
export declare const getExecutorFunctionIfEligible: (path: NodePath<NewExpression>) => NodePath<FunctionExpression | ArrowFunctionExpression> | undefined;
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const isResolvePromiseLikeReference: (referencePath: NodePath) => boolean;
|
|
5
|
+
export declare const isRejectPromiseLikeReference: (referencePath: NodePath) => boolean;
|
|
5
6
|
export declare const wouldShadowReferencedOuterBinding: (executorPath: NodePath<FunctionExpression | ArrowFunctionExpression>, targetName: string) => boolean;
|
package/dist/transforms-by-id/rename-deferred-resolve-parameters-v2/deferred-executor-heuristics.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAssignedToOnHandlerProperty, isCatchRejectionHandlerArgument, isDirectCallOfBinding, isErrorEventHandlerArgument, isErrorEventHandlerRemovalArgument, isThenFulfillmentHandlerArgument, isThenRejectionHandlerArgument, } from "../rename-promise-executor-parameters-v2/promise-executor-heuristics.js";
|
|
1
2
|
import { isValidBindingIdentifier } from "./is-valid-binding-identifier.js";
|
|
2
3
|
const isExecutorParameterEligible = (parameter) => {
|
|
3
4
|
if (!isValidBindingIdentifier(parameter.name))
|
|
@@ -63,14 +64,27 @@ const isVariableDeclaratorInitReference = (referencePath) => {
|
|
|
63
64
|
return false;
|
|
64
65
|
return referencePath.key === "init";
|
|
65
66
|
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
const isStoredReference = (referencePath) => isArrayElementReference(referencePath) ||
|
|
68
|
+
isObjectPropertyValueReference(referencePath) ||
|
|
69
|
+
isAssignmentValueReference(referencePath) ||
|
|
70
|
+
isVariableDeclaratorInitReference(referencePath);
|
|
71
|
+
export const isResolvePromiseLikeReference = (referencePath) => {
|
|
72
|
+
if (!referencePath.isIdentifier())
|
|
73
|
+
return false;
|
|
74
|
+
return (isStoredReference(referencePath) ||
|
|
75
|
+
isDirectCallOfBinding(referencePath) ||
|
|
76
|
+
isThenFulfillmentHandlerArgument(referencePath));
|
|
77
|
+
};
|
|
78
|
+
export const isRejectPromiseLikeReference = (referencePath) => {
|
|
79
|
+
if (!referencePath.isIdentifier())
|
|
80
|
+
return false;
|
|
81
|
+
return (isStoredReference(referencePath) ||
|
|
82
|
+
isDirectCallOfBinding(referencePath) ||
|
|
83
|
+
isThenRejectionHandlerArgument(referencePath) ||
|
|
84
|
+
isCatchRejectionHandlerArgument(referencePath) ||
|
|
85
|
+
isAssignedToOnHandlerProperty(referencePath) ||
|
|
86
|
+
isErrorEventHandlerArgument(referencePath) ||
|
|
87
|
+
isErrorEventHandlerRemovalArgument(referencePath));
|
|
74
88
|
};
|
|
75
89
|
export const wouldShadowReferencedOuterBinding = (executorPath, targetName) => {
|
|
76
90
|
const parentBinding = executorPath.scope.getBinding(targetName);
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"notes": "Renames deferred promise executor parameters to $resolve/$reject when both are only stored.",
|
|
3
3
|
"evaluations": {
|
|
4
4
|
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
5
|
-
"diffSizePercent":
|
|
6
|
-
"evaluatedAt": "2026-02-
|
|
7
|
-
"changedLines":
|
|
8
|
-
"durationSeconds":
|
|
5
|
+
"diffSizePercent": 99.99244056393393,
|
|
6
|
+
"evaluatedAt": "2026-02-16T08:21:48.869Z",
|
|
7
|
+
"changedLines": 1670,
|
|
8
|
+
"durationSeconds": 40.99589079100001,
|
|
9
9
|
"stableNames": 1359
|
|
10
10
|
}
|
|
11
11
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { isStableRenamed } from "../../core/stable-naming.js";
|
|
3
3
|
import { getFilesToProcess, } from "../../core/types.js";
|
|
4
|
-
import { getExecutorFunctionIfEligible,
|
|
4
|
+
import { getExecutorFunctionIfEligible, isRejectPromiseLikeReference, isResolvePromiseLikeReference, wouldShadowReferencedOuterBinding, } from "./deferred-executor-heuristics.js";
|
|
5
5
|
import { hasExecutorLocalDynamicNameLookup } from "./has-executor-local-dynamic-name-lookup.js";
|
|
6
6
|
import { canRenameBindingSafely } from "./rename-binding-if-safe.js";
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
@@ -10,7 +10,7 @@ const traverse = require("@babel/traverse").default;
|
|
|
10
10
|
const STABLE_PREFIX = "$";
|
|
11
11
|
export const renameDeferredResolveParametersV2Transform = {
|
|
12
12
|
id: "rename-deferred-resolve-parameters-v2",
|
|
13
|
-
description: "Renames deferred promise resolve/reject parameters to $resolve/$reject when
|
|
13
|
+
description: "Renames deferred promise resolve/reject parameters to $resolve/$reject when safe",
|
|
14
14
|
scope: "file",
|
|
15
15
|
parallelizable: true,
|
|
16
16
|
transform(context) {
|
|
@@ -60,28 +60,29 @@ export const renameDeferredResolveParametersV2Transform = {
|
|
|
60
60
|
const rejectBinding = bindingScope.getBinding(rejectParameter.name);
|
|
61
61
|
if (!resolveBinding || !rejectBinding)
|
|
62
62
|
return;
|
|
63
|
-
if (resolveBinding.constantViolations.length > 0)
|
|
64
|
-
return;
|
|
65
|
-
if (rejectBinding.constantViolations.length > 0)
|
|
66
|
-
return;
|
|
67
|
-
const resolveHasReferences = resolveBinding.referencePaths.length > 0;
|
|
68
|
-
const rejectHasReferences = rejectBinding.referencePaths.length > 0;
|
|
69
63
|
const resolveReferencePaths = resolveBinding.referencePaths;
|
|
70
64
|
const rejectReferencePaths = rejectBinding.referencePaths;
|
|
71
65
|
const hasUsageSignal = resolveReferencePaths.length > 0 || rejectReferencePaths.length > 0;
|
|
72
66
|
if (!hasUsageSignal)
|
|
73
67
|
return;
|
|
74
|
-
const
|
|
75
|
-
if (!
|
|
68
|
+
const resolveHasOnlyPromiseLikeReferences = resolveReferencePaths.every((referencePath) => isResolvePromiseLikeReference(referencePath));
|
|
69
|
+
if (!resolveHasOnlyPromiseLikeReferences)
|
|
76
70
|
return;
|
|
77
|
-
const
|
|
78
|
-
if (!
|
|
71
|
+
const rejectHasOnlyPromiseLikeReferences = rejectReferencePaths.every((referencePath) => isRejectPromiseLikeReference(referencePath));
|
|
72
|
+
if (!rejectHasOnlyPromiseLikeReferences)
|
|
79
73
|
return;
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const wantsRejectRename = rejectHasReferences && rejectParameter.name !== targetRejectName;
|
|
74
|
+
const wantsResolveRename = resolveParameter.name !== targetResolveName;
|
|
75
|
+
const wantsRejectRename = rejectParameter.name !== targetRejectName;
|
|
83
76
|
if (!wantsResolveRename && !wantsRejectRename)
|
|
84
77
|
return;
|
|
78
|
+
if (wantsResolveRename &&
|
|
79
|
+
resolveBinding.constantViolations.length > 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (wantsRejectRename &&
|
|
83
|
+
rejectBinding.constantViolations.length > 0) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
85
86
|
// Shadowing checks require a full traversal of the executor, so only do them for parameters
|
|
86
87
|
// that actually need renaming.
|
|
87
88
|
const resolveWouldShadow = wantsResolveRename &&
|
|
@@ -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-
|
|
7
|
-
"changedLines":
|
|
8
|
-
"durationSeconds":
|
|
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
|
-
|
|
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,
|
|
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.
|
|
5
|
-
"evaluatedAt": "2026-02-
|
|
6
|
-
"changedLines":
|
|
7
|
-
"durationSeconds":
|
|
8
|
-
"stableNames":
|
|
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)) {
|
package/dist/transforms-by-id/stabilize-deferred-top-level-bindings/collect-deferred-variables.d.ts
CHANGED
|
@@ -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
|
|
20
|
-
*
|
|
20
|
+
* Group deferred vars by enclosing function and select one deterministic
|
|
21
|
+
* candidate for each function.
|
|
21
22
|
*/
|
|
22
|
-
export declare const
|
|
23
|
+
export declare const selectDeferredVariablesForRename: (deferredVariables: DeferredVariableInfo[]) => DeferredVariableInfo[];
|
|
23
24
|
export {};
|
package/dist/transforms-by-id/stabilize-deferred-top-level-bindings/collect-deferred-variables.js
CHANGED
|
@@ -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
|
|
98
|
-
*
|
|
98
|
+
* Group deferred vars by enclosing function and select one deterministic
|
|
99
|
+
* candidate for each function.
|
|
99
100
|
*/
|
|
100
|
-
export const
|
|
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
|
-
//
|
|
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
|
-
|
|
117
|
-
|
|
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
|
|
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.
|
|
6
|
-
"evaluatedAt": "2026-02-
|
|
7
|
-
"changedLines":
|
|
8
|
-
"durationSeconds":
|
|
9
|
-
"stableNames":
|
|
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,
|
|
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.
|
|
15
|
-
"
|
|
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
|
-
//
|
|
37
|
-
const eligibleVariables =
|
|
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.
|
|
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",
|