miniread 1.115.3 → 1.115.4
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-command-result-variables/manifest.json +4 -4
- package/dist/transforms-by-id/rename-command-result-variables/rename-command-result-variables-transform.js +15 -1
- package/dist/transforms-by-id/rename-error-first-callback-parameters/manifest.json +6 -6
- package/dist/transforms-by-id/rename-error-first-callback-parameters/rename-error-first-callback-parameters-transform.js +3 -1
- package/dist/transforms-by-id/rename-http-method-parameters/manifest.json +7 -7
- package/dist/transforms-by-id/rename-http-method-parameters/rename-http-method-parameters-transform.js +3 -3
- package/dist/transforms-by-id/rename-object-keys-iterator-variables/manifest.json +4 -4
- package/dist/transforms-by-id/rename-object-keys-iterator-variables/rename-object-keys-iterator-variables-transform.js +3 -1
- package/dist/transforms-by-id/rename-return-object-bindings/collect-return-object-binding-candidates.js +16 -3
- package/dist/transforms-by-id/rename-uint8array-concat-variables/ast-helpers.d.ts +1 -1
- package/dist/transforms-by-id/rename-uint8array-concat-variables/ast-helpers.js +1 -5
- package/dist/transforms-by-id/rename-uint8array-concat-variables/find-loop-copy-match.d.ts +13 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/find-loop-copy-match.js +68 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/find-uint8array-concat-match.js +9 -3
- package/dist/transforms-by-id/rename-uint8array-concat-variables/find-uint8array-output-rename-matches.d.ts +14 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/find-uint8array-output-rename-matches.js +60 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/get-loop-set-and-offset-expressions.d.ts +3 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/get-loop-set-and-offset-expressions.js +28 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/has-local-dynamic-name-lookup.d.ts +3 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/has-local-dynamic-name-lookup.js +38 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/is-plus-equals-length-or-bytelength.d.ts +2 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/is-plus-equals-length-or-bytelength.js +25 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/is-set-member-expression.d.ts +2 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/is-set-member-expression.js +10 -0
- package/dist/transforms-by-id/rename-uint8array-concat-variables/loop-matches.d.ts +3 -3
- package/dist/transforms-by-id/rename-uint8array-concat-variables/loop-matches.js +36 -19
- package/dist/transforms-by-id/rename-uint8array-concat-variables/manifest.json +7 -7
- package/dist/transforms-by-id/rename-uint8array-concat-variables/rename-uint8array-concat-variables-transform.js +28 -2
- package/dist/transforms-by-id/stabilize-deferred-stable-rhs/manifest.json +6 -6
- package/dist/transforms-by-id/stabilize-deferred-stable-rhs/stabilize-deferred-stable-rhs-transform.js +33 -15
- package/dist/transforms-by-id/stabilize-nested-bindings/is-factory-callback.d.ts +7 -2
- package/dist/transforms-by-id/stabilize-nested-bindings/is-factory-callback.js +31 -5
- package/dist/transforms-by-id/stabilize-nested-bindings/manifest.json +5 -5
- package/dist/transforms-by-id/stabilize-nested-bindings/stabilize-nested-bindings-transform.js +3 -3
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"evaluations": {
|
|
3
3
|
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
4
|
-
"diffSizePercent":
|
|
5
|
-
"evaluatedAt": "2026-02-
|
|
6
|
-
"changedLines":
|
|
7
|
-
"durationSeconds": 38.
|
|
4
|
+
"diffSizePercent": 99.99055070491741,
|
|
5
|
+
"evaluatedAt": "2026-02-21T07:41:21.454Z",
|
|
6
|
+
"changedLines": 494,
|
|
7
|
+
"durationSeconds": 38.767131458,
|
|
8
8
|
"stableNames": 1359
|
|
9
9
|
}
|
|
10
10
|
},
|
|
@@ -5,6 +5,16 @@ const require = createRequire(import.meta.url);
|
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
6
6
|
const traverse = require("@babel/traverse").default;
|
|
7
7
|
const BASE_NAME = "commandResult";
|
|
8
|
+
const isAwaitedCallResultBinding = (binding) => {
|
|
9
|
+
if (!binding.path.isVariableDeclarator())
|
|
10
|
+
return false;
|
|
11
|
+
const init = binding.path.node.init;
|
|
12
|
+
if (!init)
|
|
13
|
+
return false;
|
|
14
|
+
if (init.type !== "AwaitExpression")
|
|
15
|
+
return false;
|
|
16
|
+
return init.argument.type === "CallExpression";
|
|
17
|
+
};
|
|
8
18
|
const isCommandResultProperty = (path) => {
|
|
9
19
|
const memberExpressionPath = path.parentPath;
|
|
10
20
|
if (!memberExpressionPath.isMemberExpression())
|
|
@@ -38,7 +48,11 @@ const hasCommandResultPattern = (binding) => {
|
|
|
38
48
|
hasOutputProperty = true;
|
|
39
49
|
}
|
|
40
50
|
}
|
|
41
|
-
|
|
51
|
+
if (!hasOutputProperty)
|
|
52
|
+
return false;
|
|
53
|
+
if (hasCodeProperty)
|
|
54
|
+
return true;
|
|
55
|
+
return isAwaitedCallResultBinding(binding);
|
|
42
56
|
};
|
|
43
57
|
export const renameCommandResultVariablesTransform = {
|
|
44
58
|
id: "rename-command-result-variables",
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"recommended": true,
|
|
3
|
-
"recommendedOrder": 100,
|
|
4
2
|
"notes": "Measured with baseline none: diff size remains 100.00% of baseline for this evaluation set. This transform remains recommended because it targets no-else error-first guards and is complementary to rename-error-first-callback-parameters-v2.",
|
|
5
3
|
"evaluations": {
|
|
6
4
|
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
7
5
|
"diffSizePercent": 100,
|
|
8
|
-
"evaluatedAt": "2026-02-
|
|
9
|
-
"changedLines":
|
|
10
|
-
"durationSeconds":
|
|
6
|
+
"evaluatedAt": "2026-02-21T06:37:23.328Z",
|
|
7
|
+
"changedLines": 428,
|
|
8
|
+
"durationSeconds": 142.79656364099998,
|
|
11
9
|
"stableNames": 1360
|
|
12
10
|
}
|
|
13
|
-
}
|
|
11
|
+
},
|
|
12
|
+
"recommended": true,
|
|
13
|
+
"recommendedOrder": 100
|
|
14
14
|
}
|
|
@@ -46,7 +46,9 @@ const matchesErrorFirstGuard = (path) => {
|
|
|
46
46
|
return undefined;
|
|
47
47
|
if (firstBinding.kind !== "param")
|
|
48
48
|
return undefined;
|
|
49
|
-
if (firstBinding.
|
|
49
|
+
if (firstBinding.constantViolations.length > 0)
|
|
50
|
+
return undefined;
|
|
51
|
+
if (firstBinding.referencePaths.length < 2)
|
|
50
52
|
return undefined;
|
|
51
53
|
const ifStatement = getErrorFirstIfStatement(path);
|
|
52
54
|
if (!ifStatement)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"recommended": true,
|
|
3
|
-
"recommendedOrder": 50,
|
|
4
2
|
"evaluations": {
|
|
5
3
|
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
6
4
|
"diffSizePercent": 100,
|
|
7
|
-
"evaluatedAt": "2026-02-
|
|
8
|
-
"changedLines":
|
|
9
|
-
"durationSeconds":
|
|
10
|
-
"stableNames":
|
|
5
|
+
"evaluatedAt": "2026-02-21T09:21:11.297Z",
|
|
6
|
+
"changedLines": 16,
|
|
7
|
+
"durationSeconds": 28.464535625,
|
|
8
|
+
"stableNames": 1358
|
|
11
9
|
}
|
|
12
|
-
}
|
|
10
|
+
},
|
|
11
|
+
"recommended": true,
|
|
12
|
+
"recommendedOrder": 50
|
|
13
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { RenameGroup, isStableRenamed } from "../../core/stable-naming.js";
|
|
3
3
|
import { getFilesToProcess, } from "../../core/types.js";
|
|
4
|
-
import { hasDynamicNameLookup } from "
|
|
4
|
+
import { hasDynamicNameLookup } from "../stabilize-nested-bindings/has-dynamic-name-lookup.js";
|
|
5
5
|
const require = createRequire(import.meta.url);
|
|
6
6
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
7
7
|
const traverse = require("@babel/traverse").default;
|
|
@@ -37,8 +37,6 @@ export const renameHttpMethodParametersTransform = {
|
|
|
37
37
|
let nodesVisited = 0;
|
|
38
38
|
let transformationsApplied = 0;
|
|
39
39
|
for (const fileInfo of getFilesToProcess(context)) {
|
|
40
|
-
if (hasDynamicNameLookup(fileInfo.ast))
|
|
41
|
-
continue;
|
|
42
40
|
const group = new RenameGroup();
|
|
43
41
|
traverse(fileInfo.ast, {
|
|
44
42
|
CallExpression(path) {
|
|
@@ -70,6 +68,8 @@ export const renameHttpMethodParametersTransform = {
|
|
|
70
68
|
if (!callbackPath.isFunctionExpression() &&
|
|
71
69
|
!callbackPath.isArrowFunctionExpression())
|
|
72
70
|
return;
|
|
71
|
+
if (hasDynamicNameLookup(callbackPath))
|
|
72
|
+
return;
|
|
73
73
|
const parameterPaths = callbackPath.get("params");
|
|
74
74
|
if (!Array.isArray(parameterPaths))
|
|
75
75
|
return;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"evaluations": {
|
|
3
3
|
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
4
|
-
"diffSizePercent":
|
|
5
|
-
"evaluatedAt": "2026-02-
|
|
6
|
-
"changedLines":
|
|
7
|
-
"durationSeconds":
|
|
4
|
+
"diffSizePercent": 99.99811014098348,
|
|
5
|
+
"evaluatedAt": "2026-02-21T09:45:41.024Z",
|
|
6
|
+
"changedLines": 1814,
|
|
7
|
+
"durationSeconds": 43.250947458999995,
|
|
8
8
|
"stableNames": 1359
|
|
9
9
|
}
|
|
10
10
|
},
|
|
@@ -73,8 +73,10 @@ const renameIteratorBinding = (path, identifier, group, options) => {
|
|
|
73
73
|
const binding = path.scope.getBinding(currentName);
|
|
74
74
|
if (!binding)
|
|
75
75
|
return;
|
|
76
|
-
if (
|
|
76
|
+
if (binding.kind === "var" &&
|
|
77
|
+
[...binding.referencePaths, ...binding.constantViolations].some((usagePath) => !usagePath.isDescendant(path))) {
|
|
77
78
|
return;
|
|
79
|
+
}
|
|
78
80
|
group.add({
|
|
79
81
|
scope: binding.scope,
|
|
80
82
|
currentName,
|
|
@@ -13,6 +13,19 @@ const getPropertyName = (key) => {
|
|
|
13
13
|
}
|
|
14
14
|
return undefined;
|
|
15
15
|
};
|
|
16
|
+
const getReturnObjectExpression = (argument) => {
|
|
17
|
+
if (!argument)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (argument.type === "ObjectExpression")
|
|
20
|
+
return argument;
|
|
21
|
+
if (argument.type !== "SequenceExpression")
|
|
22
|
+
return undefined;
|
|
23
|
+
const lastExpression = argument.expressions.at(-1);
|
|
24
|
+
if (lastExpression?.type !== "ObjectExpression") {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return lastExpression;
|
|
28
|
+
};
|
|
16
29
|
const collectCandidatesForObjectExpression = (options) => {
|
|
17
30
|
const { objectExpression, scope, isScript, exportedNames, candidates } = options;
|
|
18
31
|
for (const property of objectExpression.properties) {
|
|
@@ -68,11 +81,11 @@ export const collectReturnObjectBindingCandidates = (options) => {
|
|
|
68
81
|
traverse(ast, {
|
|
69
82
|
ReturnStatement(path) {
|
|
70
83
|
nodesVisited++;
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
84
|
+
const objectExpression = getReturnObjectExpression(path.node.argument);
|
|
85
|
+
if (!objectExpression)
|
|
73
86
|
return;
|
|
74
87
|
collectCandidatesForObjectExpression({
|
|
75
|
-
objectExpression
|
|
88
|
+
objectExpression,
|
|
76
89
|
scope: path.scope,
|
|
77
90
|
isScript,
|
|
78
91
|
exportedNames,
|
|
@@ -8,7 +8,7 @@ export declare const getBodyStatements: (bodyPath: NodePath<BlockStatement> | No
|
|
|
8
8
|
export declare const getZeroInitializedVariable: (path: NodePath<Statement>) => ZeroInitMatch | undefined;
|
|
9
9
|
export declare const getUint8ArrayInitializer: (path: NodePath<Statement>, totalLengthName: string) => ZeroInitMatch | undefined;
|
|
10
10
|
export declare const getLoopVariable: (path: NodePath<ForOfStatement>) => NodePath<Identifier> | undefined;
|
|
11
|
-
export declare const
|
|
11
|
+
export declare const getArrayExpression: (expression: Expression) => Expression;
|
|
12
12
|
export declare const isPlusEqualsLength: (expression: Expression, targetName: string, loopVariableName: string) => boolean;
|
|
13
13
|
export declare const isSetCall: (expression: Expression, outputName: string, loopVariableName: string, offsetName: string) => boolean;
|
|
14
14
|
export {};
|
|
@@ -61,11 +61,7 @@ export const getLoopVariable = (path) => {
|
|
|
61
61
|
return undefined;
|
|
62
62
|
return idPath;
|
|
63
63
|
};
|
|
64
|
-
export const
|
|
65
|
-
if (!t.isIdentifier(expression))
|
|
66
|
-
return undefined;
|
|
67
|
-
return expression.name;
|
|
68
|
-
};
|
|
64
|
+
export const getArrayExpression = (expression) => expression;
|
|
69
65
|
const isLengthAccess = (expression, objectName) => {
|
|
70
66
|
if (!t.isMemberExpression(expression))
|
|
71
67
|
return false;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Binding, NodePath, Scope } from "@babel/traverse";
|
|
2
|
+
import type { ForOfStatement } from "@babel/types";
|
|
3
|
+
export type LoopCopyMatch = {
|
|
4
|
+
outputBinding: Binding;
|
|
5
|
+
outputName: string;
|
|
6
|
+
offsetName: string;
|
|
7
|
+
offsetScope: Scope;
|
|
8
|
+
loopVariableName: string;
|
|
9
|
+
loopVarScope: Scope;
|
|
10
|
+
arrayName: string;
|
|
11
|
+
arrayScope: Scope | undefined;
|
|
12
|
+
};
|
|
13
|
+
export declare const findLoopCopyMatch: (loopPath: NodePath<ForOfStatement>) => LoopCopyMatch | undefined;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { getLoopSetAndOffsetExpressions } from "./get-loop-set-and-offset-expressions.js";
|
|
3
|
+
import { isPlusEqualsLengthOrByteLength } from "./is-plus-equals-length-or-bytelength.js";
|
|
4
|
+
import { isSetMemberExpression } from "./is-set-member-expression.js";
|
|
5
|
+
export const findLoopCopyMatch = (loopPath) => {
|
|
6
|
+
const leftPath = loopPath.get("left");
|
|
7
|
+
if (!leftPath.isVariableDeclaration())
|
|
8
|
+
return undefined;
|
|
9
|
+
if (leftPath.node.declarations.length !== 1)
|
|
10
|
+
return undefined;
|
|
11
|
+
const loopVariablePath = loopPath.get("left.declarations.0.id");
|
|
12
|
+
if (!loopVariablePath.isIdentifier())
|
|
13
|
+
return undefined;
|
|
14
|
+
const loopVariableName = loopVariablePath.node.name;
|
|
15
|
+
const bodyPath = loopPath.get("body");
|
|
16
|
+
const bodyStatements = bodyPath.isBlockStatement()
|
|
17
|
+
? bodyPath.get("body")
|
|
18
|
+
: [bodyPath];
|
|
19
|
+
const setAndOffsetExpressions = getLoopSetAndOffsetExpressions(bodyStatements);
|
|
20
|
+
if (!setAndOffsetExpressions)
|
|
21
|
+
return undefined;
|
|
22
|
+
const [setExpression, offsetExpression] = setAndOffsetExpressions;
|
|
23
|
+
if (!t.isCallExpression(setExpression))
|
|
24
|
+
return undefined;
|
|
25
|
+
if (!t.isMemberExpression(setExpression.callee))
|
|
26
|
+
return undefined;
|
|
27
|
+
if (!t.isIdentifier(setExpression.callee.object))
|
|
28
|
+
return undefined;
|
|
29
|
+
if (!isSetMemberExpression(setExpression.callee))
|
|
30
|
+
return undefined;
|
|
31
|
+
if (setExpression.arguments.length !== 2)
|
|
32
|
+
return undefined;
|
|
33
|
+
const [valueArgument, offsetArgument] = setExpression.arguments;
|
|
34
|
+
if (!valueArgument || !offsetArgument)
|
|
35
|
+
return undefined;
|
|
36
|
+
if (!t.isIdentifier(valueArgument, { name: loopVariableName }))
|
|
37
|
+
return undefined;
|
|
38
|
+
if (!t.isIdentifier(offsetArgument))
|
|
39
|
+
return undefined;
|
|
40
|
+
const outputName = setExpression.callee.object.name;
|
|
41
|
+
const offsetName = offsetArgument.name;
|
|
42
|
+
if (!isPlusEqualsLengthOrByteLength(offsetExpression, offsetName, loopVariableName)) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const outputBinding = loopPath.scope.getBinding(outputName);
|
|
46
|
+
if (!outputBinding)
|
|
47
|
+
return undefined;
|
|
48
|
+
const offsetBinding = loopPath.scope.getBinding(offsetName);
|
|
49
|
+
if (!offsetBinding)
|
|
50
|
+
return undefined;
|
|
51
|
+
const arrayName = t.isIdentifier(loopPath.node.right)
|
|
52
|
+
? loopPath.node.right.name
|
|
53
|
+
: "";
|
|
54
|
+
const arrayBinding = arrayName
|
|
55
|
+
? loopPath.scope.getBinding(arrayName)
|
|
56
|
+
: undefined;
|
|
57
|
+
const arrayScope = arrayBinding?.kind === "param" ? arrayBinding.scope : undefined;
|
|
58
|
+
return {
|
|
59
|
+
outputBinding,
|
|
60
|
+
outputName,
|
|
61
|
+
offsetName,
|
|
62
|
+
offsetScope: offsetBinding.scope,
|
|
63
|
+
loopVariableName,
|
|
64
|
+
loopVarScope: loopVariablePath.scope,
|
|
65
|
+
arrayName,
|
|
66
|
+
arrayScope,
|
|
67
|
+
};
|
|
68
|
+
};
|
package/dist/transforms-by-id/rename-uint8array-concat-variables/find-uint8array-concat-match.js
CHANGED
|
@@ -44,17 +44,23 @@ const findMatchesInStatementList = (functionPath, statementPaths) => {
|
|
|
44
44
|
const secondLoopMatch = getForOfSet(secondLoopStatement, outputMatch.name, offsetMatch.name);
|
|
45
45
|
if (!secondLoopMatch)
|
|
46
46
|
continue;
|
|
47
|
-
if (firstLoopMatch.
|
|
47
|
+
if (!t.isNodesEquivalent(firstLoopMatch.arrayExpression, secondLoopMatch.arrayExpression)) {
|
|
48
48
|
continue;
|
|
49
|
+
}
|
|
49
50
|
const returnName = getReturnName(returnStatement);
|
|
50
51
|
if (!returnName)
|
|
51
52
|
continue;
|
|
52
53
|
if (returnName !== outputMatch.name)
|
|
53
54
|
continue;
|
|
54
|
-
const
|
|
55
|
+
const arrayName = t.isIdentifier(firstLoopMatch.arrayExpression)
|
|
56
|
+
? firstLoopMatch.arrayExpression.name
|
|
57
|
+
: "";
|
|
58
|
+
const arrayBinding = arrayName
|
|
59
|
+
? functionPath.scope.getBinding(arrayName)
|
|
60
|
+
: undefined;
|
|
55
61
|
const arrayScope = arrayBinding?.kind === "param" ? arrayBinding.scope : undefined;
|
|
56
62
|
matches.push({
|
|
57
|
-
arrayName
|
|
63
|
+
arrayName,
|
|
58
64
|
arrayScope,
|
|
59
65
|
totalLengthName: totalLengthMatch.name,
|
|
60
66
|
totalLengthScope: totalLengthMatch.scope,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NodePath, Scope } from "@babel/traverse";
|
|
2
|
+
import type { Function } from "@babel/types";
|
|
3
|
+
type Uint8ArrayOutputRenameMatch = {
|
|
4
|
+
outputScope: Scope;
|
|
5
|
+
outputName: string;
|
|
6
|
+
offsetName: string;
|
|
7
|
+
offsetScope: Scope;
|
|
8
|
+
loopVariableName: string;
|
|
9
|
+
loopVarScope: Scope;
|
|
10
|
+
arrayName: string;
|
|
11
|
+
arrayScope: Scope | undefined;
|
|
12
|
+
};
|
|
13
|
+
export declare const findUint8ArrayOutputRenameMatches: (functionPath: NodePath<Function>) => Uint8ArrayOutputRenameMatch[];
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { findLoopCopyMatch, } from "./find-loop-copy-match.js";
|
|
3
|
+
export const findUint8ArrayOutputRenameMatches = (functionPath) => {
|
|
4
|
+
const uint8ArrayBindings = new Set();
|
|
5
|
+
const returnedBindings = new Set();
|
|
6
|
+
const loopMatches = [];
|
|
7
|
+
functionPath.traverse({
|
|
8
|
+
Function(innerPath) {
|
|
9
|
+
innerPath.skip();
|
|
10
|
+
},
|
|
11
|
+
VariableDeclarator(variablePath) {
|
|
12
|
+
const idPath = variablePath.get("id");
|
|
13
|
+
if (!idPath.isIdentifier())
|
|
14
|
+
return;
|
|
15
|
+
const initPath = variablePath.get("init");
|
|
16
|
+
if (!initPath.isNewExpression())
|
|
17
|
+
return;
|
|
18
|
+
if (!t.isIdentifier(initPath.node.callee, { name: "Uint8Array" })) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const binding = variablePath.scope.getBinding(idPath.node.name);
|
|
22
|
+
if (!binding)
|
|
23
|
+
return;
|
|
24
|
+
uint8ArrayBindings.add(binding);
|
|
25
|
+
},
|
|
26
|
+
ReturnStatement(returnPath) {
|
|
27
|
+
const argumentPath = returnPath.get("argument");
|
|
28
|
+
if (!argumentPath.isIdentifier())
|
|
29
|
+
return;
|
|
30
|
+
const binding = returnPath.scope.getBinding(argumentPath.node.name);
|
|
31
|
+
if (!binding)
|
|
32
|
+
return;
|
|
33
|
+
returnedBindings.add(binding);
|
|
34
|
+
},
|
|
35
|
+
ForOfStatement(loopPath) {
|
|
36
|
+
const loopMatch = findLoopCopyMatch(loopPath);
|
|
37
|
+
if (!loopMatch)
|
|
38
|
+
return;
|
|
39
|
+
loopMatches.push(loopMatch);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
const outputMatches = [];
|
|
43
|
+
for (const match of loopMatches) {
|
|
44
|
+
if (!uint8ArrayBindings.has(match.outputBinding))
|
|
45
|
+
continue;
|
|
46
|
+
if (!returnedBindings.has(match.outputBinding))
|
|
47
|
+
continue;
|
|
48
|
+
outputMatches.push({
|
|
49
|
+
outputScope: match.outputBinding.scope,
|
|
50
|
+
outputName: match.outputName,
|
|
51
|
+
offsetScope: match.offsetScope,
|
|
52
|
+
offsetName: match.offsetName,
|
|
53
|
+
loopVarScope: match.loopVarScope,
|
|
54
|
+
loopVariableName: match.loopVariableName,
|
|
55
|
+
arrayName: match.arrayName,
|
|
56
|
+
arrayScope: match.arrayScope,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return outputMatches;
|
|
60
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
export const getLoopSetAndOffsetExpressions = (statements) => {
|
|
3
|
+
if (statements.length === 2) {
|
|
4
|
+
const [setStatement, offsetStatement] = statements;
|
|
5
|
+
if (!setStatement || !offsetStatement)
|
|
6
|
+
return undefined;
|
|
7
|
+
if (!setStatement.isExpressionStatement())
|
|
8
|
+
return undefined;
|
|
9
|
+
if (!offsetStatement.isExpressionStatement())
|
|
10
|
+
return undefined;
|
|
11
|
+
return [setStatement.node.expression, offsetStatement.node.expression];
|
|
12
|
+
}
|
|
13
|
+
if (statements.length !== 1)
|
|
14
|
+
return undefined;
|
|
15
|
+
const [singleStatement] = statements;
|
|
16
|
+
if (!singleStatement)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (!singleStatement.isExpressionStatement())
|
|
19
|
+
return undefined;
|
|
20
|
+
if (!t.isSequenceExpression(singleStatement.node.expression))
|
|
21
|
+
return undefined;
|
|
22
|
+
if (singleStatement.node.expression.expressions.length !== 2)
|
|
23
|
+
return undefined;
|
|
24
|
+
const [setExpression, offsetExpression] = singleStatement.node.expression.expressions;
|
|
25
|
+
if (!setExpression || !offsetExpression)
|
|
26
|
+
return undefined;
|
|
27
|
+
return [setExpression, offsetExpression];
|
|
28
|
+
};
|
package/dist/transforms-by-id/rename-uint8array-concat-variables/has-local-dynamic-name-lookup.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { isEvalLikeCallCallee } from "../../transforms/dynamic-name-lookup/eval-like-call-detection.js";
|
|
3
|
+
export const hasLocalDynamicNameLookup = (functionPath) => {
|
|
4
|
+
let found = false;
|
|
5
|
+
functionPath.traverse({
|
|
6
|
+
WithStatement(path) {
|
|
7
|
+
found = true;
|
|
8
|
+
path.stop();
|
|
9
|
+
},
|
|
10
|
+
ReferencedIdentifier(path) {
|
|
11
|
+
if (found)
|
|
12
|
+
return;
|
|
13
|
+
if (!t.isIdentifier(path.node, { name: "eval" }))
|
|
14
|
+
return;
|
|
15
|
+
if (path.scope.getBinding("eval"))
|
|
16
|
+
return;
|
|
17
|
+
found = true;
|
|
18
|
+
path.stop();
|
|
19
|
+
},
|
|
20
|
+
CallExpression(path) {
|
|
21
|
+
if (found)
|
|
22
|
+
return;
|
|
23
|
+
if (!isEvalLikeCallCallee(path.node.callee, path.scope))
|
|
24
|
+
return;
|
|
25
|
+
found = true;
|
|
26
|
+
path.stop();
|
|
27
|
+
},
|
|
28
|
+
OptionalCallExpression(path) {
|
|
29
|
+
if (found)
|
|
30
|
+
return;
|
|
31
|
+
if (!isEvalLikeCallCallee(path.node.callee, path.scope))
|
|
32
|
+
return;
|
|
33
|
+
found = true;
|
|
34
|
+
path.stop();
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
return found;
|
|
38
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
const isLengthOrByteLengthAccess = (expression, objectName) => {
|
|
3
|
+
if (!t.isMemberExpression(expression))
|
|
4
|
+
return false;
|
|
5
|
+
if (!t.isIdentifier(expression.object, { name: objectName }))
|
|
6
|
+
return false;
|
|
7
|
+
if (!expression.computed && t.isIdentifier(expression.property)) {
|
|
8
|
+
return (expression.property.name === "length" ||
|
|
9
|
+
expression.property.name === "byteLength");
|
|
10
|
+
}
|
|
11
|
+
if (expression.computed && t.isStringLiteral(expression.property)) {
|
|
12
|
+
return (expression.property.value === "length" ||
|
|
13
|
+
expression.property.value === "byteLength");
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
17
|
+
export const isPlusEqualsLengthOrByteLength = (expression, targetName, loopVariableName) => {
|
|
18
|
+
if (!t.isAssignmentExpression(expression))
|
|
19
|
+
return false;
|
|
20
|
+
if (expression.operator !== "+=")
|
|
21
|
+
return false;
|
|
22
|
+
if (!t.isIdentifier(expression.left, { name: targetName }))
|
|
23
|
+
return false;
|
|
24
|
+
return isLengthOrByteLengthAccess(expression.right, loopVariableName);
|
|
25
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
export const isSetMemberExpression = (callee) => {
|
|
3
|
+
if (!callee.computed && t.isIdentifier(callee.property, { name: "set" })) {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
if (callee.computed && t.isStringLiteral(callee.property, { value: "set" })) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
return false;
|
|
10
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { NodePath, Scope } from "@babel/traverse";
|
|
2
|
-
import type { Statement } from "@babel/types";
|
|
2
|
+
import type { Expression, Statement } from "@babel/types";
|
|
3
3
|
type ForOfLengthSumMatch = {
|
|
4
|
-
|
|
4
|
+
arrayExpression: Expression;
|
|
5
5
|
loopVariableName: string;
|
|
6
6
|
loopVarScope: Scope;
|
|
7
7
|
};
|
|
8
8
|
type ForOfSetMatch = {
|
|
9
|
-
|
|
9
|
+
arrayExpression: Expression;
|
|
10
10
|
loopVariableName: string;
|
|
11
11
|
loopVarScope: Scope;
|
|
12
12
|
};
|
|
@@ -1,13 +1,39 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { getArrayExpression, getBodyStatements, getLoopVariable, isPlusEqualsLength, isSetCall, } from "./ast-helpers.js";
|
|
3
|
+
const getSetAndOffsetExpressions = (statements) => {
|
|
4
|
+
if (statements.length === 2) {
|
|
5
|
+
const [setStatement, offsetStatement] = statements;
|
|
6
|
+
if (!setStatement || !offsetStatement)
|
|
7
|
+
return undefined;
|
|
8
|
+
if (!setStatement.isExpressionStatement())
|
|
9
|
+
return undefined;
|
|
10
|
+
if (!offsetStatement.isExpressionStatement())
|
|
11
|
+
return undefined;
|
|
12
|
+
return [setStatement.node.expression, offsetStatement.node.expression];
|
|
13
|
+
}
|
|
14
|
+
if (statements.length !== 1)
|
|
15
|
+
return undefined;
|
|
16
|
+
const [singleStatement] = statements;
|
|
17
|
+
if (!singleStatement)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (!singleStatement.isExpressionStatement())
|
|
20
|
+
return undefined;
|
|
21
|
+
if (!t.isSequenceExpression(singleStatement.node.expression))
|
|
22
|
+
return undefined;
|
|
23
|
+
if (singleStatement.node.expression.expressions.length !== 2)
|
|
24
|
+
return undefined;
|
|
25
|
+
const [setExpression, offsetExpression] = singleStatement.node.expression.expressions;
|
|
26
|
+
if (!setExpression || !offsetExpression)
|
|
27
|
+
return undefined;
|
|
28
|
+
return [setExpression, offsetExpression];
|
|
29
|
+
};
|
|
2
30
|
export const getForOfLengthSum = (path, totalLengthName) => {
|
|
3
31
|
if (!path.isForOfStatement())
|
|
4
32
|
return undefined;
|
|
5
33
|
const loopVariablePath = getLoopVariable(path);
|
|
6
34
|
if (!loopVariablePath)
|
|
7
35
|
return undefined;
|
|
8
|
-
const
|
|
9
|
-
if (!arrayName)
|
|
10
|
-
return undefined;
|
|
36
|
+
const arrayExpression = getArrayExpression(path.node.right);
|
|
11
37
|
const statements = getBodyStatements(path.get("body"));
|
|
12
38
|
if (statements.length !== 1)
|
|
13
39
|
return undefined;
|
|
@@ -21,7 +47,7 @@ export const getForOfLengthSum = (path, totalLengthName) => {
|
|
|
21
47
|
return undefined;
|
|
22
48
|
}
|
|
23
49
|
return {
|
|
24
|
-
|
|
50
|
+
arrayExpression,
|
|
25
51
|
loopVariableName: loopVariablePath.node.name,
|
|
26
52
|
loopVarScope: loopVariablePath.scope,
|
|
27
53
|
};
|
|
@@ -32,29 +58,20 @@ export const getForOfSet = (path, outputName, offsetName) => {
|
|
|
32
58
|
const loopVariablePath = getLoopVariable(path);
|
|
33
59
|
if (!loopVariablePath)
|
|
34
60
|
return undefined;
|
|
35
|
-
const
|
|
36
|
-
if (!arrayName)
|
|
37
|
-
return undefined;
|
|
61
|
+
const arrayExpression = getArrayExpression(path.node.right);
|
|
38
62
|
const statements = getBodyStatements(path.get("body"));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const [setStatement, offsetStatement] = statements;
|
|
42
|
-
if (!setStatement || !offsetStatement)
|
|
43
|
-
return undefined;
|
|
44
|
-
if (!setStatement.isExpressionStatement())
|
|
45
|
-
return undefined;
|
|
46
|
-
if (!offsetStatement.isExpressionStatement())
|
|
63
|
+
const setAndOffsetExpressions = getSetAndOffsetExpressions(statements);
|
|
64
|
+
if (!setAndOffsetExpressions)
|
|
47
65
|
return undefined;
|
|
48
|
-
const setExpression =
|
|
66
|
+
const [setExpression, offsetExpression] = setAndOffsetExpressions;
|
|
49
67
|
if (!isSetCall(setExpression, outputName, loopVariablePath.node.name, offsetName)) {
|
|
50
68
|
return undefined;
|
|
51
69
|
}
|
|
52
|
-
const offsetExpression = offsetStatement.node.expression;
|
|
53
70
|
if (!isPlusEqualsLength(offsetExpression, offsetName, loopVariablePath.node.name)) {
|
|
54
71
|
return undefined;
|
|
55
72
|
}
|
|
56
73
|
return {
|
|
57
|
-
|
|
74
|
+
arrayExpression,
|
|
58
75
|
loopVariableName: loopVariablePath.node.name,
|
|
59
76
|
loopVarScope: loopVariablePath.scope,
|
|
60
77
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"recommended": true,
|
|
3
|
-
"recommendedOrder": 100,
|
|
4
2
|
"notes": "Measured with baseline none: 0.00%. Added to recommended for readability.",
|
|
5
3
|
"evaluations": {
|
|
6
4
|
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
7
5
|
"diffSizePercent": 100,
|
|
8
|
-
"evaluatedAt": "2026-02-
|
|
9
|
-
"changedLines":
|
|
10
|
-
"durationSeconds":
|
|
11
|
-
"stableNames":
|
|
6
|
+
"evaluatedAt": "2026-02-21T07:21:13.555Z",
|
|
7
|
+
"changedLines": 88,
|
|
8
|
+
"durationSeconds": 31.42075075,
|
|
9
|
+
"stableNames": 1361
|
|
12
10
|
}
|
|
13
|
-
}
|
|
11
|
+
},
|
|
12
|
+
"recommended": true,
|
|
13
|
+
"recommendedOrder": 100
|
|
14
14
|
}
|
|
@@ -2,6 +2,8 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { RenameGroup } from "../../core/stable-naming.js";
|
|
3
3
|
import { getFilesToProcess, } from "../../core/types.js";
|
|
4
4
|
import { findUint8ArrayConcatMatches } from "./find-uint8array-concat-match.js";
|
|
5
|
+
import { findUint8ArrayOutputRenameMatches } from "./find-uint8array-output-rename-matches.js";
|
|
6
|
+
import { hasLocalDynamicNameLookup } from "./has-local-dynamic-name-lookup.js";
|
|
5
7
|
const require = createRequire(import.meta.url);
|
|
6
8
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
7
9
|
const traverse = require("@babel/traverse").default;
|
|
@@ -30,9 +32,33 @@ export const renameUint8arrayConcatVariablesTransform = {
|
|
|
30
32
|
traverse(fileInfo.ast, {
|
|
31
33
|
Function(path) {
|
|
32
34
|
nodesVisited++;
|
|
33
|
-
|
|
34
|
-
if (matches.length === 0)
|
|
35
|
+
if (hasLocalDynamicNameLookup(path))
|
|
35
36
|
return;
|
|
37
|
+
for (const outputMatch of findUint8ArrayOutputRenameMatches(path)) {
|
|
38
|
+
if (outputMatch.arrayScope) {
|
|
39
|
+
addRenameOnce(renamedVariablesByScope, group, {
|
|
40
|
+
scope: outputMatch.arrayScope,
|
|
41
|
+
currentName: outputMatch.arrayName,
|
|
42
|
+
baseName: "chunks",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
addRenameOnce(renamedVariablesByScope, group, {
|
|
46
|
+
scope: outputMatch.outputScope,
|
|
47
|
+
currentName: outputMatch.outputName,
|
|
48
|
+
baseName: "bytes",
|
|
49
|
+
});
|
|
50
|
+
addRenameOnce(renamedVariablesByScope, group, {
|
|
51
|
+
scope: outputMatch.offsetScope,
|
|
52
|
+
currentName: outputMatch.offsetName,
|
|
53
|
+
baseName: "byteOffset",
|
|
54
|
+
});
|
|
55
|
+
addRenameOnce(renamedVariablesByScope, group, {
|
|
56
|
+
scope: outputMatch.loopVarScope,
|
|
57
|
+
currentName: outputMatch.loopVariableName,
|
|
58
|
+
baseName: "chunk",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const matches = findUint8ArrayConcatMatches(path);
|
|
36
62
|
for (const match of matches) {
|
|
37
63
|
if (match.arrayScope) {
|
|
38
64
|
addRenameOnce(renamedVariablesByScope, group, {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"notes": "Stabilizes deferred top-level
|
|
2
|
+
"notes": "Stabilizes deferred top-level var/let bindings whose RHS contains only stable identifiers (globals or $h_/$f_/$v_ prefixed names). Derives names by hashing the RHS code. Complements stabilize-deferred-top-level-bindings by handling multi-var functions.",
|
|
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":
|
|
9
|
-
"stableNames":
|
|
5
|
+
"diffSizePercent": 97.66602411460104,
|
|
6
|
+
"evaluatedAt": "2026-02-21T09:23:30.126Z",
|
|
7
|
+
"changedLines": 9309,
|
|
8
|
+
"durationSeconds": 44.433428834,
|
|
9
|
+
"stableNames": 2231
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
"recommended": true,
|
|
@@ -14,9 +14,25 @@ const traverse = require("@babel/traverse").default;
|
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
15
15
|
const generate = require("@babel/generator").default;
|
|
16
16
|
const hashCode = (code) => createHash("sha256").update(code).digest("hex").slice(0, 16);
|
|
17
|
+
const hasNameIndependentShadowingRisk = (bindingScope, bindingName) => {
|
|
18
|
+
const binding = bindingScope.getBinding(bindingName);
|
|
19
|
+
if (!binding)
|
|
20
|
+
return true;
|
|
21
|
+
const identifierPaths = [
|
|
22
|
+
...binding.referencePaths,
|
|
23
|
+
...binding.constantViolations,
|
|
24
|
+
];
|
|
25
|
+
return identifierPaths.some((identifierPath) => {
|
|
26
|
+
if (identifierPath.scope === bindingScope)
|
|
27
|
+
return false;
|
|
28
|
+
return (identifierPath.findParent((path) => {
|
|
29
|
+
return t.isWithStatement(path.node);
|
|
30
|
+
}) !== null);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
17
33
|
export const stabilizeDeferredStableRhsTransform = {
|
|
18
34
|
id: "stabilize-deferred-stable-rhs",
|
|
19
|
-
description: "Renames deferred top-level
|
|
35
|
+
description: "Renames deferred top-level var/let bindings whose RHS contains only stable identifiers " +
|
|
20
36
|
"(globals or $h_/$f_/$v_ prefixed). Derives stable names by hashing the RHS code. " +
|
|
21
37
|
"Complements stabilize-deferred-top-level-bindings by handling multi-var functions.",
|
|
22
38
|
scope: "file",
|
|
@@ -37,14 +53,14 @@ export const stabilizeDeferredStableRhsTransform = {
|
|
|
37
53
|
continue;
|
|
38
54
|
const fileProgramScope = programScope;
|
|
39
55
|
const programScopeBindings = new Set(Object.keys(fileProgramScope.bindings));
|
|
40
|
-
// Step 1: Find all uninitialized top-level
|
|
56
|
+
// Step 1: Find all uninitialized top-level var/let bindings that are not already stable
|
|
41
57
|
const uninitializedVariables = new Set();
|
|
42
58
|
traverse(fileInfo.ast, {
|
|
43
59
|
VariableDeclaration(path) {
|
|
44
60
|
nodesVisited++;
|
|
45
61
|
if (path.parent.type !== "Program")
|
|
46
62
|
return;
|
|
47
|
-
if (path.node.kind !== "var")
|
|
63
|
+
if (path.node.kind !== "var" && path.node.kind !== "let")
|
|
48
64
|
return;
|
|
49
65
|
for (const declaration of path.node.declarations) {
|
|
50
66
|
if (!declaration.init &&
|
|
@@ -55,7 +71,7 @@ export const stabilizeDeferredStableRhsTransform = {
|
|
|
55
71
|
}
|
|
56
72
|
},
|
|
57
73
|
});
|
|
58
|
-
// Step 2: Find assignments to those
|
|
74
|
+
// Step 2: Find assignments to those bindings inside functions.
|
|
59
75
|
// Exclude shadowed locals by requiring the assignment target to resolve
|
|
60
76
|
// to the program-scope binding for that name.
|
|
61
77
|
const assignments = [];
|
|
@@ -72,9 +88,6 @@ export const stabilizeDeferredStableRhsTransform = {
|
|
|
72
88
|
return;
|
|
73
89
|
if (path.scope.getBinding(variableName) !== programBinding)
|
|
74
90
|
return;
|
|
75
|
-
const functionPath = path.findParent((p) => p.isFunction());
|
|
76
|
-
if (!functionPath)
|
|
77
|
-
return;
|
|
78
91
|
// Check stability and capture RHS code NOW, before any renames happen.
|
|
79
92
|
// This prevents order-dependent cascading where renaming one var
|
|
80
93
|
// makes another var's RHS appear stable.
|
|
@@ -82,7 +95,6 @@ export const stabilizeDeferredStableRhsTransform = {
|
|
|
82
95
|
const isStable = hasAllStableReferences(rhsNode, programScopeBindings);
|
|
83
96
|
assignments.push({
|
|
84
97
|
variableName,
|
|
85
|
-
rhsNode,
|
|
86
98
|
rhsCode: isStable
|
|
87
99
|
? generate(rhsNode, { concise: true }).code
|
|
88
100
|
: undefined,
|
|
@@ -90,7 +102,7 @@ export const stabilizeDeferredStableRhsTransform = {
|
|
|
90
102
|
},
|
|
91
103
|
});
|
|
92
104
|
// Step 3: Apply renames for assignments with all-stable RHS.
|
|
93
|
-
// Deduplicate by variable name — if a
|
|
105
|
+
// Deduplicate by variable name — if a binding has multiple assignments,
|
|
94
106
|
// use only the first (order is AST traversal order).
|
|
95
107
|
// Track used target names to prevent collisions: two vars with identical
|
|
96
108
|
// RHS code produce the same hash, and renameBindingInPlace does not update
|
|
@@ -104,13 +116,19 @@ export const stabilizeDeferredStableRhsTransform = {
|
|
|
104
116
|
if (seen.has(variableName))
|
|
105
117
|
continue;
|
|
106
118
|
seen.add(variableName);
|
|
107
|
-
|
|
108
|
-
if (usedNewNames.has(newName))
|
|
109
|
-
continue;
|
|
110
|
-
if (!canRenameBindingSafely(programScope, variableName, newName))
|
|
119
|
+
if (hasNameIndependentShadowingRisk(programScope, variableName)) {
|
|
111
120
|
continue;
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
}
|
|
122
|
+
const baseName = `$v_${hashCode(rhsCode)}`;
|
|
123
|
+
let candidateName = baseName;
|
|
124
|
+
let suffix = 2;
|
|
125
|
+
while (usedNewNames.has(candidateName) ||
|
|
126
|
+
!canRenameBindingSafely(programScope, variableName, candidateName)) {
|
|
127
|
+
candidateName = `${baseName}_${suffix}`;
|
|
128
|
+
suffix++;
|
|
129
|
+
}
|
|
130
|
+
if (renameBindingInPlace(programScope, variableName, candidateName)) {
|
|
131
|
+
usedNewNames.add(candidateName);
|
|
114
132
|
fileTransformations++;
|
|
115
133
|
}
|
|
116
134
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import type { NodePath } from "@babel/traverse";
|
|
1
2
|
import type { ArrowFunctionExpression, FunctionExpression } from "@babel/types";
|
|
2
3
|
/**
|
|
3
4
|
* Checks if a function looks like a CommonJS factory callback.
|
|
4
|
-
* Heuristic:
|
|
5
|
+
* Heuristic:
|
|
6
|
+
* - 1-2 parameters: preserve existing broad behavior (first parameter must be an identifier).
|
|
7
|
+
* - 3 parameters: preserve broad matching, but reject known middleware
|
|
8
|
+
* handler signatures (req/res/next-like) that are outside this transform's
|
|
9
|
+
* intended scope.
|
|
5
10
|
*/
|
|
6
|
-
export declare const isFactoryCallback: (
|
|
11
|
+
export declare const isFactoryCallback: (callbackPath: NodePath<FunctionExpression | ArrowFunctionExpression>) => boolean;
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import * as t from "@babel/types";
|
|
2
|
+
const MIDDLEWARE_PARAMETER_SIGNATURES = new Set([
|
|
3
|
+
"req|res|next",
|
|
4
|
+
"request|response|next",
|
|
5
|
+
"ctx|next|error",
|
|
6
|
+
"context|next|error",
|
|
7
|
+
]);
|
|
8
|
+
const normalizeParameterName = (name) => name.replace(/^[$_]+/u, "").toLowerCase();
|
|
9
|
+
const isKnownMiddlewareSignature = (names) => MIDDLEWARE_PARAMETER_SIGNATURES.has(names.join("|"));
|
|
2
10
|
/**
|
|
3
11
|
* Checks if a function looks like a CommonJS factory callback.
|
|
4
|
-
* Heuristic:
|
|
12
|
+
* Heuristic:
|
|
13
|
+
* - 1-2 parameters: preserve existing broad behavior (first parameter must be an identifier).
|
|
14
|
+
* - 3 parameters: preserve broad matching, but reject known middleware
|
|
15
|
+
* handler signatures (req/res/next-like) that are outside this transform's
|
|
16
|
+
* intended scope.
|
|
5
17
|
*/
|
|
6
|
-
export const isFactoryCallback = (
|
|
7
|
-
const parameters =
|
|
8
|
-
if (parameters.length === 0 || parameters.length >
|
|
18
|
+
export const isFactoryCallback = (callbackPath) => {
|
|
19
|
+
const parameters = callbackPath.node.params;
|
|
20
|
+
if (parameters.length === 0 || parameters.length > 3)
|
|
9
21
|
return false;
|
|
10
|
-
|
|
22
|
+
const firstParameter = parameters[0];
|
|
23
|
+
if (!t.isIdentifier(firstParameter))
|
|
24
|
+
return false;
|
|
25
|
+
if (parameters.length < 3)
|
|
26
|
+
return true;
|
|
27
|
+
const secondParameter = parameters[1];
|
|
28
|
+
const thirdParameter = parameters[2];
|
|
29
|
+
if (!t.isIdentifier(secondParameter) || !t.isIdentifier(thirdParameter))
|
|
30
|
+
return true;
|
|
31
|
+
const normalizedParameterNames = [
|
|
32
|
+
normalizeParameterName(firstParameter.name),
|
|
33
|
+
normalizeParameterName(secondParameter.name),
|
|
34
|
+
normalizeParameterName(thirdParameter.name),
|
|
35
|
+
];
|
|
36
|
+
return !isKnownMiddlewareSignature(normalizedParameterNames);
|
|
11
37
|
};
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"notes": "Stabilizes local bindings inside CommonJS factory callbacks using content-hash naming ($f_<hash>). Must run AFTER stabilize-top-level-bindings. Combined with top-level stabilization, achieves 33.49% diff size (67% reduction from baseline).",
|
|
3
3
|
"evaluations": {
|
|
4
4
|
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
5
|
-
"diffSizePercent": 56.
|
|
6
|
-
"evaluatedAt": "2026-02-
|
|
7
|
-
"changedLines":
|
|
8
|
-
"durationSeconds":
|
|
9
|
-
"stableNames":
|
|
5
|
+
"diffSizePercent": 56.432135162716854,
|
|
6
|
+
"evaluatedAt": "2026-02-21T09:46:16.200Z",
|
|
7
|
+
"changedLines": 115623,
|
|
8
|
+
"durationSeconds": 81.049385083,
|
|
9
|
+
"stableNames": 13327
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
"recommended": true,
|
package/dist/transforms-by-id/stabilize-nested-bindings/stabilize-nested-bindings-transform.js
CHANGED
|
@@ -19,14 +19,14 @@ const renameCallbackLocalBindings = (path) => {
|
|
|
19
19
|
if (!t.isFunctionExpression(firstArgument) &&
|
|
20
20
|
!t.isArrowFunctionExpression(firstArgument))
|
|
21
21
|
return 0;
|
|
22
|
-
// Verify it looks like a callback shape this transform should process
|
|
23
|
-
if (!isFactoryCallback(firstArgument))
|
|
24
|
-
return 0;
|
|
25
22
|
// Get the path to the function argument
|
|
26
23
|
const functionArgumentPath = path.get("arguments.0");
|
|
27
24
|
if (!functionArgumentPath.isFunctionExpression() &&
|
|
28
25
|
!functionArgumentPath.isArrowFunctionExpression())
|
|
29
26
|
return 0;
|
|
27
|
+
// Verify it looks like a callback shape this transform should process
|
|
28
|
+
if (!isFactoryCallback(functionArgumentPath))
|
|
29
|
+
return 0;
|
|
30
30
|
// Skip renaming if callback contains dynamic name lookup (eval, with, etc.)
|
|
31
31
|
if (hasDynamicNameLookup(functionArgumentPath))
|
|
32
32
|
return 0;
|
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.115.
|
|
5
|
+
"version": "1.115.4",
|
|
6
6
|
"description": "Transform minified JavaScript/TypeScript into a more readable form using deterministic AST-based transforms.",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|