miniread 1.36.0 → 1.38.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.
@@ -192,6 +192,12 @@ const manifestData = {
192
192
  evaluatedAt: "2026-01-23T17:57:26.908Z",
193
193
  notes: "Measured with baseline none: 0.00%. Improves readability by stabilizing `this` aliases used for closures.",
194
194
  },
195
+ "rename-timeout-duration-parameters": {
196
+ diffReductionImpact: -0.000018848364904400228,
197
+ recommended: false,
198
+ evaluatedAt: "2026-01-24T23:36:14.192Z",
199
+ notes: "Negative diff reduction; leaving out of recommended preset.",
200
+ },
195
201
  "rename-timeout-ids": {
196
202
  diffReductionImpact: 0.00003774938185385768,
197
203
  recommended: true,
@@ -214,6 +220,12 @@ const manifestData = {
214
220
  evaluatedAt: "2026-01-24T15:52:58.710Z",
215
221
  notes: "Measured with baseline none: 0.00%. Added to recommended for readability.",
216
222
  },
223
+ "rename-url-variables": {
224
+ diffReductionImpact: 0.000018848364904289205,
225
+ recommended: true,
226
+ evaluatedAt: "2026-01-24T23:50:56.857Z",
227
+ notes: "Measured with baseline none: 0.00%. Added to recommended for readability.",
228
+ },
217
229
  "rename-use-reference-guards": {
218
230
  diffReductionImpact: 0,
219
231
  recommended: false,
@@ -32,10 +32,12 @@ import { renameRegexBuildersTransform } from "../rename-regex-builders/rename-re
32
32
  import { renameReplaceChildParametersTransform } from "../rename-replace-child-parameters/rename-replace-child-parameters-transform.js";
33
33
  import { renameRestParametersTransform } from "../rename-rest-parameters/rename-rest-parameters-transform.js";
34
34
  import { renameThisAliasesTransform } from "../rename-this-aliases/rename-this-aliases-transform.js";
35
+ import { renameTimeoutDurationParametersTransform } from "../rename-timeout-duration-parameters/rename-timeout-duration-parameters-transform.js";
35
36
  import { renameTimeoutIdsTransform } from "../rename-timeout-ids/rename-timeout-ids-transform.js";
36
37
  import { renameTypeofVariablesTransform } from "../rename-typeof-variables/rename-typeof-variables-transform.js";
37
38
  import { renameUint8arrayConcatVariablesTransform } from "../rename-uint8array-concat-variables/rename-uint8array-concat-variables-transform.js";
38
39
  import { renameUrlParametersTransform } from "../rename-url-parameters/rename-url-parameters-transform.js";
40
+ import { renameUrlVariablesTransform } from "../rename-url-variables/rename-url-variables-transform.js";
39
41
  import { renameUseReferenceGuardsTransform } from "../rename-use-reference-guards/rename-use-reference-guards-transform.js";
40
42
  import { renameUseReferenceGuardsV2Transform } from "../rename-use-reference-guards-v2/rename-use-reference-guards-v2-transform.js";
41
43
  import { simplifyBooleanNegationsTransform } from "../simplify-boolean-negations/simplify-boolean-negations-transform.js";
@@ -75,10 +77,12 @@ export const transformRegistry = {
75
77
  [renameReplaceChildParametersTransform.id]: renameReplaceChildParametersTransform,
76
78
  [renameRestParametersTransform.id]: renameRestParametersTransform,
77
79
  [renameThisAliasesTransform.id]: renameThisAliasesTransform,
80
+ [renameTimeoutDurationParametersTransform.id]: renameTimeoutDurationParametersTransform,
78
81
  [renameTimeoutIdsTransform.id]: renameTimeoutIdsTransform,
79
82
  [renameTypeofVariablesTransform.id]: renameTypeofVariablesTransform,
80
83
  [renameUint8arrayConcatVariablesTransform.id]: renameUint8arrayConcatVariablesTransform,
81
84
  [renameUrlParametersTransform.id]: renameUrlParametersTransform,
85
+ [renameUrlVariablesTransform.id]: renameUrlVariablesTransform,
82
86
  [renameUseReferenceGuardsTransform.id]: renameUseReferenceGuardsTransform,
83
87
  [renameUseReferenceGuardsV2Transform.id]: renameUseReferenceGuardsV2Transform,
84
88
  [simplifyBooleanNegationsTransform.id]: simplifyBooleanNegationsTransform,
@@ -0,0 +1,6 @@
1
+ {
2
+ "diffReductionImpact": -0.000018848364904400228,
3
+ "recommended": false,
4
+ "evaluatedAt": "2026-01-24T23:36:14.192Z",
5
+ "notes": "Negative diff reduction; leaving out of recommended preset."
6
+ }
@@ -0,0 +1,2 @@
1
+ import { type Transform } from "../../core/types.js";
2
+ export declare const renameTimeoutDurationParametersTransform: Transform;
@@ -0,0 +1,137 @@
1
+ import { createRequire } from "node:module";
2
+ import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
3
+ import { getFilesToProcess, } from "../../core/types.js";
4
+ const require = createRequire(import.meta.url);
5
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
6
+ const traverse = require("@babel/traverse").default;
7
+ const BASE_NAME = "timeoutMs";
8
+ const getParameterIdentifier = (parameter) => {
9
+ if (parameter.type === "Identifier")
10
+ return parameter;
11
+ if (parameter.type !== "AssignmentPattern")
12
+ return undefined;
13
+ const { left } = parameter;
14
+ if (left.type !== "Identifier")
15
+ return undefined;
16
+ return left;
17
+ };
18
+ const isSetTimeoutDelayReference = (referencePath, bindingName) => {
19
+ if (!referencePath.isIdentifier())
20
+ return false;
21
+ const callPath = referencePath.parentPath;
22
+ if (!callPath.isCallExpression())
23
+ return false;
24
+ if (referencePath.listKey !== "arguments")
25
+ return false;
26
+ if (typeof referencePath.key !== "number")
27
+ return false;
28
+ if (referencePath.key !== 1)
29
+ return false;
30
+ const call = callPath.node;
31
+ if (call.callee.type !== "Identifier")
32
+ return false;
33
+ if (call.callee.name !== "setTimeout")
34
+ return false;
35
+ if (callPath.scope.hasBinding("setTimeout", true))
36
+ return false;
37
+ return referencePath.node.name === bindingName;
38
+ };
39
+ const isIfTestReference = (referencePath) => {
40
+ if (!referencePath.isIdentifier())
41
+ return false;
42
+ const parentPath = referencePath.parentPath;
43
+ if (parentPath.isIfStatement()) {
44
+ return parentPath.node.test === referencePath.node;
45
+ }
46
+ if (parentPath.isUnaryExpression({ operator: "!" })) {
47
+ const ifPath = parentPath.parentPath;
48
+ if (!ifPath.isIfStatement())
49
+ return false;
50
+ if (ifPath.node.test !== parentPath.node)
51
+ return false;
52
+ return parentPath.node.argument === referencePath.node;
53
+ }
54
+ if (parentPath.isBinaryExpression()) {
55
+ const ifPath = parentPath.parentPath;
56
+ if (!ifPath.isIfStatement())
57
+ return false;
58
+ if (ifPath.node.test !== parentPath.node)
59
+ return false;
60
+ return (parentPath.node.left === referencePath.node ||
61
+ parentPath.node.right === referencePath.node);
62
+ }
63
+ return false;
64
+ };
65
+ const isLetter = (char) => /^[a-z]$/iu.test(char);
66
+ const containsMsUnit = (raw) => {
67
+ let index = raw.indexOf("ms");
68
+ while (index !== -1) {
69
+ const before = raw[index - 1];
70
+ const after = raw[index + 2];
71
+ const beforeOk = before === undefined || !isLetter(before);
72
+ const afterOk = after === undefined || !isLetter(after);
73
+ if (beforeOk && afterOk)
74
+ return true;
75
+ index = raw.indexOf("ms", index + 2);
76
+ }
77
+ return false;
78
+ };
79
+ const isTimeoutTemplateReference = (referencePath) => {
80
+ if (!referencePath.isIdentifier())
81
+ return false;
82
+ const parentPath = referencePath.parentPath;
83
+ if (!parentPath.isTemplateLiteral())
84
+ return false;
85
+ const template = parentPath.node;
86
+ return template.quasis.some((quasi) => containsMsUnit(quasi.value.raw));
87
+ };
88
+ const isAllowedTimeoutReference = (referencePath, bindingName) => {
89
+ return (isSetTimeoutDelayReference(referencePath, bindingName) ||
90
+ isIfTestReference(referencePath) ||
91
+ isTimeoutTemplateReference(referencePath));
92
+ };
93
+ export const renameTimeoutDurationParametersTransform = {
94
+ id: "rename-timeout-duration-parameters",
95
+ description: "Renames timeout duration parameters to $timeoutMs when used for setTimeout delays and timeout messages",
96
+ scope: "file",
97
+ parallelizable: true,
98
+ transform(context) {
99
+ let nodesVisited = 0;
100
+ let transformationsApplied = 0;
101
+ for (const fileInfo of getFilesToProcess(context)) {
102
+ const group = new RenameGroup();
103
+ traverse(fileInfo.ast, {
104
+ Function(path) {
105
+ nodesVisited++;
106
+ for (const parameter of path.node.params) {
107
+ const identifier = getParameterIdentifier(parameter);
108
+ if (!identifier)
109
+ continue;
110
+ if (isStableRenamed(identifier.name))
111
+ continue;
112
+ const binding = path.scope.getBinding(identifier.name);
113
+ if (!binding)
114
+ continue;
115
+ if (binding.kind !== "param")
116
+ continue;
117
+ if (!binding.constant)
118
+ continue;
119
+ if (binding.referencePaths.length === 0)
120
+ continue;
121
+ if (!binding.referencePaths.some((referencePath) => isSetTimeoutDelayReference(referencePath, identifier.name)))
122
+ continue;
123
+ if (!binding.referencePaths.every((referencePath) => isAllowedTimeoutReference(referencePath, identifier.name)))
124
+ continue;
125
+ group.add({
126
+ scope: path.scope,
127
+ currentName: identifier.name,
128
+ baseName: BASE_NAME,
129
+ });
130
+ }
131
+ },
132
+ });
133
+ transformationsApplied += group.apply();
134
+ }
135
+ return Promise.resolve({ nodesVisited, transformationsApplied });
136
+ },
137
+ };
@@ -2,59 +2,10 @@ import { createRequire } from "node:module";
2
2
  import { isIdentifierName, isKeyword, isStrictBindReservedWord, } from "@babel/helper-validator-identifier";
3
3
  import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
4
4
  import { getFilesToProcess, } from "../../core/types.js";
5
+ import { hasUrlDestructure } from "../url-usage-heuristics.js";
5
6
  const require = createRequire(import.meta.url);
6
7
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
7
8
  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
9
  const hasUrlConstruction = (referencePath) => {
59
10
  const parent = referencePath.parentPath;
60
11
  if (!parent?.isNewExpression())
@@ -0,0 +1,6 @@
1
+ {
2
+ "diffReductionImpact": 0.000018848364904289205,
3
+ "recommended": true,
4
+ "evaluatedAt": "2026-01-24T23:50:56.857Z",
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 renameUrlVariablesTransform: Transform;
@@ -0,0 +1,94 @@
1
+ import { createRequire } from "node:module";
2
+ import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
3
+ import { getFilesToProcess, } from "../../core/types.js";
4
+ import { hasUrlDestructure, urlPropertyNames, } from "../url-usage-heuristics.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 isUrlConstruction = (node) => {
9
+ if (node?.type !== "NewExpression")
10
+ return false;
11
+ if (node.callee.type !== "Identifier" || node.callee.name !== "URL") {
12
+ return false;
13
+ }
14
+ return true;
15
+ };
16
+ const isUrlPropertyAccess = (referencePath) => {
17
+ const parent = referencePath.parentPath;
18
+ if (!parent)
19
+ return false;
20
+ if (!parent.isMemberExpression())
21
+ return false;
22
+ if (parent.node.object !== referencePath.node)
23
+ return false;
24
+ if (parent.node.computed)
25
+ return false;
26
+ const key = parent.node.property;
27
+ const keyName = key.type === "Identifier"
28
+ ? key.name
29
+ : key.type === "StringLiteral"
30
+ ? key.value
31
+ : undefined;
32
+ if (!keyName)
33
+ return false;
34
+ return urlPropertyNames.has(keyName);
35
+ };
36
+ export const renameUrlVariablesTransform = {
37
+ id: "rename-url-variables",
38
+ description: "Renames variables initialized with new URL(...) when they are accessed via URL properties",
39
+ scope: "file",
40
+ parallelizable: true,
41
+ transform(context) {
42
+ let nodesVisited = 0;
43
+ let transformationsApplied = 0;
44
+ for (const fileInfo of getFilesToProcess(context)) {
45
+ const group = new RenameGroup();
46
+ traverse(fileInfo.ast, {
47
+ VariableDeclarator(path) {
48
+ nodesVisited += 1;
49
+ if (path.node.id.type !== "Identifier")
50
+ return;
51
+ if (!isUrlConstruction(path.node.init))
52
+ return;
53
+ const currentName = path.node.id.name;
54
+ if (isStableRenamed(currentName))
55
+ return;
56
+ const binding = path.scope.getBinding(currentName);
57
+ if (!binding)
58
+ return;
59
+ let sawUrlDestructure = false;
60
+ let propertyAccesses = 0;
61
+ for (const referencePath of binding.referencePaths) {
62
+ const hasDestructure = hasUrlDestructure(referencePath);
63
+ if (hasDestructure) {
64
+ sawUrlDestructure = true;
65
+ }
66
+ if (propertyAccesses < 2 && isUrlPropertyAccess(referencePath)) {
67
+ propertyAccesses += 1;
68
+ }
69
+ if (sawUrlDestructure || propertyAccesses >= 2)
70
+ break;
71
+ }
72
+ if (!sawUrlDestructure && propertyAccesses < 2)
73
+ return;
74
+ const candidateName = "url";
75
+ if (candidateName === currentName)
76
+ return;
77
+ const programScope = path.scope.getProgramParent();
78
+ if (Object.hasOwn(programScope.globals, candidateName))
79
+ return;
80
+ group.add({
81
+ scope: path.scope,
82
+ currentName,
83
+ baseName: candidateName,
84
+ });
85
+ },
86
+ });
87
+ transformationsApplied += group.apply();
88
+ }
89
+ return Promise.resolve({
90
+ nodesVisited,
91
+ transformationsApplied,
92
+ });
93
+ },
94
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ export declare const urlPropertyNames: Set<string>;
3
+ export declare const hasUrlDestructure: (referencePath: NodePath) => boolean;
@@ -0,0 +1,50 @@
1
+ export const urlPropertyNames = new Set([
2
+ "hash",
3
+ "host",
4
+ "hostname",
5
+ "href",
6
+ "origin",
7
+ "password",
8
+ "pathname",
9
+ "port",
10
+ "protocol",
11
+ "search",
12
+ "searchParams",
13
+ "username",
14
+ ]);
15
+ const countUrlProperties = (pattern) => {
16
+ let count = 0;
17
+ for (const property of pattern.properties) {
18
+ if (property.type !== "ObjectProperty")
19
+ continue;
20
+ if (property.computed)
21
+ continue;
22
+ const key = property.key;
23
+ const keyName = key.type === "Identifier"
24
+ ? key.name
25
+ : key.type === "StringLiteral"
26
+ ? key.value
27
+ : undefined;
28
+ if (keyName && urlPropertyNames.has(keyName)) {
29
+ count += 1;
30
+ }
31
+ }
32
+ return count;
33
+ };
34
+ export const hasUrlDestructure = (referencePath) => {
35
+ const parent = referencePath.parentPath;
36
+ if (!parent)
37
+ return false;
38
+ if (parent.isVariableDeclarator() &&
39
+ parent.node.init === referencePath.node &&
40
+ parent.node.id.type === "ObjectPattern") {
41
+ return countUrlProperties(parent.node.id) >= 2;
42
+ }
43
+ if (parent.isAssignmentExpression() &&
44
+ parent.node.operator === "=" &&
45
+ parent.node.right === referencePath.node &&
46
+ parent.node.left.type === "ObjectPattern") {
47
+ return countUrlProperties(parent.node.left) >= 2;
48
+ }
49
+ return false;
50
+ };
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.36.0",
5
+ "version": "1.38.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",