miniread 1.23.0 → 1.25.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/core/stable-naming.js +19 -2
- package/dist/transforms/_generated/manifest.js +10 -0
- package/dist/transforms/_generated/registry.js +4 -0
- package/dist/transforms/preset-stats.json +1 -1
- package/dist/transforms/rename-rest-parameters/manifest.json +4 -0
- package/dist/transforms/rename-rest-parameters/rename-rest-parameters-transform.d.ts +2 -0
- package/dist/transforms/rename-rest-parameters/rename-rest-parameters-transform.js +62 -0
- package/dist/transforms/rename-url-parameters/manifest.json +6 -0
- package/dist/transforms/rename-url-parameters/rename-url-parameters-transform.d.ts +2 -0
- package/dist/transforms/rename-url-parameters/rename-url-parameters-transform.js +135 -0
- package/package.json +1 -1
|
@@ -22,10 +22,23 @@ export const isStableRenamed = (name) => {
|
|
|
22
22
|
const makeStableName = (baseName) => {
|
|
23
23
|
return `${STABLE_PREFIX}${baseName}`;
|
|
24
24
|
};
|
|
25
|
+
const hasShadowingRisk = (binding, bindingScope, targetName) => binding.referencePaths.some((referencePath) => {
|
|
26
|
+
if (referencePath.scope === bindingScope)
|
|
27
|
+
return false;
|
|
28
|
+
// Only consider bindings between this reference’s scope and the binding scope.
|
|
29
|
+
// This avoids incorrectly blocking safe renames due to unrelated outer bindings.
|
|
30
|
+
const hasBindingOptions = {
|
|
31
|
+
noGlobals: true,
|
|
32
|
+
noUids: true,
|
|
33
|
+
// Present in Babel scope implementation, but missing from our TypeScript types.
|
|
34
|
+
upToScope: bindingScope,
|
|
35
|
+
};
|
|
36
|
+
return referencePath.scope.hasBinding(targetName, hasBindingOptions);
|
|
37
|
+
});
|
|
25
38
|
/**
|
|
26
39
|
* Finds the next available name slot in a scope.
|
|
27
40
|
*/
|
|
28
|
-
const findAvailableName = (scope, baseName, startIndex, canBeStable) => {
|
|
41
|
+
const findAvailableName = (scope, binding, baseName, startIndex, canBeStable) => {
|
|
29
42
|
let index = startIndex;
|
|
30
43
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
31
44
|
while (true) {
|
|
@@ -39,6 +52,10 @@ const findAvailableName = (scope, baseName, startIndex, canBeStable) => {
|
|
|
39
52
|
if (!stableTaken && !readableTaken) {
|
|
40
53
|
// This slot is available
|
|
41
54
|
const name = canBeStable ? candidateStable : candidateReadable;
|
|
55
|
+
if (binding && hasShadowingRisk(binding, scope, name)) {
|
|
56
|
+
index++;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
42
59
|
return { name, nextIndex: index + 1 };
|
|
43
60
|
}
|
|
44
61
|
index++;
|
|
@@ -107,7 +124,7 @@ export class RenameGroup {
|
|
|
107
124
|
let index = 1;
|
|
108
125
|
for (const entry of group) {
|
|
109
126
|
// Find next available name slot
|
|
110
|
-
const targetName = findAvailableName(scope, baseName, index, canBeStable);
|
|
127
|
+
const targetName = findAvailableName(scope, scope.getBinding(entry.currentName), baseName, index, canBeStable);
|
|
111
128
|
index = targetName.nextIndex;
|
|
112
129
|
if (entry.currentName !== targetName.name) {
|
|
113
130
|
scope.rename(entry.currentName, targetName.name);
|
|
@@ -130,6 +130,10 @@ const manifestData = {
|
|
|
130
130
|
evaluatedAt: "2026-01-23T17:28:09.184Z",
|
|
131
131
|
notes: "Measured with baseline none: 0.00%.",
|
|
132
132
|
},
|
|
133
|
+
"rename-rest-parameters": {
|
|
134
|
+
diffReductionImpact: 0,
|
|
135
|
+
recommended: true,
|
|
136
|
+
},
|
|
133
137
|
"rename-this-aliases": {
|
|
134
138
|
diffReductionImpact: 0.00003774938185385768,
|
|
135
139
|
recommended: true,
|
|
@@ -146,6 +150,12 @@ const manifestData = {
|
|
|
146
150
|
diffReductionImpact: 0,
|
|
147
151
|
recommended: true,
|
|
148
152
|
},
|
|
153
|
+
"rename-url-parameters": {
|
|
154
|
+
diffReductionImpact: 0,
|
|
155
|
+
recommended: true,
|
|
156
|
+
evaluatedAt: "2026-01-24T15:52:58.710Z",
|
|
157
|
+
notes: "Measured with baseline none: 0.00%. Added to recommended for readability.",
|
|
158
|
+
},
|
|
149
159
|
"rename-use-reference-guards": {
|
|
150
160
|
diffReductionImpact: 0,
|
|
151
161
|
recommended: false,
|
|
@@ -21,9 +21,11 @@ import { renameParametersToMatchPropertiesTransform } from "../rename-parameters
|
|
|
21
21
|
import { renameParametersToMatchPropertiesV2Transform } from "../rename-parameters-to-match-properties-v2/rename-parameters-to-match-properties-v2-transform.js";
|
|
22
22
|
import { renamePromiseExecutorParametersTransform } from "../rename-promise-executor-parameters/rename-promise-executor-parameters-transform.js";
|
|
23
23
|
import { renameReplaceChildParametersTransform } from "../rename-replace-child-parameters/rename-replace-child-parameters-transform.js";
|
|
24
|
+
import { renameRestParametersTransform } from "../rename-rest-parameters/rename-rest-parameters-transform.js";
|
|
24
25
|
import { renameThisAliasesTransform } from "../rename-this-aliases/rename-this-aliases-transform.js";
|
|
25
26
|
import { renameTimeoutIdsTransform } from "../rename-timeout-ids/rename-timeout-ids-transform.js";
|
|
26
27
|
import { renameTypeofVariablesTransform } from "../rename-typeof-variables/rename-typeof-variables-transform.js";
|
|
28
|
+
import { renameUrlParametersTransform } from "../rename-url-parameters/rename-url-parameters-transform.js";
|
|
27
29
|
import { renameUseReferenceGuardsTransform } from "../rename-use-reference-guards/rename-use-reference-guards-transform.js";
|
|
28
30
|
import { renameUseReferenceGuardsV2Transform } from "../rename-use-reference-guards-v2/rename-use-reference-guards-v2-transform.js";
|
|
29
31
|
import { simplifyBooleanNegationsTransform } from "../simplify-boolean-negations/simplify-boolean-negations-transform.js";
|
|
@@ -50,9 +52,11 @@ export const transformRegistry = {
|
|
|
50
52
|
[renameParametersToMatchPropertiesV2Transform.id]: renameParametersToMatchPropertiesV2Transform,
|
|
51
53
|
[renamePromiseExecutorParametersTransform.id]: renamePromiseExecutorParametersTransform,
|
|
52
54
|
[renameReplaceChildParametersTransform.id]: renameReplaceChildParametersTransform,
|
|
55
|
+
[renameRestParametersTransform.id]: renameRestParametersTransform,
|
|
53
56
|
[renameThisAliasesTransform.id]: renameThisAliasesTransform,
|
|
54
57
|
[renameTimeoutIdsTransform.id]: renameTimeoutIdsTransform,
|
|
55
58
|
[renameTypeofVariablesTransform.id]: renameTypeofVariablesTransform,
|
|
59
|
+
[renameUrlParametersTransform.id]: renameUrlParametersTransform,
|
|
56
60
|
[renameUseReferenceGuardsTransform.id]: renameUseReferenceGuardsTransform,
|
|
57
61
|
[renameUseReferenceGuardsV2Transform.id]: renameUseReferenceGuardsV2Transform,
|
|
58
62
|
[simplifyBooleanNegationsTransform.id]: simplifyBooleanNegationsTransform,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { isIdentifier } from "@babel/types";
|
|
3
|
+
import { RenameGroup, isStableRenamed } from "../../core/stable-naming.js";
|
|
4
|
+
import { getFilesToProcess, } from "../../core/types.js";
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
7
|
+
const traverse = require("@babel/traverse").default;
|
|
8
|
+
const BASE_NAME = "args";
|
|
9
|
+
const getRestParameter = (parameters) => {
|
|
10
|
+
const lastParameter = parameters.at(-1);
|
|
11
|
+
if (!lastParameter)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (lastParameter.type !== "RestElement")
|
|
14
|
+
return undefined;
|
|
15
|
+
return lastParameter;
|
|
16
|
+
};
|
|
17
|
+
export const renameRestParametersTransform = {
|
|
18
|
+
id: "rename-rest-parameters",
|
|
19
|
+
description: "Renames rest parameters to $args (stable when unique)",
|
|
20
|
+
scope: "file",
|
|
21
|
+
parallelizable: true,
|
|
22
|
+
transform(context) {
|
|
23
|
+
let nodesVisited = 0;
|
|
24
|
+
let transformationsApplied = 0;
|
|
25
|
+
for (const fileInfo of getFilesToProcess(context)) {
|
|
26
|
+
const group = new RenameGroup();
|
|
27
|
+
traverse(fileInfo.ast, {
|
|
28
|
+
Function(path) {
|
|
29
|
+
nodesVisited++;
|
|
30
|
+
const restParameter = getRestParameter(path.node.params);
|
|
31
|
+
if (!restParameter)
|
|
32
|
+
return;
|
|
33
|
+
const argument = restParameter.argument;
|
|
34
|
+
if (!isIdentifier(argument))
|
|
35
|
+
return;
|
|
36
|
+
const currentName = argument.name;
|
|
37
|
+
if (isStableRenamed(currentName))
|
|
38
|
+
return;
|
|
39
|
+
if (currentName.length > 2)
|
|
40
|
+
return;
|
|
41
|
+
const binding = path.scope.getBinding(currentName);
|
|
42
|
+
if (!binding)
|
|
43
|
+
return;
|
|
44
|
+
if (!binding.constant)
|
|
45
|
+
return;
|
|
46
|
+
if (binding.referencePaths.length === 0)
|
|
47
|
+
return;
|
|
48
|
+
group.add({
|
|
49
|
+
scope: path.scope,
|
|
50
|
+
currentName,
|
|
51
|
+
baseName: BASE_NAME,
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
transformationsApplied += group.apply();
|
|
56
|
+
}
|
|
57
|
+
return Promise.resolve({
|
|
58
|
+
nodesVisited,
|
|
59
|
+
transformationsApplied,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { isIdentifierName, isKeyword, isStrictBindReservedWord, } from "@babel/helper-validator-identifier";
|
|
3
|
+
import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
|
|
4
|
+
import { getFilesToProcess, } from "../../core/types.js";
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
7
|
+
const traverse = require("@babel/traverse").default;
|
|
8
|
+
const urlPropertyNames = new Set([
|
|
9
|
+
"hash",
|
|
10
|
+
"host",
|
|
11
|
+
"hostname",
|
|
12
|
+
"href",
|
|
13
|
+
"origin",
|
|
14
|
+
"password",
|
|
15
|
+
"pathname",
|
|
16
|
+
"port",
|
|
17
|
+
"protocol",
|
|
18
|
+
"search",
|
|
19
|
+
"searchParams",
|
|
20
|
+
"username",
|
|
21
|
+
]);
|
|
22
|
+
const countUrlProperties = (pattern) => {
|
|
23
|
+
let count = 0;
|
|
24
|
+
for (const property of pattern.properties) {
|
|
25
|
+
if (property.type !== "ObjectProperty")
|
|
26
|
+
continue;
|
|
27
|
+
if (property.computed)
|
|
28
|
+
continue;
|
|
29
|
+
const key = property.key;
|
|
30
|
+
const keyName = key.type === "Identifier"
|
|
31
|
+
? key.name
|
|
32
|
+
: key.type === "StringLiteral"
|
|
33
|
+
? key.value
|
|
34
|
+
: undefined;
|
|
35
|
+
if (keyName && urlPropertyNames.has(keyName)) {
|
|
36
|
+
count += 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return count;
|
|
40
|
+
};
|
|
41
|
+
const hasUrlDestructure = (referencePath) => {
|
|
42
|
+
const parent = referencePath.parentPath;
|
|
43
|
+
if (!parent)
|
|
44
|
+
return false;
|
|
45
|
+
if (parent.isVariableDeclarator() &&
|
|
46
|
+
parent.node.init === referencePath.node &&
|
|
47
|
+
parent.node.id.type === "ObjectPattern") {
|
|
48
|
+
return countUrlProperties(parent.node.id) >= 2;
|
|
49
|
+
}
|
|
50
|
+
if (parent.isAssignmentExpression() &&
|
|
51
|
+
parent.node.operator === "=" &&
|
|
52
|
+
parent.node.right === referencePath.node &&
|
|
53
|
+
parent.node.left.type === "ObjectPattern") {
|
|
54
|
+
return countUrlProperties(parent.node.left) >= 2;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
const hasUrlConstruction = (referencePath) => {
|
|
59
|
+
const parent = referencePath.parentPath;
|
|
60
|
+
if (!parent?.isNewExpression())
|
|
61
|
+
return false;
|
|
62
|
+
if (parent.node.callee.type !== "Identifier" ||
|
|
63
|
+
parent.node.callee.name !== "URL") {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (parent.node.arguments.length === 0 || parent.node.arguments.length > 2) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return parent.node.arguments[0] === referencePath.node;
|
|
70
|
+
};
|
|
71
|
+
export const renameUrlParametersTransform = {
|
|
72
|
+
id: "rename-url-parameters",
|
|
73
|
+
description: "Renames parameters that are normalized with new URL(...) and destructured into URL fields",
|
|
74
|
+
scope: "file",
|
|
75
|
+
parallelizable: true,
|
|
76
|
+
transform(context) {
|
|
77
|
+
let nodesVisited = 0;
|
|
78
|
+
let transformationsApplied = 0;
|
|
79
|
+
for (const fileInfo of getFilesToProcess(context)) {
|
|
80
|
+
const group = new RenameGroup();
|
|
81
|
+
traverse(fileInfo.ast, {
|
|
82
|
+
Function(path) {
|
|
83
|
+
nodesVisited++;
|
|
84
|
+
for (const parameterPath of path.get("params")) {
|
|
85
|
+
if (!parameterPath.isIdentifier())
|
|
86
|
+
continue;
|
|
87
|
+
const currentName = parameterPath.node.name;
|
|
88
|
+
if (isStableRenamed(currentName))
|
|
89
|
+
continue;
|
|
90
|
+
const binding = path.scope.getBinding(currentName);
|
|
91
|
+
if (!binding)
|
|
92
|
+
continue;
|
|
93
|
+
let sawUrlConstruction = false;
|
|
94
|
+
let sawUrlDestructure = false;
|
|
95
|
+
for (const referencePath of binding.referencePaths) {
|
|
96
|
+
if (!sawUrlConstruction && hasUrlConstruction(referencePath)) {
|
|
97
|
+
sawUrlConstruction = true;
|
|
98
|
+
}
|
|
99
|
+
if (!sawUrlDestructure && hasUrlDestructure(referencePath)) {
|
|
100
|
+
sawUrlDestructure = true;
|
|
101
|
+
}
|
|
102
|
+
if (sawUrlConstruction && sawUrlDestructure)
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
if (!sawUrlConstruction || !sawUrlDestructure)
|
|
106
|
+
continue;
|
|
107
|
+
const candidateName = "url";
|
|
108
|
+
if (candidateName === currentName ||
|
|
109
|
+
!isIdentifierName(candidateName) ||
|
|
110
|
+
isKeyword(candidateName) ||
|
|
111
|
+
isStrictBindReservedWord(candidateName, true)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const programScope = path.scope.getProgramParent();
|
|
115
|
+
if (Object.hasOwn(programScope.globals, candidateName))
|
|
116
|
+
continue;
|
|
117
|
+
// `RenameGroup` avoids collisions for both the readable and stable forms
|
|
118
|
+
// (e.g. `url` and `$url`) across the scope chain, so we don't need an
|
|
119
|
+
// additional `wouldShadowReferencedOuterBinding` check here.
|
|
120
|
+
group.add({
|
|
121
|
+
scope: path.scope,
|
|
122
|
+
currentName,
|
|
123
|
+
baseName: candidateName,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
transformationsApplied += group.apply();
|
|
129
|
+
}
|
|
130
|
+
return Promise.resolve({
|
|
131
|
+
nodesVisited,
|
|
132
|
+
transformationsApplied,
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
};
|
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.25.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",
|