miniread 1.48.0 → 1.49.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.
Files changed (20) hide show
  1. package/dist/transforms/_generated/manifest.js +5 -0
  2. package/dist/transforms/_generated/registry.js +2 -0
  3. package/dist/transforms/rename-string-split-variables/get-split-helper-info.d.ts +8 -0
  4. package/dist/transforms/rename-string-split-variables/get-split-helper-info.js +40 -0
  5. package/dist/transforms/rename-string-split-variables/manifest.json +13 -0
  6. package/dist/transforms/rename-string-split-variables/rename-string-split-variables-transform.d.ts +2 -0
  7. package/dist/transforms/rename-string-split-variables/rename-string-split-variables-transform.js +69 -0
  8. package/dist/transforms/rename-string-split-variables/split-helper-declarations.d.ts +11 -0
  9. package/dist/transforms/rename-string-split-variables/split-helper-declarations.js +80 -0
  10. package/dist/transforms/rename-string-split-variables/split-helper-guards.d.ts +8 -0
  11. package/dist/transforms/rename-string-split-variables/split-helper-guards.js +73 -0
  12. package/dist/transforms/rename-string-split-variables/split-helper-loop.d.ts +3 -0
  13. package/dist/transforms/rename-string-split-variables/split-helper-loop.js +116 -0
  14. package/dist/transforms/rename-string-split-variables/split-helper-tail-expressions.d.ts +3 -0
  15. package/dist/transforms/rename-string-split-variables/split-helper-tail-expressions.js +86 -0
  16. package/dist/transforms/rename-string-split-variables/split-helper-tail-tests.d.ts +4 -0
  17. package/dist/transforms/rename-string-split-variables/split-helper-tail-tests.js +39 -0
  18. package/dist/transforms/rename-string-split-variables/split-helper-tail.d.ts +3 -0
  19. package/dist/transforms/rename-string-split-variables/split-helper-tail.js +49 -0
  20. package/package.json +1 -1
@@ -185,6 +185,11 @@ const manifestData = {
185
185
  notes: "Negative diff reduction in evaluation; keep optional.",
186
186
  evaluations: { "legacy:evaluation": { "diffSizePercent": 100.00188483649043, "evaluatedAt": "2026-01-25T10:04:22.417Z", "changedLines": 0, "durationSeconds": 0, "stableNames": 0 } },
187
187
  },
