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.
@@ -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,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "recommended": {
3
- "diffReductionImpact": 0.001470172462538888,
3
+ "diffReductionImpact": 0.0015078691923475773,
4
4
  "notes": "Measured with baseline none: 0.15%."
5
5
  }
6
6
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "diffReductionImpact": 0,
3
+ "recommended": true
4
+ }
@@ -0,0 +1,2 @@
1
+ import { type Transform } from "../../core/types.js";
2
+ export declare const renameRestParametersTransform: Transform;
@@ -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,6 @@
1
+ {
2
+ "diffReductionImpact": 0,
3
+ "recommended": true,
4
+ "evaluatedAt": "2026-01-24T15:52:58.710Z",
5
+ "notes": "Measured with baseline none: 0.00%. Added to recommended for readability."
6
+ }
@@ -0,0 +1,2 @@
1
+ import { type Transform } from "../../core/types.js";
2
+ export declare const renameUrlParametersTransform: Transform;
@@ -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.23.0",
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",