miniread 1.9.0 → 1.11.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/scripts/evaluate/create-output-slug.d.ts +7 -0
- package/dist/scripts/evaluate/create-output-slug.js +38 -0
- package/dist/scripts/evaluate/diff-utilities.d.ts +0 -1
- package/dist/scripts/evaluate/diff-utilities.js +0 -3
- package/dist/scripts/evaluate/parse-evaluate-cli-options.js +11 -4
- package/dist/scripts/snapshot/run-snapshot-cli.js +58 -69
- package/dist/transforms/rename-replace-child-parameters/ast-node-predicates.d.ts +3 -0
- package/dist/transforms/rename-replace-child-parameters/ast-node-predicates.js +12 -0
- package/dist/transforms/rename-replace-child-parameters/collect-replace-child-parameter-renames.d.ts +3 -0
- package/dist/transforms/rename-replace-child-parameters/collect-replace-child-parameter-renames.js +39 -0
- package/dist/transforms/rename-replace-child-parameters/constants.d.ts +3 -0
- package/dist/transforms/rename-replace-child-parameters/constants.js +3 -0
- package/dist/transforms/rename-replace-child-parameters/get-single-return-identifier-name.d.ts +2 -0
- package/dist/transforms/rename-replace-child-parameters/get-single-return-identifier-name.js +23 -0
- package/dist/transforms/rename-replace-child-parameters/get-two-identifier-parameters.d.ts +6 -0
- package/dist/transforms/rename-replace-child-parameters/get-two-identifier-parameters.js +13 -0
- package/dist/transforms/rename-replace-child-parameters/has-replace-child-forwarding-call.d.ts +2 -0
- package/dist/transforms/rename-replace-child-parameters/has-replace-child-forwarding-call.js +57 -0
- package/dist/transforms/rename-replace-child-parameters/is-already-child-parameter-name.d.ts +1 -0
- package/dist/transforms/rename-replace-child-parameters/is-already-child-parameter-name.js +8 -0
- package/dist/transforms/rename-replace-child-parameters/is-replace-child-named-function.d.ts +2 -0
- package/dist/transforms/rename-replace-child-parameters/is-replace-child-named-function.js +43 -0
- package/dist/transforms/rename-replace-child-parameters/rename-replace-child-parameters-transform.d.ts +2 -0
- package/dist/transforms/rename-replace-child-parameters/rename-replace-child-parameters-transform.js +44 -0
- package/dist/transforms/rename-replace-child-parameters/replace-child-types.d.ts +3 -0
- package/dist/transforms/rename-replace-child-parameters/replace-child-types.js +1 -0
- package/dist/transforms/rename-this-aliases/rename-this-aliases-transform.d.ts +2 -0
- package/dist/transforms/rename-this-aliases/rename-this-aliases-transform.js +121 -0
- package/dist/transforms/transform-registry.js +4 -0
- package/package.json +1 -1
- package/transform-manifest.json +22 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { sanitizeFileComponent } from "./sanitize.js";
|
|
3
|
+
const isSameOrderedList = (a, b) => {
|
|
4
|
+
if (a.length !== b.length)
|
|
5
|
+
return false;
|
|
6
|
+
for (const [index, value] of a.entries()) {
|
|
7
|
+
const other = b[index];
|
|
8
|
+
if (other === undefined)
|
|
9
|
+
return false;
|
|
10
|
+
if (value !== other)
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
};
|
|
15
|
+
const createRawTransformSlug = (options) => {
|
|
16
|
+
const { transforms, recommendedTransforms, allTransformIds } = options;
|
|
17
|
+
if (transforms.length === 0)
|
|
18
|
+
return "none";
|
|
19
|
+
if (isSameOrderedList(transforms, recommendedTransforms))
|
|
20
|
+
return "recommended";
|
|
21
|
+
if (isSameOrderedList(transforms, allTransformIds))
|
|
22
|
+
return "all";
|
|
23
|
+
return transforms.join("-");
|
|
24
|
+
};
|
|
25
|
+
export const createOutputSlug = (options) => {
|
|
26
|
+
const sanitized = sanitizeFileComponent(createRawTransformSlug(options));
|
|
27
|
+
const maxLength = 120;
|
|
28
|
+
if (sanitized.length <= maxLength)
|
|
29
|
+
return sanitized;
|
|
30
|
+
const hash = crypto
|
|
31
|
+
.createHash("sha256")
|
|
32
|
+
.update(sanitized)
|
|
33
|
+
.digest("hex")
|
|
34
|
+
.slice(0, 10);
|
|
35
|
+
const prefixLength = maxLength - hash.length - 1;
|
|
36
|
+
const prefix = sanitized.slice(0, Math.max(0, prefixLength));
|
|
37
|
+
return `${prefix}-${hash}`;
|
|
38
|
+
};
|
|
@@ -4,9 +4,6 @@ import fs from "node:fs";
|
|
|
4
4
|
import * as fsPromises from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import readline from "node:readline";
|
|
7
|
-
export const createTransformSlug = (transforms) => {
|
|
8
|
-
return transforms.length > 0 ? transforms.join("-") : "none";
|
|
9
|
-
};
|
|
10
7
|
const isErrnoException = (error) => {
|
|
11
8
|
return error instanceof Error;
|
|
12
9
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { transformPresets } from "../../transforms/transform-presets.js";
|
|
3
3
|
import { allTransformIds } from "../../transforms/transform-registry.js";
|
|
4
|
-
import {
|
|
5
|
-
import { sanitizeFileComponent } from "./sanitize.js";
|
|
4
|
+
import { createOutputSlug } from "./create-output-slug.js";
|
|
6
5
|
const parseTransformList = (value) => {
|
|
7
6
|
if (!value || value === "none")
|
|
8
7
|
return [];
|
|
@@ -66,8 +65,16 @@ export const parseEvaluateCliOptions = (options) => {
|
|
|
66
65
|
const verbose = rawOptions.verbose ?? false;
|
|
67
66
|
const overwrite = rawOptions.overwrite ?? false;
|
|
68
67
|
const formatCode = rawOptions.formatCode;
|
|
69
|
-
const baselineSlug =
|
|
70
|
-
|
|
68
|
+
const baselineSlug = createOutputSlug({
|
|
69
|
+
transforms: baselineTransforms,
|
|
70
|
+
recommendedTransforms: transformPresets.recommended,
|
|
71
|
+
allTransformIds,
|
|
72
|
+
});
|
|
73
|
+
const testSlug = createOutputSlug({
|
|
74
|
+
transforms: testTransforms,
|
|
75
|
+
recommendedTransforms: transformPresets.recommended,
|
|
76
|
+
allTransformIds,
|
|
77
|
+
});
|
|
71
78
|
const sourcesDirectory = path.resolve(cwd, rawOptions.sources);
|
|
72
79
|
const evaluationDirectory = path.resolve(cwd, rawOptions.evaluationDir);
|
|
73
80
|
const manifestPath = path.resolve(cwd, rawOptions.manifestFile);
|
|
@@ -70,24 +70,11 @@ export const runSnapshotCli = async (argv) => {
|
|
|
70
70
|
}
|
|
71
71
|
const expectedPath = path.join(testCaseDirectory, `${transform}-expected.js`);
|
|
72
72
|
const actualPath = path.join(testCaseDirectory, `${transform}.js`);
|
|
73
|
-
// Run the transform and format
|
|
74
|
-
let actualOutput;
|
|
75
|
-
try {
|
|
76
|
-
const rawOutput = await runTransform(basePath, transform);
|
|
77
|
-
actualOutput = await formatCode(rawOutput);
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
-
console.error(`Error running transform: ${message}`);
|
|
82
|
-
return 1;
|
|
83
|
-
}
|
|
84
73
|
if (expected) {
|
|
85
74
|
// Expected-file workflow
|
|
86
75
|
// Step 1: Check if expected file exists, create from base.js if not
|
|
87
|
-
let expectedExists = false;
|
|
88
76
|
try {
|
|
89
77
|
await fs.access(expectedPath);
|
|
90
|
-
expectedExists = true;
|
|
91
78
|
}
|
|
92
79
|
catch {
|
|
93
80
|
// Expected file doesn't exist, create it from base.js
|
|
@@ -96,67 +83,69 @@ export const runSnapshotCli = async (argv) => {
|
|
|
96
83
|
await fs.writeFile(expectedPath, formattedBase);
|
|
97
84
|
console.log(`Created: test-cases/${testcase}/${transform}-expected.js`);
|
|
98
85
|
console.log("Edit this file to match your expected output, then re-run.");
|
|
99
|
-
}
|
|
100
|
-
// Step 2: Write actual output
|
|
101
|
-
await fs.writeFile(actualPath, actualOutput);
|
|
102
|
-
console.log(`Created: test-cases/${testcase}/${transform}.js`);
|
|
103
|
-
if (!expectedExists) {
|
|
104
|
-
// First run - just created expected file, user needs to edit it
|
|
105
|
-
return 0;
|
|
106
|
-
}
|
|
107
|
-
// Step 3: Compare expected vs actual
|
|
108
|
-
const expectedContent = await fs.readFile(expectedPath, "utf8");
|
|
109
|
-
if (expectedContent === actualOutput) {
|
|
110
|
-
// Success! Delete expected file
|
|
111
|
-
await fs.unlink(expectedPath);
|
|
112
|
-
console.log(`\nSuccess: Output matches expected.`);
|
|
113
|
-
console.log(`Deleted: test-cases/${testcase}/${transform}-expected.js`);
|
|
114
|
-
console.log(`\nSnapshot ready: test-cases/${testcase}/${transform}.js`);
|
|
115
86
|
return 0;
|
|
116
87
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
88
|
+
}
|
|
89
|
+
// Common path: Run the transform, format, and write actual output
|
|
90
|
+
let actualOutput;
|
|
91
|
+
try {
|
|
92
|
+
const rawOutput = await runTransform(basePath, transform);
|
|
93
|
+
actualOutput = await formatCode(rawOutput);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
console.error(`Error running transform: ${message}`);
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
await fs.writeFile(actualPath, actualOutput);
|
|
101
|
+
console.log(`Created: test-cases/${testcase}/${transform}.js`);
|
|
102
|
+
if (!expected) {
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
// Step 3: Compare expected vs actual
|
|
106
|
+
const expectedContent = await fs.readFile(expectedPath, "utf8");
|
|
107
|
+
if (expectedContent === actualOutput) {
|
|
108
|
+
// Success! Delete expected file
|
|
109
|
+
await fs.unlink(expectedPath);
|
|
110
|
+
console.log(`\nSuccess: Output matches expected.`);
|
|
111
|
+
console.log(`Deleted: test-cases/${testcase}/${transform}-expected.js`);
|
|
112
|
+
console.log(`\nSnapshot ready: test-cases/${testcase}/${transform}.js`);
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
// Diff - show the difference
|
|
116
|
+
console.log(`\nDiff: expected vs actual`);
|
|
117
|
+
console.log("---");
|
|
118
|
+
// Simple line-by-line diff output
|
|
119
|
+
const expectedLines = expectedContent.split("\n");
|
|
120
|
+
const actualLines = actualOutput.split("\n");
|
|
121
|
+
const maxLines = Math.max(expectedLines.length, actualLines.length);
|
|
122
|
+
let hasDiff = false;
|
|
123
|
+
for (let lineIndex = 0; lineIndex < maxLines; lineIndex++) {
|
|
124
|
+
const exp = expectedLines[lineIndex];
|
|
125
|
+
const act = actualLines[lineIndex];
|
|
126
|
+
if (exp !== act) {
|
|
127
|
+
hasDiff = true;
|
|
128
|
+
if (exp !== undefined && act === undefined) {
|
|
129
|
+
console.log(`Line ${lineIndex + 1}:`);
|
|
130
|
+
console.log(` - ${exp}`);
|
|
145
131
|
}
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
console.log(
|
|
132
|
+
else if (exp === undefined && act !== undefined) {
|
|
133
|
+
console.log(`Line ${lineIndex + 1}:`);
|
|
134
|
+
console.log(` + ${act}`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.log(`Line ${lineIndex + 1}:`);
|
|
138
|
+
console.log(` - ${exp}`);
|
|
139
|
+
console.log(` + ${act}`);
|
|
149
140
|
}
|
|
150
|
-
console.log("---");
|
|
151
|
-
console.log(`\nExpected file preserved: test-cases/${testcase}/${transform}-expected.js`);
|
|
152
|
-
console.log("Fix the implementation or update the expected file, then re-run.");
|
|
153
|
-
return 1;
|
|
154
141
|
}
|
|
155
142
|
}
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
console.log(`Created: test-cases/${testcase}/${transform}.js`);
|
|
160
|
-
return 0;
|
|
143
|
+
if (!hasDiff) {
|
|
144
|
+
// Shouldn't happen since we already checked equality, but just in case
|
|
145
|
+
console.log("(no visible differences)");
|
|
161
146
|
}
|
|
147
|
+
console.log("---");
|
|
148
|
+
console.log(`\nExpected file preserved: test-cases/${testcase}/${transform}-expected.js`);
|
|
149
|
+
console.log("Fix the implementation or update the expected file, then re-run.");
|
|
150
|
+
return 1;
|
|
162
151
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const isIdentifierNamed = (node, name) => {
|
|
2
|
+
return (typeof node === "object" &&
|
|
3
|
+
node !== null &&
|
|
4
|
+
node.type === "Identifier" &&
|
|
5
|
+
node.name === name);
|
|
6
|
+
};
|
|
7
|
+
export const isStringLiteralValue = (node, value) => {
|
|
8
|
+
return (typeof node === "object" &&
|
|
9
|
+
node !== null &&
|
|
10
|
+
node.type === "StringLiteral" &&
|
|
11
|
+
node.value === value);
|
|
12
|
+
};
|
package/dist/transforms/rename-replace-child-parameters/collect-replace-child-parameter-renames.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isStableRenamed } from "../../core/stable-naming.js";
|
|
2
|
+
import { BASE_NEW_CHILD, BASE_OLD_CHILD } from "./constants.js";
|
|
3
|
+
import { getSingleReturnIdentifierName } from "./get-single-return-identifier-name.js";
|
|
4
|
+
import { getTwoIdentifierParameters } from "./get-two-identifier-parameters.js";
|
|
5
|
+
import { hasReplaceChildForwardingCall } from "./has-replace-child-forwarding-call.js";
|
|
6
|
+
import { isAlreadyChildParameterName } from "./is-already-child-parameter-name.js";
|
|
7
|
+
import { isReplaceChildNamedFunction } from "./is-replace-child-named-function.js";
|
|
8
|
+
export const collectReplaceChildParameterRenames = (path, group) => {
|
|
9
|
+
if (!isReplaceChildNamedFunction(path))
|
|
10
|
+
return;
|
|
11
|
+
if (path.node.body.type !== "BlockStatement")
|
|
12
|
+
return;
|
|
13
|
+
const parameters = getTwoIdentifierParameters(path);
|
|
14
|
+
if (!parameters)
|
|
15
|
+
return;
|
|
16
|
+
const returnIdentifierName = getSingleReturnIdentifierName(path);
|
|
17
|
+
if (returnIdentifierName !== parameters.parameter1.name)
|
|
18
|
+
return;
|
|
19
|
+
if (!hasReplaceChildForwardingCall(path, parameters.parameter0.name, parameters.parameter1.name))
|
|
20
|
+
return;
|
|
21
|
+
const parameter0Name = parameters.parameter0.name;
|
|
22
|
+
if (!isStableRenamed(parameter0Name) &&
|
|
23
|
+
!isAlreadyChildParameterName(parameter0Name, BASE_NEW_CHILD)) {
|
|
24
|
+
group.add({
|
|
25
|
+
scope: path.scope,
|
|
26
|
+
currentName: parameter0Name,
|
|
27
|
+
baseName: BASE_NEW_CHILD,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const parameter1Name = parameters.parameter1.name;
|
|
31
|
+
if (!isStableRenamed(parameter1Name) &&
|
|
32
|
+
!isAlreadyChildParameterName(parameter1Name, BASE_OLD_CHILD)) {
|
|
33
|
+
group.add({
|
|
34
|
+
scope: path.scope,
|
|
35
|
+
currentName: parameter1Name,
|
|
36
|
+
baseName: BASE_OLD_CHILD,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const getSingleReturnIdentifierName = (path) => {
|
|
2
|
+
let returnCount = 0;
|
|
3
|
+
let identifierName;
|
|
4
|
+
const rootNode = path.node;
|
|
5
|
+
path.traverse({
|
|
6
|
+
Function(innerPath) {
|
|
7
|
+
if (innerPath.node !== rootNode)
|
|
8
|
+
innerPath.skip();
|
|
9
|
+
},
|
|
10
|
+
ReturnStatement(returnPath) {
|
|
11
|
+
returnCount++;
|
|
12
|
+
const argument = returnPath.node.argument;
|
|
13
|
+
if (argument?.type !== "Identifier") {
|
|
14
|
+
identifierName = undefined;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
identifierName = argument.name;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
if (returnCount !== 1)
|
|
21
|
+
return;
|
|
22
|
+
return identifierName;
|
|
23
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Identifier } from "@babel/types";
|
|
2
|
+
import type { ReplaceChildFunctionPath } from "./replace-child-types.js";
|
|
3
|
+
export declare const getTwoIdentifierParameters: (path: ReplaceChildFunctionPath) => {
|
|
4
|
+
parameter0: Identifier;
|
|
5
|
+
parameter1: Identifier;
|
|
6
|
+
} | undefined;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const getTwoIdentifierParameters = (path) => {
|
|
2
|
+
if (path.node.params.length !== 2)
|
|
3
|
+
return;
|
|
4
|
+
const parameter0 = path.node.params[0];
|
|
5
|
+
const parameter1 = path.node.params[1];
|
|
6
|
+
if (parameter0?.type !== "Identifier")
|
|
7
|
+
return;
|
|
8
|
+
if (parameter1?.type !== "Identifier")
|
|
9
|
+
return;
|
|
10
|
+
if (parameter0.name === parameter1.name)
|
|
11
|
+
return;
|
|
12
|
+
return { parameter0, parameter1 };
|
|
13
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { isIdentifierNamed } from "./ast-node-predicates.js";
|
|
2
|
+
import { REPLACE_CHILD_NAME } from "./constants.js";
|
|
3
|
+
export const hasReplaceChildForwardingCall = (path, parameter0Name, parameter1Name) => {
|
|
4
|
+
let found = false;
|
|
5
|
+
const rootNode = path.node;
|
|
6
|
+
path.traverse({
|
|
7
|
+
Function(innerPath) {
|
|
8
|
+
if (innerPath.node !== rootNode)
|
|
9
|
+
innerPath.skip();
|
|
10
|
+
},
|
|
11
|
+
CallExpression(callPath) {
|
|
12
|
+
if (found)
|
|
13
|
+
return;
|
|
14
|
+
const call = callPath.node;
|
|
15
|
+
const callee = call.callee;
|
|
16
|
+
if (callee.type !== "MemberExpression")
|
|
17
|
+
return;
|
|
18
|
+
if (callee.computed)
|
|
19
|
+
return;
|
|
20
|
+
if (callee.property.type !== "Identifier")
|
|
21
|
+
return;
|
|
22
|
+
// something.replaceChild(parameter0, parameter1)
|
|
23
|
+
if (callee.property.name === REPLACE_CHILD_NAME) {
|
|
24
|
+
const argument0 = call.arguments[0];
|
|
25
|
+
const argument1 = call.arguments[1];
|
|
26
|
+
if (!isIdentifierNamed(argument0, parameter0Name))
|
|
27
|
+
return;
|
|
28
|
+
if (!isIdentifierNamed(argument1, parameter1Name))
|
|
29
|
+
return;
|
|
30
|
+
found = true;
|
|
31
|
+
callPath.stop();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// something.replaceChild.call(this, parameter0, parameter1)
|
|
35
|
+
if (callee.property.name !== "call")
|
|
36
|
+
return;
|
|
37
|
+
const target = callee.object;
|
|
38
|
+
if (target.type !== "MemberExpression")
|
|
39
|
+
return;
|
|
40
|
+
if (target.computed)
|
|
41
|
+
return;
|
|
42
|
+
if (target.property.type !== "Identifier")
|
|
43
|
+
return;
|
|
44
|
+
if (target.property.name !== REPLACE_CHILD_NAME)
|
|
45
|
+
return;
|
|
46
|
+
const argument1 = call.arguments[1];
|
|
47
|
+
const argument2 = call.arguments[2];
|
|
48
|
+
if (!isIdentifierNamed(argument1, parameter0Name))
|
|
49
|
+
return;
|
|
50
|
+
if (!isIdentifierNamed(argument2, parameter1Name))
|
|
51
|
+
return;
|
|
52
|
+
found = true;
|
|
53
|
+
callPath.stop();
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
return found;
|
|
57
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isAlreadyChildParameterName: (name: string, base: string) => boolean;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isIdentifierNamed, isStringLiteralValue, } from "./ast-node-predicates.js";
|
|
2
|
+
import { REPLACE_CHILD_NAME } from "./constants.js";
|
|
3
|
+
const isReplaceChildKey = (key) => {
|
|
4
|
+
if (isIdentifierNamed(key, REPLACE_CHILD_NAME))
|
|
5
|
+
return true;
|
|
6
|
+
if (isStringLiteralValue(key, REPLACE_CHILD_NAME))
|
|
7
|
+
return true;
|
|
8
|
+
return false;
|
|
9
|
+
};
|
|
10
|
+
const isReplaceChildValueDescriptorFunction = (path) => {
|
|
11
|
+
const valueProperty = path.parentPath;
|
|
12
|
+
if (!valueProperty.isObjectProperty())
|
|
13
|
+
return false;
|
|
14
|
+
const valueKey = valueProperty.node.key;
|
|
15
|
+
const isValueKey = isIdentifierNamed(valueKey, "value") ||
|
|
16
|
+
isStringLiteralValue(valueKey, "value");
|
|
17
|
+
if (!isValueKey)
|
|
18
|
+
return false;
|
|
19
|
+
const descriptor = valueProperty.parentPath;
|
|
20
|
+
if (!descriptor.isObjectExpression())
|
|
21
|
+
return false;
|
|
22
|
+
const replaceChildProperty = descriptor.parentPath;
|
|
23
|
+
if (!replaceChildProperty.isObjectProperty())
|
|
24
|
+
return false;
|
|
25
|
+
return isReplaceChildKey(replaceChildProperty.node.key);
|
|
26
|
+
};
|
|
27
|
+
export const isReplaceChildNamedFunction = (path) => {
|
|
28
|
+
if ((path.isFunctionDeclaration() || path.isFunctionExpression()) &&
|
|
29
|
+
isIdentifierNamed(path.node.id, REPLACE_CHILD_NAME)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (path.isObjectMethod() || path.isClassMethod()) {
|
|
33
|
+
return isReplaceChildKey(path.node.key);
|
|
34
|
+
}
|
|
35
|
+
const parent = path.parentPath;
|
|
36
|
+
if (parent.isObjectProperty() && isReplaceChildKey(parent.node.key)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (path.isFunctionExpression() || path.isArrowFunctionExpression()) {
|
|
40
|
+
return isReplaceChildValueDescriptorFunction(path);
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
};
|
package/dist/transforms/rename-replace-child-parameters/rename-replace-child-parameters-transform.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { RenameGroup } from "../../core/stable-naming.js";
|
|
3
|
+
import { getFilesToProcess, } from "../../core/types.js";
|
|
4
|
+
import { collectReplaceChildParameterRenames } from "./collect-replace-child-parameter-renames.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
|
+
export const renameReplaceChildParametersTransform = {
|
|
9
|
+
id: "rename-replace-child-parameters",
|
|
10
|
+
description: "Renames replaceChild forwarding wrapper parameters to $newChild and $oldChild",
|
|
11
|
+
scope: "file",
|
|
12
|
+
parallelizable: true,
|
|
13
|
+
transform(context) {
|
|
14
|
+
let nodesVisited = 0;
|
|
15
|
+
let transformationsApplied = 0;
|
|
16
|
+
for (const fileInfo of getFilesToProcess(context)) {
|
|
17
|
+
const group = new RenameGroup();
|
|
18
|
+
traverse(fileInfo.ast, {
|
|
19
|
+
FunctionDeclaration(path) {
|
|
20
|
+
nodesVisited++;
|
|
21
|
+
collectReplaceChildParameterRenames(path, group);
|
|
22
|
+
},
|
|
23
|
+
FunctionExpression(path) {
|
|
24
|
+
nodesVisited++;
|
|
25
|
+
collectReplaceChildParameterRenames(path, group);
|
|
26
|
+
},
|
|
27
|
+
ArrowFunctionExpression(path) {
|
|
28
|
+
nodesVisited++;
|
|
29
|
+
collectReplaceChildParameterRenames(path, group);
|
|
30
|
+
},
|
|
31
|
+
ObjectMethod(path) {
|
|
32
|
+
nodesVisited++;
|
|
33
|
+
collectReplaceChildParameterRenames(path, group);
|
|
34
|
+
},
|
|
35
|
+
ClassMethod(path) {
|
|
36
|
+
nodesVisited++;
|
|
37
|
+
collectReplaceChildParameterRenames(path, group);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
transformationsApplied += group.apply();
|
|
41
|
+
}
|
|
42
|
+
return Promise.resolve({ nodesVisited, transformationsApplied });
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { NodePath } from "@babel/traverse";
|
|
2
|
+
import type { ArrowFunctionExpression, ClassMethod, FunctionDeclaration, FunctionExpression, ObjectMethod } from "@babel/types";
|
|
3
|
+
export type ReplaceChildFunctionPath = NodePath<FunctionDeclaration> | NodePath<FunctionExpression> | NodePath<ArrowFunctionExpression> | NodePath<ObjectMethod> | NodePath<ClassMethod>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { isArrayPattern, isAssignmentPattern, isClassDeclaration, isExportNamedDeclaration, isExportSpecifier, isFunctionDeclaration, isIdentifier, isObjectPattern, isObjectProperty, isRestElement, isVariableDeclaration, } from "@babel/types";
|
|
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 BASE_NAME = "thisRef";
|
|
9
|
+
const addBindingNamesFromNode = (node, out) => {
|
|
10
|
+
const babelNode = node;
|
|
11
|
+
if (!babelNode)
|
|
12
|
+
return;
|
|
13
|
+
if (babelNode.type === "VoidPattern")
|
|
14
|
+
return;
|
|
15
|
+
if (isIdentifier(babelNode)) {
|
|
16
|
+
out.add(babelNode.name);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (isObjectPattern(babelNode)) {
|
|
20
|
+
for (const property of babelNode.properties) {
|
|
21
|
+
if (isRestElement(property)) {
|
|
22
|
+
addBindingNamesFromNode(property.argument, out);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (!isObjectProperty(property))
|
|
26
|
+
continue;
|
|
27
|
+
addBindingNamesFromNode(property.value, out);
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (isArrayPattern(babelNode)) {
|
|
32
|
+
for (const element of babelNode.elements) {
|
|
33
|
+
addBindingNamesFromNode(element, out);
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (isRestElement(babelNode)) {
|
|
38
|
+
addBindingNamesFromNode(babelNode.argument, out);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (isAssignmentPattern(babelNode)) {
|
|
42
|
+
addBindingNamesFromNode(babelNode.left, out);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const collectExportedNames = (program) => {
|
|
46
|
+
const exportedNames = new Set();
|
|
47
|
+
for (const statement of program.body) {
|
|
48
|
+
if (!isExportNamedDeclaration(statement))
|
|
49
|
+
continue;
|
|
50
|
+
const declaration = statement.declaration;
|
|
51
|
+
if (declaration) {
|
|
52
|
+
if (isVariableDeclaration(declaration)) {
|
|
53
|
+
for (const declarator of declaration.declarations) {
|
|
54
|
+
addBindingNamesFromNode(declarator.id, exportedNames);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (isFunctionDeclaration(declaration) ||
|
|
58
|
+
isClassDeclaration(declaration)) {
|
|
59
|
+
const id = declaration.id;
|
|
60
|
+
if (!id)
|
|
61
|
+
continue;
|
|
62
|
+
exportedNames.add(id.name);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const specifier of statement.specifiers) {
|
|
66
|
+
if (!isExportSpecifier(specifier))
|
|
67
|
+
continue;
|
|
68
|
+
const local = specifier.local;
|
|
69
|
+
if (!isIdentifier(local))
|
|
70
|
+
continue;
|
|
71
|
+
exportedNames.add(local.name);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return exportedNames;
|
|
75
|
+
};
|
|
76
|
+
export const renameThisAliasesTransform = {
|
|
77
|
+
id: "rename-this-aliases",
|
|
78
|
+
description: "Renames `var x = this` aliases to $thisRef (stable when unique) or thisRef/thisRef2/...",
|
|
79
|
+
scope: "file",
|
|
80
|
+
parallelizable: true,
|
|
81
|
+
transform(context) {
|
|
82
|
+
let nodesVisited = 0;
|
|
83
|
+
let transformationsApplied = 0;
|
|
84
|
+
for (const fileInfo of getFilesToProcess(context)) {
|
|
85
|
+
const group = new RenameGroup();
|
|
86
|
+
const exportedNames = collectExportedNames(fileInfo.ast.program);
|
|
87
|
+
traverse(fileInfo.ast, {
|
|
88
|
+
VariableDeclarator(path) {
|
|
89
|
+
nodesVisited++;
|
|
90
|
+
const { id, init } = path.node;
|
|
91
|
+
if (id.type !== "Identifier")
|
|
92
|
+
return;
|
|
93
|
+
if (init?.type !== "ThisExpression")
|
|
94
|
+
return;
|
|
95
|
+
const currentName = id.name;
|
|
96
|
+
if (isStableRenamed(currentName))
|
|
97
|
+
return;
|
|
98
|
+
const binding = path.scope.getBinding(currentName);
|
|
99
|
+
if (!binding)
|
|
100
|
+
return;
|
|
101
|
+
if (!binding.constant)
|
|
102
|
+
return;
|
|
103
|
+
if (binding.scope.block.type === "Program" &&
|
|
104
|
+
exportedNames.has(currentName)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
group.add({
|
|
108
|
+
scope: binding.scope,
|
|
109
|
+
currentName,
|
|
110
|
+
baseName: BASE_NAME,
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
transformationsApplied += group.apply();
|
|
115
|
+
}
|
|
116
|
+
return Promise.resolve({
|
|
117
|
+
nodesVisited,
|
|
118
|
+
transformationsApplied,
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
};
|
|
@@ -9,6 +9,8 @@ import { renameLoopIndexVariablesTransform } from "./rename-loop-index-variables
|
|
|
9
9
|
import { renameLoopIndexVariablesV2Transform } from "./rename-loop-index-variables-v2/rename-loop-index-variables-v2-transform.js";
|
|
10
10
|
import { renameLoopIndexVariablesV3Transform } from "./rename-loop-index-variables-v3/rename-loop-index-variables-v3-transform.js";
|
|
11
11
|
import { renamePromiseExecutorParametersTransform } from "./rename-promise-executor-parameters/rename-promise-executor-parameters-transform.js";
|
|
12
|
+
import { renameReplaceChildParametersTransform } from "./rename-replace-child-parameters/rename-replace-child-parameters-transform.js";
|
|
13
|
+
import { renameThisAliasesTransform } from "./rename-this-aliases/rename-this-aliases-transform.js";
|
|
12
14
|
import { renameTimeoutIdsTransform } from "./rename-timeout-ids/rename-timeout-ids-transform.js";
|
|
13
15
|
import { renameUseReferenceGuardsTransform } from "./rename-use-reference-guards/rename-use-reference-guards-transform.js";
|
|
14
16
|
import { renameUseReferenceGuardsV2Transform } from "./rename-use-reference-guards-v2/rename-use-reference-guards-v2-transform.js";
|
|
@@ -25,6 +27,8 @@ export const transformRegistry = {
|
|
|
25
27
|
[renameLoopIndexVariablesV2Transform.id]: renameLoopIndexVariablesV2Transform,
|
|
26
28
|
[renameLoopIndexVariablesV3Transform.id]: renameLoopIndexVariablesV3Transform,
|
|
27
29
|
[renamePromiseExecutorParametersTransform.id]: renamePromiseExecutorParametersTransform,
|
|
30
|
+
[renameReplaceChildParametersTransform.id]: renameReplaceChildParametersTransform,
|
|
31
|
+
[renameThisAliasesTransform.id]: renameThisAliasesTransform,
|
|
28
32
|
[renameTimeoutIdsTransform.id]: renameTimeoutIdsTransform,
|
|
29
33
|
[renameUseReferenceGuardsTransform.id]: renameUseReferenceGuardsTransform,
|
|
30
34
|
[renameUseReferenceGuardsV2Transform.id]: renameUseReferenceGuardsV2Transform,
|
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.11.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",
|
package/transform-manifest.json
CHANGED
|
@@ -81,6 +81,16 @@
|
|
|
81
81
|
"evaluatedAt": "2026-01-22T12:49:10.952Z",
|
|
82
82
|
"notes": "Auto-added by evaluation script. Measured with baseline none: 0.09%."
|
|
83
83
|
},
|
|
84
|
+
{
|
|
85
|
+
"id": "rename-this-aliases",
|
|
86
|
+
"description": "Renames `var x = this` aliases to $thisRef (stable when unique) or thisRef/thisRef2/...",
|
|
87
|
+
"scope": "file",
|
|
88
|
+
"parallelizable": true,
|
|
89
|
+
"diffReductionImpact": 0.00003774938185385768,
|
|
90
|
+
"recommended": true,
|
|
91
|
+
"evaluatedAt": "2026-01-23T17:57:26.908Z",
|
|
92
|
+
"notes": "Measured with baseline none: 0.00%. Improves readability by stabilizing `this` aliases used for closures."
|
|
93
|
+
},
|
|
84
94
|
{
|
|
85
95
|
"id": "expand-sequence-expressions-v4",
|
|
86
96
|
"description": "Expands comma operator sequences in returns, throws, statements (including control-flow bodies), and variable initializers",
|
|
@@ -141,6 +151,16 @@
|
|
|
141
151
|
"evaluatedAt": "2026-01-22T21:39:53.578Z",
|
|
142
152
|
"notes": "Auto-added by evaluation script."
|
|
143
153
|
},
|
|
154
|
+
{
|
|
155
|
+
"id": "rename-replace-child-parameters",
|
|
156
|
+
"description": "Renames replaceChild forwarding wrapper parameters to $newChild and $oldChild",
|
|
157
|
+
"scope": "file",
|
|
158
|
+
"parallelizable": true,
|
|
159
|
+
"diffReductionImpact": 0,
|
|
160
|
+
"recommended": false,
|
|
161
|
+
"evaluatedAt": "2026-01-23T17:28:09.184Z",
|
|
162
|
+
"notes": "Measured with baseline none: 0.00%."
|
|
163
|
+
},
|
|
144
164
|
{
|
|
145
165
|
"id": "expand-special-number-literals",
|
|
146
166
|
"description": "Expands 1/0 to Infinity, -1/0 (and 1/-0) to -Infinity, and 0/0 to NaN (when not shadowed)",
|
|
@@ -154,8 +174,8 @@
|
|
|
154
174
|
],
|
|
155
175
|
"presetStats": {
|
|
156
176
|
"recommended": {
|
|
157
|
-
"diffReductionImpact": 0.
|
|
158
|
-
"notes": "Measured with baseline none: 0.
|
|
177
|
+
"diffReductionImpact": 0.003690002076215948,
|
|
178
|
+
"notes": "Measured with baseline none: 0.37%."
|
|
159
179
|
}
|
|
160
180
|
}
|
|
161
181
|
}
|