188
+ "rename-string-split-variables": {
189
+ recommended: true,
190
+ notes: "Measured with baseline none: 0.00%. Added to recommended for readability.",
191
+ evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-01-26T07:03:04.068Z", "changedLines": 0, "durationSeconds": 78.43914533300001, "stableNames": 1357 } },
192
+ },
188
193
  "rename-this-aliases": {
189
194
  recommended: true,
190
195
  notes: "Measured with baseline none: 0.00%. Improves readability by stabilizing `this` aliases used for closures.",
@@ -36,6 +36,7 @@ import { renameRegexBuildersTransform } from "../rename-regex-builders/rename-re
36
36
  import { renameReplaceChildParametersTransform } from "../rename-replace-child-parameters/rename-replace-child-parameters-transform.js";
37
37
  import { renameRestParametersTransform } from "../rename-rest-parameters/rename-rest-parameters-transform.js";
38
38
  import { renameSafePropertyAccessorParametersTransform } from "../rename-safe-property-accessor-parameters/rename-safe-property-accessor-parameters-transform.js";
39
+ import { renameStringSplitVariablesTransform } from "../rename-string-split-variables/rename-string-split-variables-transform.js";
39
40
  import { renameThisAliasesTransform } from "../rename-this-aliases/rename-this-aliases-transform.js";
40
41
  import { renameTimeoutDurationParametersTransform } from "../rename-timeout-duration-parameters/rename-timeout-duration-parameters-transform.js";
41
42
  import { renameTimeoutIdsTransform } from "../rename-timeout-ids/rename-timeout-ids-transform.js";
@@ -89,6 +90,7 @@ export const transformRegistry = {
89
90
  [renameReplaceChildParametersTransform.id]: renameReplaceChildParametersTransform,
90
91
  [renameRestParametersTransform.id]: renameRestParametersTransform,
91
92
  [renameSafePropertyAccessorParametersTransform.id]: renameSafePropertyAccessorParametersTransform,
93
+ [renameStringSplitVariablesTransform.id]: renameStringSplitVariablesTransform,
92
94
  [renameThisAliasesTransform.id]: renameThisAliasesTransform,
93
95
  [renameTimeoutDurationParametersTransform.id]: renameTimeoutDurationParametersTransform,
94
96
  [renameTimeoutIdsTransform.id]: renameTimeoutIdsTransform,
@@ -0,0 +1,8 @@
1
+ import type { ArrowFunctionExpression, FunctionDeclaration, FunctionExpression } from "@babel/types";
2
+ import type { SplitHelperDeclarations } from "./split-helper-declarations.js";
3
+ type SplitHelperInfo = SplitHelperDeclarations & {
4
+ inputName: string;
5
+ delimiterName: string;
6
+ };
7
+ export declare const getSplitHelperInfo: (node: FunctionDeclaration | FunctionExpression | ArrowFunctionExpression) => SplitHelperInfo | undefined;
8
+ export {};
@@ -0,0 +1,40 @@
1
+ import { isIdentifierNode } from "./split-helper-guards.js";
2
+ import { getSplitHelperDeclarationsFromStatements } from "./split-helper-declarations.js";
3
+ import { isSplitHelperLoop } from "./split-helper-loop.js";
4
+ import { isSplitHelperTail } from "./split-helper-tail.js";
5
+ export const getSplitHelperInfo = (node) => {
6
+ if (node.params.length !== 2)
7
+ return undefined;
8
+ const inputParameter = node.params[0];
9
+ const delimiterParameter = node.params[1];
10
+ if (!isIdentifierNode(inputParameter) ||
11
+ !isIdentifierNode(delimiterParameter))
12
+ return undefined;
13
+ if (node.body.type !== "BlockStatement")
14
+ return undefined;
15
+ const statements = node.body.body;
16
+ const declarationsMatch = getSplitHelperDeclarationsFromStatements(statements, inputParameter.name, delimiterParameter.name);
17
+ if (!declarationsMatch)
18
+ return undefined;
19
+ const { nextStatementIndex, ...declarations } = declarationsMatch;
20
+ const loopStatement = statements[nextStatementIndex];
21
+ if (!isSplitHelperLoop(loopStatement, declarations, inputParameter.name, delimiterParameter.name))
22
+ return undefined;
23
+ const tailStatements = statements.slice(nextStatementIndex + 1);
24
+ if (!isSplitHelperTail(tailStatements, declarations, inputParameter.name))
25
+ return undefined;
26
+ const uniqueNames = new Set([
27
+ inputParameter.name,
28
+ delimiterParameter.name,
29
+ declarations.partsName,
30
+ declarations.startIndexName,
31
+ declarations.delimiterIndexName,
32
+ ]);
33
+ if (uniqueNames.size !== 5)
34
+ return undefined;
35
+ return {
36
+ inputName: inputParameter.name,
37
+ delimiterName: delimiterParameter.name,
38
+ ...declarations,
39
+ };
40
+ };
@@ -0,0 +1,13 @@
1
+ {
2
+ "recommended": true,
3
+ "evaluations": {
4
+ "claude-code-2.1.10:claude-code-2.1.11": {
5
+ "diffSizePercent": 100,
6
+ "evaluatedAt": "2026-01-26T07:03:04.068Z",
7
+ "changedLines": 0,
8
+ "durationSeconds": 78.43914533300001,
9
+ "stableNames": 1357
10
+ }
11
+ },
12
+ "notes": "Measured with baseline none: 0.00%. Added to recommended for readability."
13
+ }
@@ -0,0 +1,2 @@
1
+ import type { Transform } from "../../core/types.js";
2
+ export declare const renameStringSplitVariablesTransform: Transform;
@@ -0,0 +1,69 @@
1
+ import { createRequire } from "node:module";
2
+ import { RenameGroup } from "../../core/stable-naming.js";
3
+ import { getFilesToProcess } from "../../core/types.js";
4
+ import { getSplitHelperInfo } from "./get-split-helper-info.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 collectSplitHelperRenames = (path, group) => {
9
+ // Intentionally no early-return based on stable-renamed params:
10
+ // parameters can be stable while helper locals are still minified.
11
+ const info = getSplitHelperInfo(path.node);
12
+ if (!info)
13
+ return;
14
+ const scope = path.scope;
15
+ group.add({
16
+ scope,
17
+ currentName: info.inputName,
18
+ baseName: "input",
19
+ });
20
+ group.add({
21
+ scope,
22
+ currentName: info.delimiterName,
23
+ baseName: "delimiter",
24
+ });
25
+ group.add({
26
+ scope,
27
+ currentName: info.partsName,
28
+ baseName: "parts",
29
+ });
30
+ group.add({
31
+ scope,
32
+ currentName: info.startIndexName,
33
+ baseName: "startIndex",
34
+ });
35
+ group.add({
36
+ scope,
37
+ currentName: info.delimiterIndexName,
38
+ baseName: "delimiterIndex",
39
+ });
40
+ };
41
+ export const renameStringSplitVariablesTransform = {
42
+ id: "rename-string-split-variables",
43
+ description: "Renames local variables in manual string split helpers (indexOf + slice loop) to stable $input-style names (prefers $input/$delimiter/$parts/$startIndex/$delimiterIndex)",
44
+ scope: "file",
45
+ parallelizable: true,
46
+ transform(context) {
47
+ let nodesVisited = 0;
48
+ let transformationsApplied = 0;
49
+ for (const fileInfo of getFilesToProcess(context)) {
50
+ const group = new RenameGroup();
51
+ traverse(fileInfo.ast, {
52
+ FunctionDeclaration(path) {
53
+ nodesVisited++;
54
+ collectSplitHelperRenames(path, group);
55
+ },
56
+ FunctionExpression(path) {
57
+ nodesVisited++;
58
+ collectSplitHelperRenames(path, group);
59
+ },
60
+ ArrowFunctionExpression(path) {
61
+ nodesVisited++;
62
+ collectSplitHelperRenames(path, group);
63
+ },
64
+ });
65
+ transformationsApplied += group.apply();
66
+ }
67
+ return Promise.resolve({ nodesVisited, transformationsApplied });
68
+ },
69
+ };
@@ -0,0 +1,11 @@
1
+ import type { Statement } from "@babel/types";
2
+ export type SplitHelperDeclarations = {
3
+ partsName: string;
4
+ startIndexName: string;
5
+ delimiterIndexName: string;
6
+ };
7
+ type SplitHelperDeclarationsMatch = SplitHelperDeclarations & {
8
+ nextStatementIndex: number;
9
+ };
10
+ export declare const getSplitHelperDeclarationsFromStatements: (statements: Statement[], inputName: string, delimiterName: string) => SplitHelperDeclarationsMatch | undefined;
11
+ export {};
@@ -0,0 +1,80 @@
1
+ import { isIdentifierNode, isMemberCall, isNumericLiteralNode, } from "./split-helper-guards.js";
2
+ const isDirectivePrologueStatement = (statement) => {
3
+ // In JavaScript, any leading sequence of `"..."`; expression statements is the
4
+ // "directive prologue" (most commonly `"use strict";`). Babel sometimes also
5
+ // marks these with `statement.directive`, but we intentionally only rely on
6
+ // the lexical shape so we still match after AST rewrites.
7
+ if (statement.type !== "ExpressionStatement")
8
+ return false;
9
+ return statement.expression.type === "StringLiteral";
10
+ };
11
+ const getVariableDeclarators = (statement) => {
12
+ if (statement.type !== "VariableDeclaration")
13
+ return undefined;
14
+ if (statement.declarations.length === 0)
15
+ return undefined;
16
+ return statement.declarations;
17
+ };
18
+ export const getSplitHelperDeclarationsFromStatements = (statements, inputName, delimiterName) => {
19
+ const declarators = [];
20
+ let statementIndex = 0;
21
+ while (statementIndex < statements.length) {
22
+ const statement = statements[statementIndex];
23
+ if (!statement)
24
+ return undefined;
25
+ if (!isDirectivePrologueStatement(statement))
26
+ break;
27
+ statementIndex++;
28
+ }
29
+ if (statementIndex >= statements.length)
30
+ return undefined;
31
+ let declarationStatementsConsumed = 0;
32
+ while (declarators.length < 3) {
33
+ if (statementIndex >= statements.length)
34
+ return undefined;
35
+ const statement = statements[statementIndex];
36
+ if (!statement)
37
+ return undefined;
38
+ const statementDeclarators = getVariableDeclarators(statement);
39
+ if (!statementDeclarators)
40
+ return undefined;
41
+ declarationStatementsConsumed++;
42
+ if (declarationStatementsConsumed > 3)
43
+ return undefined;
44
+ const totalDeclarators = declarators.length + statementDeclarators.length;
45
+ if (totalDeclarators > 3)
46
+ return undefined;
47
+ declarators.push(...statementDeclarators);
48
+ statementIndex++;
49
+ }
50
+ const identifierDeclarators = declarators.filter((declarator) => declarator.id.type === "Identifier");
51
+ if (identifierDeclarators.length !== declarators.length)
52
+ return undefined;
53
+ const partsDeclarator = identifierDeclarators.find((declarator) => {
54
+ if (declarator.init?.type !== "ArrayExpression")
55
+ return false;
56
+ return declarator.init.elements.length === 0;
57
+ });
58
+ const startDeclarator = identifierDeclarators.find((declarator) => isNumericLiteralNode(declarator.init ?? undefined, 0));
59
+ const indexDeclarator = identifierDeclarators.find((declarator) => {
60
+ if (declarator.init?.type !== "CallExpression")
61
+ return false;
62
+ if (!isMemberCall(declarator.init, inputName, "indexOf"))
63
+ return false;
64
+ if (!isIdentifierNode(declarator.init.arguments[0], delimiterName))
65
+ return false;
66
+ if (declarator.init.arguments.length === 1)
67
+ return true;
68
+ if (declarator.init.arguments.length !== 2)
69
+ return false;
70
+ return isNumericLiteralNode(declarator.init.arguments[1], 0);
71
+ });
72
+ if (!partsDeclarator || !startDeclarator || !indexDeclarator)
73
+ return undefined;
74
+ return {
75
+ partsName: partsDeclarator.id.name,
76
+ startIndexName: startDeclarator.id.name,
77
+ delimiterIndexName: indexDeclarator.id.name,
78
+ nextStatementIndex: statementIndex,
79
+ };
80
+ };
@@ -0,0 +1,8 @@
1
+ import type { BinaryExpression, CallExpression, Identifier, Node, NumericLiteral, Statement } from "@babel/types";
2
+ export declare const isIdentifierNode: (node: Node | undefined, name?: string) => node is Identifier;
3
+ export declare const isNumericLiteralNode: (node: Node | undefined, value?: number) => node is NumericLiteral;
4
+ export declare const getBlockStatements: (statement: Statement) => Statement[] | undefined;
5
+ export declare const isMemberCall: (expression: CallExpression, objectName: string, propertyName: string) => boolean;
6
+ export declare const isPushSliceStatement: (statement: Statement, partsName: string, inputName: string, startName: string, endName?: string) => boolean;
7
+ export declare const isAssignmentStatement: (statement: Statement, targetName: string, expectedExpression: (expression: Node) => boolean) => boolean;
8
+ export declare const isBinaryExpression: (expression: Node, operator: BinaryExpression["operator"], leftName: string, rightValue: number) => boolean;
@@ -0,0 +1,73 @@
1
+ export const isIdentifierNode = (node, name) => {
2
+ if (node?.type !== "Identifier")
3
+ return false;
4
+ return name ? node.name === name : true;
5
+ };
6
+ export const isNumericLiteralNode = (node, value) => {
7
+ if (node?.type !== "NumericLiteral")
8
+ return false;
9
+ return value === undefined ? true : node.value === value;
10
+ };
11
+ export const getBlockStatements = (statement) => {
12
+ if (statement.type !== "BlockStatement")
13
+ return undefined;
14
+ return statement.body;
15
+ };
16
+ export const isMemberCall = (expression, objectName, propertyName) => {
17
+ const callee = expression.callee;
18
+ if (callee.type !== "MemberExpression")
19
+ return false;
20
+ if (callee.computed)
21
+ return false;
22
+ if (!isIdentifierNode(callee.object, objectName))
23
+ return false;
24
+ return isIdentifierNode(callee.property, propertyName);
25
+ };
26
+ const isSliceCallWithArguments = (expression, inputName, startName, endName) => {
27
+ if (!isMemberCall(expression, inputName, "slice"))
28
+ return false;
29
+ if (!isIdentifierNode(expression.arguments[0], startName))
30
+ return false;
31
+ if (endName === undefined) {
32
+ return expression.arguments.length === 1;
33
+ }
34
+ if (!isIdentifierNode(expression.arguments[1], endName))
35
+ return false;
36
+ return expression.arguments.length === 2;
37
+ };
38
+ export const isPushSliceStatement = (statement, partsName, inputName, startName, endName) => {
39
+ if (statement.type !== "ExpressionStatement")
40
+ return false;
41
+ const expression = statement.expression;
42
+ if (expression.type !== "CallExpression")
43
+ return false;
44
+ if (!isMemberCall(expression, partsName, "push"))
45
+ return false;
46
+ if (expression.arguments.length !== 1)
47
+ return false;
48
+ const argument0 = expression.arguments[0];
49
+ if (argument0?.type !== "CallExpression")
50
+ return false;
51
+ return isSliceCallWithArguments(argument0, inputName, startName, endName);
52
+ };
53
+ export const isAssignmentStatement = (statement, targetName, expectedExpression) => {
54
+ if (statement.type !== "ExpressionStatement")
55
+ return false;
56
+ const expression = statement.expression;
57
+ if (expression.type !== "AssignmentExpression")
58
+ return false;
59
+ if (expression.operator !== "=")
60
+ return false;
61
+ if (!isIdentifierNode(expression.left, targetName))
62
+ return false;
63
+ return expectedExpression(expression.right);
64
+ };
65
+ export const isBinaryExpression = (expression, operator, leftName, rightValue) => {
66
+ if (expression.type !== "BinaryExpression")
67
+ return false;
68
+ if (expression.operator !== operator)
69
+ return false;
70
+ if (!isIdentifierNode(expression.left, leftName))
71
+ return false;
72
+ return isNumericLiteralNode(expression.right, rightValue);
73
+ };
@@ -0,0 +1,3 @@
1
+ import type { Statement } from "@babel/types";
2
+ import type { SplitHelperDeclarations } from "./split-helper-declarations.js";
3
+ export declare const isSplitHelperLoop: (statement: Statement | undefined, declarations: SplitHelperDeclarations, inputName: string, delimiterName: string) => boolean;
@@ -0,0 +1,116 @@
1
+ import { isAssignmentStatement, isBinaryExpression, isIdentifierNode, isMemberCall, isNumericLiteralNode, isPushSliceStatement, } from "./split-helper-guards.js";
2
+ const isNegativeOneLiteral = (expression) => {
3
+ if (expression.type === "NumericLiteral")
4
+ return expression.value === -1;
5
+ if (expression.type !== "UnaryExpression")
6
+ return false;
7
+ if (expression.operator !== "-")
8
+ return false;
9
+ if (expression.argument.type !== "NumericLiteral")
10
+ return false;
11
+ return expression.argument.value === 1;
12
+ };
13
+ const isNotFoundBinaryTest = (expression, name) => {
14
+ if (expression.type !== "BinaryExpression")
15
+ return false;
16
+ if (expression.operator !== "!==" && expression.operator !== "!=")
17
+ return false;
18
+ if (!isIdentifierNode(expression.left, name))
19
+ return false;
20
+ return isNegativeOneLiteral(expression.right);
21
+ };
22
+ const isFoundBinaryTest = (expression, name) => {
23
+ if (expression.type !== "BinaryExpression")
24
+ return false;
25
+ if (expression.operator !== ">")
26
+ return false;
27
+ if (!isIdentifierNode(expression.left, name))
28
+ return false;
29
+ return isNegativeOneLiteral(expression.right);
30
+ };
31
+ const isBitwiseNotTest = (expression, name) => {
32
+ if (expression.type !== "UnaryExpression")
33
+ return false;
34
+ if (expression.operator !== "~")
35
+ return false;
36
+ return isIdentifierNode(expression.argument, name);
37
+ };
38
+ export const isSplitHelperLoop = (statement, declarations, inputName, delimiterName) => {
39
+ if (statement?.type !== "WhileStatement")
40
+ return false;
41
+ const isNonNegativeTest = (expression) => {
42
+ if (isBinaryExpression(expression, ">=", declarations.delimiterIndexName, 0)) {
43
+ return true;
44
+ }
45
+ if (expression.type !== "BinaryExpression")
46
+ return false;
47
+ if (expression.operator !== "<=")
48
+ return false;
49
+ if (!isNumericLiteralNode(expression.left, 0))
50
+ return false;
51
+ return isIdentifierNode(expression.right, declarations.delimiterIndexName);
52
+ };
53
+ if (!isNonNegativeTest(statement.test) &&
54
+ !isNotFoundBinaryTest(statement.test, declarations.delimiterIndexName) &&
55
+ !isFoundBinaryTest(statement.test, declarations.delimiterIndexName) &&
56
+ !isBitwiseNotTest(statement.test, declarations.delimiterIndexName)) {
57
+ return false;
58
+ }
59
+ if (statement.body.type !== "BlockStatement")
60
+ return false;
61
+ if (statement.body.body.length !== 3)
62
+ return false;
63
+ const [pushStatement, advanceStatement, updateStatement] = statement.body.body;
64
+ if (!pushStatement || !advanceStatement || !updateStatement)
65
+ return false;
66
+ if (!isPushSliceStatement(pushStatement, declarations.partsName, inputName, declarations.startIndexName, declarations.delimiterIndexName))
67
+ return false;
68
+ const isAdvanceExpression = (expression) => {
69
+ if (isBinaryExpression(expression, "+", declarations.delimiterIndexName, 1)) {
70
+ return true;
71
+ }
72
+ if (expression.type === "BinaryExpression" && expression.operator === "+") {
73
+ if (isNumericLiteralNode(expression.left, 1) &&
74
+ isIdentifierNode(expression.right, declarations.delimiterIndexName)) {
75
+ return true;
76
+ }
77
+ const left = expression.left;
78
+ const right = expression.right;
79
+ if (left.type === "MemberExpression" &&
80
+ !left.computed &&
81
+ isIdentifierNode(left.object, delimiterName) &&
82
+ isIdentifierNode(left.property, "length") &&
83
+ isIdentifierNode(right, declarations.delimiterIndexName)) {
84
+ return true;
85
+ }
86
+ }
87
+ if (expression.type !== "BinaryExpression")
88
+ return false;
89
+ if (expression.operator !== "+")
90
+ return false;
91
+ if (!isIdentifierNode(expression.left, declarations.delimiterIndexName))
92
+ return false;
93
+ const right = expression.right;
94
+ if (right.type !== "MemberExpression")
95
+ return false;
96
+ if (right.computed)
97
+ return false;
98
+ if (!isIdentifierNode(right.object, delimiterName))
99
+ return false;
100
+ return isIdentifierNode(right.property, "length");
101
+ };
102
+ if (!isAssignmentStatement(advanceStatement, declarations.startIndexName, isAdvanceExpression))
103
+ return false;
104
+ const isUpdateExpression = (expression) => {
105
+ if (expression.type !== "CallExpression")
106
+ return false;
107
+ if (!isMemberCall(expression, inputName, "indexOf"))
108
+ return false;
109
+ if (expression.arguments.length !== 2)
110
+ return false;
111
+ if (!isIdentifierNode(expression.arguments[0], delimiterName))
112
+ return false;
113
+ return isIdentifierNode(expression.arguments[1], declarations.startIndexName);
114
+ };
115
+ return isAssignmentStatement(updateStatement, declarations.delimiterIndexName, isUpdateExpression);
116
+ };
@@ -0,0 +1,3 @@
1
+ import type { Node } from "@babel/types";
2
+ import type { SplitHelperDeclarations } from "./split-helper-declarations.js";
3
+ export declare const isReturnWithOptionalTailPush: (node: Node, declarations: SplitHelperDeclarations, inputName: string) => boolean;
@@ -0,0 +1,86 @@
1
+ import { isIdentifierNode } from "./split-helper-guards.js";
2
+ import { isAtEndTest, isShouldPushTailTest, } from "./split-helper-tail-tests.js";
3
+ const isPushSliceCallExpression = (node, declarations, inputName) => {
4
+ if (node.type !== "CallExpression")
5
+ return false;
6
+ const callee = node.callee;
7
+ if (callee.type !== "MemberExpression")
8
+ return false;
9
+ if (callee.computed)
10
+ return false;
11
+ if (!isIdentifierNode(callee.object, declarations.partsName))
12
+ return false;
13
+ if (!isIdentifierNode(callee.property, "push"))
14
+ return false;
15
+ if (node.arguments.length !== 1)
16
+ return false;
17
+ const argument0 = node.arguments[0];
18
+ if (argument0?.type !== "CallExpression")
19
+ return false;
20
+ const sliceCallee = argument0.callee;
21
+ if (sliceCallee.type !== "MemberExpression")
22
+ return false;
23
+ if (sliceCallee.computed)
24
+ return false;
25
+ if (!isIdentifierNode(sliceCallee.object, inputName))
26
+ return false;
27
+ if (!isIdentifierNode(sliceCallee.property, "slice"))
28
+ return false;
29
+ if (argument0.arguments.length !== 1)
30
+ return false;
31
+ return isIdentifierNode(argument0.arguments[0], declarations.startIndexName);
32
+ };
33
+ const isPushSequenceExpression = (node, declarations, inputName) => {
34
+ if (node.type !== "SequenceExpression")
35
+ return false;
36
+ if (node.expressions.length !== 2)
37
+ return false;
38
+ const [first, second] = node.expressions;
39
+ if (!first || !second)
40
+ return false;
41
+ if (!isPushSliceCallExpression(first, declarations, inputName))
42
+ return false;
43
+ return isIdentifierNode(second, declarations.partsName);
44
+ };
45
+ export const isReturnWithOptionalTailPush = (node, declarations, inputName) => {
46
+ if (node.type === "ConditionalExpression") {
47
+ const testIsShouldPush = isShouldPushTailTest(node.test, declarations, inputName);
48
+ const testIsAtEnd = isAtEndTest(node.test, declarations, inputName);
49
+ if (!testIsShouldPush && !testIsAtEnd)
50
+ return false;
51
+ const consequent = node.consequent;
52
+ const alternate = node.alternate;
53
+ const consequentIsParts = isIdentifierNode(consequent, declarations.partsName);
54
+ const alternateIsParts = isIdentifierNode(alternate, declarations.partsName);
55
+ const consequentIsPush = isPushSequenceExpression(consequent, declarations, inputName);
56
+ const alternateIsPush = isPushSequenceExpression(alternate, declarations, inputName);
57
+ if (testIsShouldPush) {
58
+ if (!consequentIsPush)
59
+ return false;
60
+ return alternateIsParts;
61
+ }
62
+ if (!testIsAtEnd)
63
+ return false;
64
+ if (!alternateIsPush)
65
+ return false;
66
+ return consequentIsParts;
67
+ }
68
+ if (node.type !== "SequenceExpression")
69
+ return false;
70
+ if (node.expressions.length !== 2)
71
+ return false;
72
+ const [first, second] = node.expressions;
73
+ if (!first || !second)
74
+ return false;
75
+ if (!isIdentifierNode(second, declarations.partsName))
76
+ return false;
77
+ if (isPushSliceCallExpression(first, declarations, inputName))
78
+ return true;
79
+ if (first.type === "LogicalExpression" &&
80
+ first.operator === "&&" &&
81
+ isShouldPushTailTest(first.left, declarations, inputName) &&
82
+ isPushSliceCallExpression(first.right, declarations, inputName)) {
83
+ return true;
84
+ }
85
+ return false;
86
+ };
@@ -0,0 +1,4 @@
1
+ import type { Node } from "@babel/types";
2
+ import type { SplitHelperDeclarations } from "./split-helper-declarations.js";
3
+ export declare const isShouldPushTailTest: (node: Node, declarations: SplitHelperDeclarations, inputName: string) => boolean;
4
+ export declare const isAtEndTest: (node: Node, declarations: SplitHelperDeclarations, inputName: string) => boolean;
@@ -0,0 +1,39 @@
1
+ import { isIdentifierNode } from "./split-helper-guards.js";
2
+ const isInputLengthMemberExpression = (node, inputName) => {
3
+ if (node.type !== "MemberExpression")
4
+ return false;
5
+ if (node.computed)
6
+ return false;
7
+ if (!isIdentifierNode(node.object, inputName))
8
+ return false;
9
+ return isIdentifierNode(node.property, "length");
10
+ };
11
+ export const isShouldPushTailTest = (node, declarations, inputName) => {
12
+ if (node.type !== "BinaryExpression")
13
+ return false;
14
+ const operator = node.operator;
15
+ if ((operator === "!==" || operator === "!=" || operator === "<") &&
16
+ isIdentifierNode(node.left, declarations.startIndexName) &&
17
+ isInputLengthMemberExpression(node.right, inputName)) {
18
+ return true;
19
+ }
20
+ if ((operator === "!==" || operator === "!=" || operator === ">") &&
21
+ isInputLengthMemberExpression(node.left, inputName) &&
22
+ isIdentifierNode(node.right, declarations.startIndexName)) {
23
+ return true;
24
+ }
25
+ return false;
26
+ };
27
+ export const isAtEndTest = (node, declarations, inputName) => {
28
+ if (node.type !== "BinaryExpression")
29
+ return false;
30
+ const operator = node.operator;
31
+ if (operator !== "===" && operator !== "==")
32
+ return false;
33
+ if (isIdentifierNode(node.left, declarations.startIndexName) &&
34
+ isInputLengthMemberExpression(node.right, inputName)) {
35
+ return true;
36
+ }
37
+ return (isInputLengthMemberExpression(node.left, inputName) &&
38
+ isIdentifierNode(node.right, declarations.startIndexName));
39
+ };
@@ -0,0 +1,3 @@
1
+ import type { Statement } from "@babel/types";
2
+ import type { SplitHelperDeclarations } from "./split-helper-declarations.js";
3
+ export declare const isSplitHelperTail: (tailStatements: Statement[], declarations: SplitHelperDeclarations, inputName: string) => boolean;
@@ -0,0 +1,49 @@
1
+ import { getBlockStatements, isIdentifierNode, isPushSliceStatement, } from "./split-helper-guards.js";
2
+ import { isReturnWithOptionalTailPush } from "./split-helper-tail-expressions.js";
3
+ import { isShouldPushTailTest } from "./split-helper-tail-tests.js";
4
+ const isReturnPartsStatement = (statement, declarations) => {
5
+ if (statement?.type !== "ReturnStatement")
6
+ return false;
7
+ return isIdentifierNode(statement.argument ?? undefined, declarations.partsName);
8
+ };
9
+ export const isSplitHelperTail = (tailStatements, declarations, inputName) => {
10
+ if (tailStatements.length === 2) {
11
+ const [firstTailStatement, secondTailStatement] = tailStatements;
12
+ if (!firstTailStatement || !secondTailStatement)
13
+ return false;
14
+ if (firstTailStatement.type === "IfStatement") {
15
+ const ifStatement = firstTailStatement;
16
+ const returnStatement = secondTailStatement;
17
+ if (ifStatement.alternate)
18
+ return false;
19
+ if (!isShouldPushTailTest(ifStatement.test, declarations, inputName))
20
+ return false;
21
+ const ifStatements = getBlockStatements(ifStatement.consequent) ?? [
22
+ ifStatement.consequent,
23
+ ];
24
+ if (ifStatements.length !== 1)
25
+ return false;
26
+ const ifConsequent = ifStatements[0];
27
+ if (!ifConsequent)
28
+ return false;
29
+ if (!isPushSliceStatement(ifConsequent, declarations.partsName, inputName, declarations.startIndexName))
30
+ return false;
31
+ return isReturnPartsStatement(returnStatement, declarations);
32
+ }
33
+ if (isPushSliceStatement(firstTailStatement, declarations.partsName, inputName, declarations.startIndexName)) {
34
+ return isReturnPartsStatement(secondTailStatement, declarations);
35
+ }
36
+ return false;
37
+ }
38
+ if (tailStatements.length !== 1)
39
+ return false;
40
+ const tailStatement = tailStatements[0];
41
+ if (!tailStatement)
42
+ return false;
43
+ if (tailStatement.type !== "ReturnStatement")
44
+ return false;
45
+ const argument = tailStatement.argument;
46
+ if (!argument)
47
+ return false;
48
+ return isReturnWithOptionalTailPush(argument, declarations, inputName);
49
+ };
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.48.0",
5
+ "version": "1.49.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",