miniread 1.56.0 → 1.57.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/_generated/manifest.js +5 -0
- package/dist/transforms/_generated/registry.js +2 -0
- package/dist/transforms/recommended-transform-order.d.ts +1 -1
- package/dist/transforms/recommended-transform-order.js +1 -0
- package/dist/transforms/use-optional-chaining/build-optional-access.d.ts +2 -0
- package/dist/transforms/use-optional-chaining/build-optional-access.js +51 -0
- package/dist/transforms/use-optional-chaining/is-nullish-check.d.ts +2 -0
- package/dist/transforms/use-optional-chaining/is-nullish-check.js +61 -0
- package/dist/transforms/use-optional-chaining/is-undefined-expression.d.ts +2 -0
- package/dist/transforms/use-optional-chaining/is-undefined-expression.js +12 -0
- package/dist/transforms/use-optional-chaining/manifest.json +12 -0
- package/dist/transforms/use-optional-chaining/unwrap-parentheses.d.ts +2 -0
- package/dist/transforms/use-optional-chaining/unwrap-parentheses.js +8 -0
- package/dist/transforms/use-optional-chaining/use-optional-chaining-transform.d.ts +2 -0
- package/dist/transforms/use-optional-chaining/use-optional-chaining-transform.js +70 -0
- package/package.json +1 -1
|
@@ -307,6 +307,10 @@ const manifestData = {
|
|
|
307
307
|
recommended: true,
|
|
308
308
|
notes: "Improves readability by using object shorthand for stable-renamed identifiers; no diff reduction impact expected.",
|
|
309
309
|
evaluations: { "legacy:evaluation": { "evaluatedAt": "2026-01-24T12:08:16.000Z", "changedLines": 0, "durationSeconds": 0, "stableNames": 0, "diffSizePercent": 100 }, "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-01-25T23:42:52.520Z", "changedLines": 0, "durationSeconds": 44.329058792000005, "stableNames": 1357 } },
|
|
310
|
+
},
|
|
311
|
+
"use-optional-chaining": {
|
|
312
|
+
recommended: true,
|
|
313
|
+
evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-02-01T22:32:15.450Z", "changedLines": 0, "durationSeconds": 146.893061276, "stableNames": 1357 } },
|
|
310
314
|
}
|
|
311
315
|
};
|
|
312
316
|
export const manifestEntries = Object.entries(transformRegistry)
|
|
@@ -372,6 +376,7 @@ export const recommendedTransformIds = [
|
|
|
372
376
|
"simplify-boolean-negations",
|
|
373
377
|
"simplify-string-trim",
|
|
374
378
|
"split-variable-declarations",
|
|
379
|
+
"use-optional-chaining",
|
|
375
380
|
"use-object-property-shorthand",
|
|
376
381
|
"use-object-shorthand",
|
|
377
382
|
"replace-dynamic-require-eval"
|
|
@@ -61,6 +61,7 @@ import { splitVariableDeclarationsTransform } from "../split-variable-declaratio
|
|
|
61
61
|
import { stabilizeTopLevelBindingsTransform } from "../stabilize-top-level-bindings/stabilize-top-level-bindings-transform.js";
|
|
62
62
|
import { useObjectPropertyShorthandTransform } from "../use-object-property-shorthand/use-object-property-shorthand-transform.js";
|
|
63
63
|
import { useObjectShorthandTransform } from "../use-object-shorthand/use-object-shorthand-transform.js";
|
|
64
|
+
import { useOptionalChainingTransform } from "../use-optional-chaining/use-optional-chaining-transform.js";
|
|
64
65
|
export const transformRegistry = {
|
|
65
66
|
[expandBooleanLiteralsTransform.id]: expandBooleanLiteralsTransform,
|
|
66
67
|
[expandSequenceExpressionsV4Transform.id]: expandSequenceExpressionsV4Transform,
|
|
@@ -123,5 +124,6 @@ export const transformRegistry = {
|
|
|
123
124
|
[stabilizeTopLevelBindingsTransform.id]: stabilizeTopLevelBindingsTransform,
|
|
124
125
|
[useObjectPropertyShorthandTransform.id]: useObjectPropertyShorthandTransform,
|
|
125
126
|
[useObjectShorthandTransform.id]: useObjectShorthandTransform,
|
|
127
|
+
[useOptionalChainingTransform.id]: useOptionalChainingTransform,
|
|
126
128
|
};
|
|
127
129
|
export const allTransformIds = Object.keys(transformRegistry);
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* This lets us tune transform interactions intentionally instead of relying on
|
|
5
5
|
* alphabetical ID sorting.
|
|
6
6
|
*/
|
|
7
|
-
export declare const recommendedTransformOrder: readonly ["stabilize-top-level-bindings", "expand-boolean-literals", "expand-sequence-expressions-v5", "expand-special-number-literals", "expand-typeof-undefined-comparisons", "expand-undefined-literals", "remove-redundant-else", "rename-arguments-length-flags", "rename-asap-wrappers", "rename-awaiter-parameters", "rename-buffer-variables", "rename-catch-parameters", "rename-char-code-at", "rename-charcode-variables-v2", "rename-client-aliases", "rename-comparison-flags", "rename-date-now-start-times", "rename-default-options-parameters", "rename-deferred-resolve-parameters", "rename-destructured-aliases", "rename-error-first-callback-parameters", "rename-error-variables", "rename-event-parameters", "rename-interval-ids", "rename-loop-index-variables-v3", "rename-loop-length-variables", "rename-object-keys-variables", "rename-parameters-to-match-properties-v2", "rename-promise-executor-parameters-v2", "rename-range-parameters", "rename-read-file-lines", "rename-regex-builders", "rename-rest-parameters", "rename-search-parameters-variables", "rename-string-split-variables", "rename-this-aliases", "rename-timeout-ids", "rename-typeof-variables", "rename-typescript-helper-aliases", "rename-uint8array-concat-variables", "rename-url-parameters", "rename-url-variables", "rename-use-reference-guards-v2", "rename-use-reference-sets", "simplify-boolean-negations", "simplify-string-trim", "split-variable-declarations", "use-object-property-shorthand", "use-object-shorthand", "replace-dynamic-require-eval"];
|
|
7
|
+
export declare const recommendedTransformOrder: readonly ["stabilize-top-level-bindings", "expand-boolean-literals", "expand-sequence-expressions-v5", "expand-special-number-literals", "expand-typeof-undefined-comparisons", "expand-undefined-literals", "remove-redundant-else", "rename-arguments-length-flags", "rename-asap-wrappers", "rename-awaiter-parameters", "rename-buffer-variables", "rename-catch-parameters", "rename-char-code-at", "rename-charcode-variables-v2", "rename-client-aliases", "rename-comparison-flags", "rename-date-now-start-times", "rename-default-options-parameters", "rename-deferred-resolve-parameters", "rename-destructured-aliases", "rename-error-first-callback-parameters", "rename-error-variables", "rename-event-parameters", "rename-interval-ids", "rename-loop-index-variables-v3", "rename-loop-length-variables", "rename-object-keys-variables", "rename-parameters-to-match-properties-v2", "rename-promise-executor-parameters-v2", "rename-range-parameters", "rename-read-file-lines", "rename-regex-builders", "rename-rest-parameters", "rename-search-parameters-variables", "rename-string-split-variables", "rename-this-aliases", "rename-timeout-ids", "rename-typeof-variables", "rename-typescript-helper-aliases", "rename-uint8array-concat-variables", "rename-url-parameters", "rename-url-variables", "rename-use-reference-guards-v2", "rename-use-reference-sets", "simplify-boolean-negations", "simplify-string-trim", "split-variable-declarations", "use-optional-chaining", "use-object-property-shorthand", "use-object-shorthand", "replace-dynamic-require-eval"];
|
|
@@ -54,6 +54,7 @@ export const recommendedTransformOrder = [
|
|
|
54
54
|
"simplify-boolean-negations",
|
|
55
55
|
"simplify-string-trim",
|
|
56
56
|
"split-variable-declarations",
|
|
57
|
+
"use-optional-chaining",
|
|
57
58
|
"use-object-property-shorthand",
|
|
58
59
|
"use-object-shorthand",
|
|
59
60
|
"replace-dynamic-require-eval",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { cloneNode, isCallExpression, isIdentifier, isMemberExpression, isPrivateName, optionalCallExpression, optionalMemberExpression, } from "@babel/types";
|
|
2
|
+
export const buildOptionalAccess = (target, alternate) => {
|
|
3
|
+
const buildOptionalMemberChain = (member) => {
|
|
4
|
+
if (!isMemberExpression(member))
|
|
5
|
+
return;
|
|
6
|
+
const property = member.property;
|
|
7
|
+
if (isPrivateName(property))
|
|
8
|
+
return;
|
|
9
|
+
const object = member.object;
|
|
10
|
+
if (isIdentifier(object)) {
|
|
11
|
+
if (object.name !== target.name)
|
|
12
|
+
return;
|
|
13
|
+
return optionalMemberExpression(cloneNode(object), cloneNode(property), member.computed, true);
|
|
14
|
+
}
|
|
15
|
+
const optionalObject = buildOptionalMemberChain(object);
|
|
16
|
+
if (!optionalObject)
|
|
17
|
+
return;
|
|
18
|
+
return optionalMemberExpression(optionalObject, cloneNode(property), member.computed, false);
|
|
19
|
+
};
|
|
20
|
+
if (isMemberExpression(alternate)) {
|
|
21
|
+
return buildOptionalMemberChain(alternate);
|
|
22
|
+
}
|
|
23
|
+
if (!isCallExpression(alternate))
|
|
24
|
+
return;
|
|
25
|
+
if (isMemberExpression(alternate.callee)) {
|
|
26
|
+
const optionalCallee = buildOptionalMemberChain(alternate.callee);
|
|
27
|
+
if (!optionalCallee)
|
|
28
|
+
return;
|
|
29
|
+
const optionalCall = optionalCallExpression(optionalCallee, alternate.arguments.map((argument) => cloneNode(argument)), false);
|
|
30
|
+
if (alternate.typeParameters) {
|
|
31
|
+
optionalCall.typeParameters = cloneNode(alternate.typeParameters);
|
|
32
|
+
}
|
|
33
|
+
if ("typeArguments" in alternate && alternate.typeArguments) {
|
|
34
|
+
optionalCall.typeArguments = cloneNode(alternate.typeArguments);
|
|
35
|
+
}
|
|
36
|
+
return optionalCall;
|
|
37
|
+
}
|
|
38
|
+
if (isIdentifier(alternate.callee)) {
|
|
39
|
+
if (alternate.callee.name !== target.name)
|
|
40
|
+
return;
|
|
41
|
+
const optionalCall = optionalCallExpression(cloneNode(alternate.callee), alternate.arguments.map((argument) => cloneNode(argument)), true);
|
|
42
|
+
if (alternate.typeParameters) {
|
|
43
|
+
optionalCall.typeParameters = cloneNode(alternate.typeParameters);
|
|
44
|
+
}
|
|
45
|
+
if ("typeArguments" in alternate && alternate.typeArguments) {
|
|
46
|
+
optionalCall.typeArguments = cloneNode(alternate.typeArguments);
|
|
47
|
+
}
|
|
48
|
+
return optionalCall;
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { isBinaryExpression, isIdentifier, isLogicalExpression, isNullLiteral, isPrivateName, } from "@babel/types";
|
|
2
|
+
import { isUndefinedExpression } from "./is-undefined-expression.js";
|
|
3
|
+
import { unwrapParentheses } from "./unwrap-parentheses.js";
|
|
4
|
+
const noCoverage = {
|
|
5
|
+
coversNull: false,
|
|
6
|
+
coversUndefined: false,
|
|
7
|
+
};
|
|
8
|
+
const mergeCoverage = (left, right) => {
|
|
9
|
+
return {
|
|
10
|
+
coversNull: left.coversNull || right.coversNull,
|
|
11
|
+
coversUndefined: left.coversUndefined || right.coversUndefined,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const getNullishCoverage = (test, identifierName) => {
|
|
15
|
+
const unwrapped = unwrapParentheses(test);
|
|
16
|
+
if (isBinaryExpression(unwrapped)) {
|
|
17
|
+
if (unwrapped.operator !== "===")
|
|
18
|
+
return noCoverage;
|
|
19
|
+
const getValueCoverage = (value) => {
|
|
20
|
+
if (isPrivateName(value))
|
|
21
|
+
return noCoverage;
|
|
22
|
+
const unwrappedValue = unwrapParentheses(value);
|
|
23
|
+
if (isNullLiteral(unwrappedValue)) {
|
|
24
|
+
return { coversNull: true, coversUndefined: false };
|
|
25
|
+
}
|
|
26
|
+
if (isUndefinedExpression(unwrappedValue)) {
|
|
27
|
+
return { coversNull: false, coversUndefined: true };
|
|
28
|
+
}
|
|
29
|
+
return noCoverage;
|
|
30
|
+
};
|
|
31
|
+
const left = unwrapped.left;
|
|
32
|
+
const right = unwrapped.right;
|
|
33
|
+
if (isPrivateName(left))
|
|
34
|
+
return noCoverage;
|
|
35
|
+
if (isIdentifier(left) && left.name === identifierName) {
|
|
36
|
+
return getValueCoverage(right);
|
|
37
|
+
}
|
|
38
|
+
if (!isPrivateName(right) &&
|
|
39
|
+
isIdentifier(right) &&
|
|
40
|
+
right.name === identifierName) {
|
|
41
|
+
return getValueCoverage(left);
|
|
42
|
+
}
|
|
43
|
+
return noCoverage;
|
|
44
|
+
}
|
|
45
|
+
if (isLogicalExpression(unwrapped) && unwrapped.operator === "||") {
|
|
46
|
+
const leftCoverage = getNullishCoverage(unwrapped.left, identifierName);
|
|
47
|
+
if (!leftCoverage.coversNull && !leftCoverage.coversUndefined) {
|
|
48
|
+
return noCoverage;
|
|
49
|
+
}
|
|
50
|
+
const rightCoverage = getNullishCoverage(unwrapped.right, identifierName);
|
|
51
|
+
if (!rightCoverage.coversNull && !rightCoverage.coversUndefined) {
|
|
52
|
+
return noCoverage;
|
|
53
|
+
}
|
|
54
|
+
return mergeCoverage(leftCoverage, rightCoverage);
|
|
55
|
+
}
|
|
56
|
+
return noCoverage;
|
|
57
|
+
};
|
|
58
|
+
export const isNullishCheck = (test, identifierName) => {
|
|
59
|
+
const coverage = getNullishCoverage(test, identifierName);
|
|
60
|
+
return coverage.coversNull && coverage.coversUndefined;
|
|
61
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { isIdentifier, isNumericLiteral, isUnaryExpression, } from "@babel/types";
|
|
2
|
+
import { unwrapParentheses } from "./unwrap-parentheses.js";
|
|
3
|
+
export const isUndefinedExpression = (expression) => {
|
|
4
|
+
const unwrapped = unwrapParentheses(expression);
|
|
5
|
+
if (isIdentifier(unwrapped) && unwrapped.name === "undefined")
|
|
6
|
+
return true;
|
|
7
|
+
if (isUnaryExpression(unwrapped) && unwrapped.operator === "void") {
|
|
8
|
+
const argument = unwrapParentheses(unwrapped.argument);
|
|
9
|
+
return isNumericLiteral(argument);
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { isCallExpression, isIdentifier, isMemberExpression, } from "@babel/types";
|
|
3
|
+
import { getFilesToProcess, } from "../../core/types.js";
|
|
4
|
+
import { buildOptionalAccess } from "./build-optional-access.js";
|
|
5
|
+
import { isNullishCheck } from "./is-nullish-check.js";
|
|
6
|
+
import { isUndefinedExpression } from "./is-undefined-expression.js";
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
9
|
+
const traverse = require("@babel/traverse").default;
|
|
10
|
+
const shouldSkipUndefinedBinding = (path) => Boolean(path.scope.getBinding("undefined"));
|
|
11
|
+
export const useOptionalChainingTransform = {
|
|
12
|
+
id: "use-optional-chaining",
|
|
13
|
+
description: "Replace explicit null/undefined ternaries with optional chaining",
|
|
14
|
+
scope: "file",
|
|
15
|
+
parallelizable: true,
|
|
16
|
+
transform(context) {
|
|
17
|
+
let nodesVisited = 0;
|
|
18
|
+
let transformationsApplied = 0;
|
|
19
|
+
for (const fileInfo of getFilesToProcess(context)) {
|
|
20
|
+
traverse(fileInfo.ast, {
|
|
21
|
+
ConditionalExpression(path) {
|
|
22
|
+
nodesVisited++;
|
|
23
|
+
if (shouldSkipUndefinedBinding(path))
|
|
24
|
+
return;
|
|
25
|
+
const alternate = path.node.alternate;
|
|
26
|
+
const target = (() => {
|
|
27
|
+
if (isMemberExpression(alternate)) {
|
|
28
|
+
let object = alternate.object;
|
|
29
|
+
while (isMemberExpression(object)) {
|
|
30
|
+
object = object.object;
|
|
31
|
+
}
|
|
32
|
+
if (!isIdentifier(object))
|
|
33
|
+
return;
|
|
34
|
+
return object;
|
|
35
|
+
}
|
|
36
|
+
if (!isCallExpression(alternate))
|
|
37
|
+
return;
|
|
38
|
+
const callee = alternate.callee;
|
|
39
|
+
if (isIdentifier(callee))
|
|
40
|
+
return callee;
|
|
41
|
+
if (isMemberExpression(callee)) {
|
|
42
|
+
let object = callee.object;
|
|
43
|
+
while (isMemberExpression(object)) {
|
|
44
|
+
object = object.object;
|
|
45
|
+
}
|
|
46
|
+
if (!isIdentifier(object))
|
|
47
|
+
return;
|
|
48
|
+
return object;
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
})();
|
|
52
|
+
if (!target)
|
|
53
|
+
return;
|
|
54
|
+
if (!path.scope.getBinding(target.name))
|
|
55
|
+
return;
|
|
56
|
+
if (!isNullishCheck(path.node.test, target.name))
|
|
57
|
+
return;
|
|
58
|
+
if (!isUndefinedExpression(path.node.consequent))
|
|
59
|
+
return;
|
|
60
|
+
const replacement = buildOptionalAccess(target, path.node.alternate);
|
|
61
|
+
if (!replacement)
|
|
62
|
+
return;
|
|
63
|
+
path.replaceWith(replacement);
|
|
64
|
+
transformationsApplied++;
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return Promise.resolve({ nodesVisited, transformationsApplied });
|
|
69
|
+
},
|
|
70
|
+
};
|
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.57.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",
|