miniread 1.17.0 → 1.18.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/snapshot/format-code.d.ts +1 -0
- package/dist/scripts/snapshot/format-code.js +4 -0
- package/dist/scripts/snapshot/print-snapshot-diff.d.ts +6 -0
- package/dist/scripts/snapshot/print-snapshot-diff.js +26 -0
- package/dist/scripts/snapshot/resolve-testcase-input.d.ts +16 -0
- package/dist/scripts/snapshot/resolve-testcase-input.js +30 -0
- package/dist/scripts/snapshot/run-snapshot-cli.js +67 -119
- package/dist/scripts/snapshot/run-testcase-transform.d.ts +13 -0
- package/dist/scripts/snapshot/run-testcase-transform.js +46 -0
- package/dist/transforms/simplify-boolean-negations/simplify-boolean-negations-transform.d.ts +2 -0
- package/dist/transforms/simplify-boolean-negations/simplify-boolean-negations-transform.js +90 -0
- package/dist/transforms/transform-registry.js +2 -0
- package/package.json +1 -1
- package/transform-manifest.json +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const formatCode: (code: string) => Promise<string>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const printSnapshotDiff = (options) => {
|
|
2
|
+
const { expectedContent, actualOutput } = options;
|
|
3
|
+
console.log(`\nDiff: expected vs actual`);
|
|
4
|
+
console.log("---");
|
|
5
|
+
const expectedLines = expectedContent.split("\n");
|
|
6
|
+
const actualLines = actualOutput.split("\n");
|
|
7
|
+
const maxLines = Math.max(expectedLines.length, actualLines.length);
|
|
8
|
+
for (let lineIndex = 0; lineIndex < maxLines; lineIndex++) {
|
|
9
|
+
const expectedLine = expectedLines[lineIndex];
|
|
10
|
+
const actualLine = actualLines[lineIndex];
|
|
11
|
+
if (expectedLine === actualLine)
|
|
12
|
+
continue;
|
|
13
|
+
console.log(`Line ${lineIndex + 1}:`);
|
|
14
|
+
if (expectedLine !== undefined && actualLine === undefined) {
|
|
15
|
+
console.log(` - ${expectedLine}`);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (expectedLine === undefined && actualLine !== undefined) {
|
|
19
|
+
console.log(` + ${actualLine}`);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
console.log(` - ${expectedLine}`);
|
|
23
|
+
console.log(` + ${actualLine}`);
|
|
24
|
+
}
|
|
25
|
+
console.log("---");
|
|
26
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type ResolveTestcaseInputOptions = {
|
|
2
|
+
testcase: string;
|
|
3
|
+
};
|
|
4
|
+
type ResolveTestcaseInputValue = {
|
|
5
|
+
testcaseDirectory: string;
|
|
6
|
+
basePath: string;
|
|
7
|
+
};
|
|
8
|
+
type Result<T> = {
|
|
9
|
+
ok: true;
|
|
10
|
+
value: T;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
error: Error;
|
|
14
|
+
};
|
|
15
|
+
export declare const resolveTestcaseInput: (options: ResolveTestcaseInputOptions) => Promise<Result<ResolveTestcaseInputValue>>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const TEST_CASES_DIR = path.resolve(import.meta.dirname, "../../../test-cases");
|
|
4
|
+
export const resolveTestcaseInput = async (options) => {
|
|
5
|
+
const { testcase } = options;
|
|
6
|
+
const testcaseDirectory = path.join(TEST_CASES_DIR, testcase);
|
|
7
|
+
try {
|
|
8
|
+
const stats = await fs.stat(testcaseDirectory);
|
|
9
|
+
if (!stats.isDirectory()) {
|
|
10
|
+
return { ok: false, error: new Error(`${testcase} is not a directory`) };
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {
|
|
15
|
+
ok: false,
|
|
16
|
+
error: new Error(`Test case directory not found: test-cases/${testcase}`),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const basePath = path.join(testcaseDirectory, "base.js");
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(basePath);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
error: new Error(`base.js not found in test-cases/${testcase}/`),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return { ok: true, value: { testcaseDirectory, basePath } };
|
|
30
|
+
};
|
|
@@ -1,75 +1,39 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import * as prettier from "prettier";
|
|
5
3
|
import packageJson from "../../../package.json" with { type: "json" };
|
|
6
|
-
import {
|
|
7
|
-
import { transformPresets } from "../../transforms/transform-presets.js";
|
|
8
|
-
import { transformRegistry } from "../../transforms/transform-registry.js";
|
|
4
|
+
import { formatCode } from "./format-code.js";
|
|
9
5
|
import { createSnapshotCommand, } from "./create-snapshot-command.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const TEST_CASES_DIR = path.resolve(import.meta.dirname, "../../../test-cases");
|
|
14
|
-
const formatCode = async (code) => prettier.format(code, { parser: "babel" });
|
|
15
|
-
const runTransform = async (inputPath, transformId) => {
|
|
16
|
-
const content = await fs.readFile(inputPath, "utf8");
|
|
17
|
-
// Handle "recommended" as a special case that runs all recommended transforms
|
|
18
|
-
const transformIds = transformId === "recommended"
|
|
19
|
-
? transformPresets.recommended
|
|
20
|
-
: [transformId];
|
|
21
|
-
const transforms = transformIds.map((id) => {
|
|
22
|
-
const transform = transformRegistry[id];
|
|
23
|
-
if (!transform) {
|
|
24
|
-
throw new Error(`Unknown transform: ${id}. Run 'miniread --list-transforms' to see available transforms.`);
|
|
25
|
-
}
|
|
26
|
-
return transform;
|
|
27
|
-
});
|
|
28
|
-
const projectGraph = buildProjectGraph([{ path: inputPath, content }]);
|
|
29
|
-
for (const transform of transforms) {
|
|
30
|
-
await transform.transform({
|
|
31
|
-
projectGraph,
|
|
32
|
-
currentFile: undefined,
|
|
33
|
-
options: {},
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
const fileInfo = projectGraph.files.get(inputPath);
|
|
37
|
-
if (!fileInfo) {
|
|
38
|
-
return content;
|
|
39
|
-
}
|
|
40
|
-
return generate(fileInfo.ast).code;
|
|
41
|
-
};
|
|
6
|
+
import { printSnapshotDiff } from "./print-snapshot-diff.js";
|
|
7
|
+
import { resolveTestcaseInput } from "./resolve-testcase-input.js";
|
|
8
|
+
import { runTestcaseTransform } from "./run-testcase-transform.js";
|
|
42
9
|
export const runSnapshotCli = async (argv) => {
|
|
43
10
|
const program = createSnapshotCommand({ version: packageJson.version });
|
|
44
11
|
program.parse(argv.filter((argument) => argument !== "--"));
|
|
45
12
|
const rawOptions = program.opts();
|
|
46
13
|
const { testcase, transform, expected } = rawOptions;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
console.error(`Error: ${testcase} is not a directory`);
|
|
14
|
+
const resolved = await resolveTestcaseInput({ testcase });
|
|
15
|
+
if (!resolved.ok) {
|
|
16
|
+
const message = resolved.error.message;
|
|
17
|
+
if (message === `${testcase} is not a directory`) {
|
|
18
|
+
console.error(`Error: ${message}`);
|
|
53
19
|
return 1;
|
|
54
20
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
catch {
|
|
67
|
-
console.error(`Error: base.js not found in test-cases/${testcase}/`);
|
|
68
|
-
console.error(`Create test-cases/${testcase}/base.js with your minified snippet first.`);
|
|
21
|
+
if (message.startsWith("Test case directory not found:")) {
|
|
22
|
+
console.error(`Error: ${message}`);
|
|
23
|
+
console.error(`Create it first: mkdir -p test-cases/${testcase}`);
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
if (message.startsWith("base.js not found in test-cases/")) {
|
|
27
|
+
console.error(`Error: ${message}`);
|
|
28
|
+
console.error(`Create test-cases/${testcase}/base.js with your minified snippet first.`);
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
console.error(`Error: ${message}`);
|
|
69
32
|
return 1;
|
|
70
33
|
}
|
|
71
|
-
const
|
|
72
|
-
const
|
|
34
|
+
const { testcaseDirectory, basePath } = resolved.value;
|
|
35
|
+
const expectedPath = path.join(testcaseDirectory, `${transform}-expected.js`);
|
|
36
|
+
const actualPath = path.join(testcaseDirectory, `${transform}.js`);
|
|
73
37
|
if (expected) {
|
|
74
38
|
// Expected-file workflow
|
|
75
39
|
// Step 1: Check if expected file exists, create from base.js if not
|
|
@@ -83,69 +47,53 @@ export const runSnapshotCli = async (argv) => {
|
|
|
83
47
|
await fs.writeFile(expectedPath, formattedBase);
|
|
84
48
|
console.log(`Created: test-cases/${testcase}/${transform}-expected.js`);
|
|
85
49
|
console.log("Edit this file to match your expected output, then re-run.");
|
|
50
|
+
// Design phase: allow creating the expected file before the transform exists.
|
|
51
|
+
// The next run (after implementing the transform) will write the actual output
|
|
52
|
+
// and compare it against this expected snapshot.
|
|
86
53
|
return 0;
|
|
87
54
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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}`);
|
|
131
|
-
}
|
|
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}`);
|
|
140
|
-
}
|
|
55
|
+
const outputResult = await runTestcaseTransform({
|
|
56
|
+
inputPath: basePath,
|
|
57
|
+
transformId: transform,
|
|
58
|
+
});
|
|
59
|
+
if (!outputResult.ok) {
|
|
60
|
+
console.error(`Error running transform: ${outputResult.error.message}`);
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
const actualOutput = outputResult.value;
|
|
64
|
+
// Step 2: Write actual output
|
|
65
|
+
await fs.writeFile(actualPath, actualOutput);
|
|
66
|
+
console.log(`Created: test-cases/${testcase}/${transform}.js`);
|
|
67
|
+
// Step 3: Compare expected vs actual
|
|
68
|
+
const expectedContent = await fs.readFile(expectedPath, "utf8");
|
|
69
|
+
if (expectedContent === actualOutput) {
|
|
70
|
+
// Success! Delete expected file
|
|
71
|
+
await fs.unlink(expectedPath);
|
|
72
|
+
console.log(`\nSuccess: Output matches expected.`);
|
|
73
|
+
console.log(`Deleted: test-cases/${testcase}/${transform}-expected.js`);
|
|
74
|
+
console.log(`\nSnapshot ready: test-cases/${testcase}/${transform}.js`);
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
printSnapshotDiff({ expectedContent, actualOutput });
|
|
79
|
+
console.log(`\nExpected file preserved: test-cases/${testcase}/${transform}-expected.js`);
|
|
80
|
+
console.log("Fix the implementation or update the expected file, then re-run.");
|
|
81
|
+
return 1;
|
|
141
82
|
}
|
|
142
83
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
84
|
+
else {
|
|
85
|
+
const outputResult = await runTestcaseTransform({
|
|
86
|
+
inputPath: basePath,
|
|
87
|
+
transformId: transform,
|
|
88
|
+
});
|
|
89
|
+
if (!outputResult.ok) {
|
|
90
|
+
console.error(`Error running transform: ${outputResult.error.message}`);
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
93
|
+
const actualOutput = outputResult.value;
|
|
94
|
+
// Simple mode: just write actual output
|
|
95
|
+
await fs.writeFile(actualPath, actualOutput);
|
|
96
|
+
console.log(`Created: test-cases/${testcase}/${transform}.js`);
|
|
97
|
+
return 0;
|
|
146
98
|
}
|
|
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;
|
|
151
99
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type Result<T> = {
|
|
2
|
+
ok: true;
|
|
3
|
+
value: T;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
error: Error;
|
|
7
|
+
};
|
|
8
|
+
type RunTestcaseTransformOptions = {
|
|
9
|
+
inputPath: string;
|
|
10
|
+
transformId: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const runTestcaseTransform: (options: RunTestcaseTransformOptions) => Promise<Result<string>>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import { buildProjectGraph } from "../../core/project-graph.js";
|
|
4
|
+
import { transformPresets } from "../../transforms/transform-presets.js";
|
|
5
|
+
import { transformRegistry } from "../../transforms/transform-registry.js";
|
|
6
|
+
import { formatCode } from "./format-code.js";
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
9
|
+
const generate = require("@babel/generator").default;
|
|
10
|
+
const runTransform = async (inputPath, transformId) => {
|
|
11
|
+
const content = await fs.readFile(inputPath, "utf8");
|
|
12
|
+
const transformIds = transformId === "recommended"
|
|
13
|
+
? transformPresets.recommended
|
|
14
|
+
: [transformId];
|
|
15
|
+
const transforms = transformIds.map((id) => {
|
|
16
|
+
const transform = transformRegistry[id];
|
|
17
|
+
if (!transform) {
|
|
18
|
+
throw new Error(`Unknown transform: ${id}. Run 'miniread --list-transforms' to see available transforms.`);
|
|
19
|
+
}
|
|
20
|
+
return transform;
|
|
21
|
+
});
|
|
22
|
+
const projectGraph = buildProjectGraph([{ path: inputPath, content }]);
|
|
23
|
+
for (const transform of transforms) {
|
|
24
|
+
await transform.transform({
|
|
25
|
+
projectGraph,
|
|
26
|
+
currentFile: undefined,
|
|
27
|
+
options: {},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const fileInfo = projectGraph.files.get(inputPath);
|
|
31
|
+
if (!fileInfo)
|
|
32
|
+
return content;
|
|
33
|
+
return generate(fileInfo.ast).code;
|
|
34
|
+
};
|
|
35
|
+
export const runTestcaseTransform = async (options) => {
|
|
36
|
+
const { inputPath, transformId } = options;
|
|
37
|
+
try {
|
|
38
|
+
const rawOutput = await runTransform(inputPath, transformId);
|
|
39
|
+
const output = await formatCode(rawOutput);
|
|
40
|
+
return { ok: true, value: output };
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
+
return { ok: false, error: new Error(message) };
|
|
45
|
+
}
|
|
46
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { getFilesToProcess } from "../../core/types.js";
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
5
|
+
const traverse = require("@babel/traverse").default;
|
|
6
|
+
const t = require("@babel/types");
|
|
7
|
+
const isInBooleanTestContext = (path) => {
|
|
8
|
+
let current = path;
|
|
9
|
+
// A `!!x` node is safe to rewrite as `x` only when its value is consumed
|
|
10
|
+
// purely for truthiness (control-flow tests and boolean operator chains).
|
|
11
|
+
// If we encounter any other parent expression before reaching the test
|
|
12
|
+
// boundary, the expression value is being used (e.g. as a call argument),
|
|
13
|
+
// and rewriting would be unsafe.
|
|
14
|
+
for (;;) {
|
|
15
|
+
const parent = current.parentPath;
|
|
16
|
+
if (!parent)
|
|
17
|
+
return false;
|
|
18
|
+
if ((parent.isIfStatement() ||
|
|
19
|
+
parent.isWhileStatement() ||
|
|
20
|
+
parent.isDoWhileStatement() ||
|
|
21
|
+
parent.isForStatement() ||
|
|
22
|
+
parent.isConditionalExpression()) &&
|
|
23
|
+
current.key === "test") {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (parent.isLogicalExpression()) {
|
|
27
|
+
if (current.key !== "left" && current.key !== "right")
|
|
28
|
+
return false;
|
|
29
|
+
current = parent;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (parent.isUnaryExpression({ operator: "!" })) {
|
|
33
|
+
if (current.key !== "argument")
|
|
34
|
+
return false;
|
|
35
|
+
current = parent;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (parent.isSequenceExpression()) {
|
|
39
|
+
if (current.listKey !== "expressions")
|
|
40
|
+
return false;
|
|
41
|
+
const expressions = parent.node.expressions;
|
|
42
|
+
const lastExpression = expressions.at(-1);
|
|
43
|
+
if (!lastExpression)
|
|
44
|
+
return false;
|
|
45
|
+
if (lastExpression !== current.node)
|
|
46
|
+
return false;
|
|
47
|
+
current = parent;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (parent.isParenthesizedExpression()) {
|
|
51
|
+
if (current.key !== "expression")
|
|
52
|
+
return false;
|
|
53
|
+
current = parent;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
export const simplifyBooleanNegationsTransform = {
|
|
60
|
+
id: "simplify-boolean-negations",
|
|
61
|
+
description: "Simplifies redundant boolean negations: !true/!false and !!x in control-flow tests",
|
|
62
|
+
scope: "file",
|
|
63
|
+
parallelizable: true,
|
|
64
|
+
transform(context) {
|
|
65
|
+
let nodesVisited = 0;
|
|
66
|
+
let transformationsApplied = 0;
|
|
67
|
+
for (const fileInfo of getFilesToProcess(context)) {
|
|
68
|
+
traverse(fileInfo.ast, {
|
|
69
|
+
UnaryExpression(path) {
|
|
70
|
+
nodesVisited++;
|
|
71
|
+
if (path.node.operator !== "!")
|
|
72
|
+
return;
|
|
73
|
+
const argument = path.node.argument;
|
|
74
|
+
if (argument.type === "BooleanLiteral") {
|
|
75
|
+
path.replaceWith(t.booleanLiteral(!argument.value));
|
|
76
|
+
transformationsApplied++;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (argument.type === "UnaryExpression" &&
|
|
80
|
+
argument.operator === "!" &&
|
|
81
|
+
isInBooleanTestContext(path)) {
|
|
82
|
+
path.replaceWith(argument.argument);
|
|
83
|
+
transformationsApplied++;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return Promise.resolve({ nodesVisited, transformationsApplied });
|
|
89
|
+
},
|
|
90
|
+
};
|
|
@@ -21,6 +21,7 @@ import { renameThisAliasesTransform } from "./rename-this-aliases/rename-this-al
|
|
|
21
21
|
import { renameTimeoutIdsTransform } from "./rename-timeout-ids/rename-timeout-ids-transform.js";
|
|
22
22
|
import { renameUseReferenceGuardsTransform } from "./rename-use-reference-guards/rename-use-reference-guards-transform.js";
|
|
23
23
|
import { renameUseReferenceGuardsV2Transform } from "./rename-use-reference-guards-v2/rename-use-reference-guards-v2-transform.js";
|
|
24
|
+
import { simplifyBooleanNegationsTransform } from "./simplify-boolean-negations/simplify-boolean-negations-transform.js";
|
|
24
25
|
import { splitVariableDeclarationsTransform } from "./split-variable-declarations/split-variable-declarations-transform.js";
|
|
25
26
|
export const transformRegistry = {
|
|
26
27
|
[expandBooleanLiteralsTransform.id]: expandBooleanLiteralsTransform,
|
|
@@ -46,6 +47,7 @@ export const transformRegistry = {
|
|
|
46
47
|
[renameTimeoutIdsTransform.id]: renameTimeoutIdsTransform,
|
|
47
48
|
[renameUseReferenceGuardsTransform.id]: renameUseReferenceGuardsTransform,
|
|
48
49
|
[renameUseReferenceGuardsV2Transform.id]: renameUseReferenceGuardsV2Transform,
|
|
50
|
+
[simplifyBooleanNegationsTransform.id]: simplifyBooleanNegationsTransform,
|
|
49
51
|
[splitVariableDeclarationsTransform.id]: splitVariableDeclarationsTransform,
|
|
50
52
|
};
|
|
51
53
|
export const allTransformIds = Object.keys(transformRegistry);
|
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.18.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
|
@@ -10,6 +10,15 @@
|
|
|
10
10
|
"evaluatedAt": "2026-01-21T15:01:23.708Z",
|
|
11
11
|
"notes": "Improves readability but does not reduce diffs (boolean literals are already deterministic)"
|
|
12
12
|
},
|
|
13
|
+
{
|
|
14
|
+
"id": "simplify-boolean-negations",
|
|
15
|
+
"description": "Simplifies redundant boolean negations: !true/!false and !!x in control-flow tests",
|
|
16
|
+
"scope": "file",
|
|
17
|
+
"parallelizable": true,
|
|
18
|
+
"diffReductionImpact": 0,
|
|
19
|
+
"recommended": true,
|
|
20
|
+
"notes": "Added manually; improves readability and cleans up `expand-boolean-literals` output."
|
|
21
|
+
},
|
|
13
22
|
{
|
|
14
23
|
"id": "expand-undefined-literals",
|
|
15
24
|
"description": "Expands void 0 to undefined (when undefined is not shadowed)",
|