miniread 1.5.0 → 1.7.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/core/normalize-code.d.ts +17 -0
- package/dist/core/normalize-code.js +35 -0
- package/dist/core/stable-naming.d.ts +61 -0
- package/dist/core/stable-naming.js +121 -0
- package/dist/scripts/evaluate/check-expected-evaluations.js +2 -0
- package/dist/scripts/evaluate/check-recommended-snapshot.d.ts +11 -0
- package/dist/scripts/evaluate/check-recommended-snapshot.js +53 -0
- package/dist/scripts/evaluate/check-snapshots.d.ts +1 -0
- package/dist/scripts/evaluate/check-snapshots.js +21 -2
- package/dist/scripts/evaluate/transform-content.d.ts +5 -0
- package/dist/scripts/evaluate/transform-content.js +23 -13
- package/dist/transforms/rename-catch-parameters/rename-catch-parameters-transform.js +15 -38
- package/dist/transforms/rename-destructured-aliases/rename-destructured-aliases-transform.js +13 -12
- package/dist/transforms/rename-event-parameters/process-event-handler-function.d.ts +2 -3
- package/dist/transforms/rename-event-parameters/process-event-handler-function.js +13 -9
- package/dist/transforms/rename-event-parameters/rename-event-parameters-transform.d.ts +1 -1
- package/dist/transforms/rename-event-parameters/rename-event-parameters-transform.js +10 -7
- package/dist/transforms/rename-loop-index-variables/rename-loop-index-variables-transform.js +19 -13
- package/dist/transforms/rename-promise-executor-parameters/rename-promise-executor-parameters-transform.js +12 -5
- package/dist/transforms/rename-timeout-ids/rename-timeout-ids-transform.d.ts +1 -1
- package/dist/transforms/rename-timeout-ids/rename-timeout-ids-transform.js +10 -11
- package/dist/transforms/rename-use-reference-guards/rename-use-reference-guards-transform.js +10 -11
- package/dist/transforms/rename-use-reference-guards-v2/get-member-expression-for-reference.d.ts +3 -0
- package/dist/transforms/rename-use-reference-guards-v2/get-member-expression-for-reference.js +10 -0
- package/dist/transforms/rename-use-reference-guards-v2/get-reference-usage.d.ts +8 -0
- package/dist/transforms/rename-use-reference-guards-v2/get-reference-usage.js +58 -0
- package/dist/transforms/rename-use-reference-guards-v2/is-use-reference-false-initializer.d.ts +2 -0
- package/dist/transforms/rename-use-reference-guards-v2/is-use-reference-false-initializer.js +40 -0
- package/dist/transforms/rename-use-reference-guards-v2/rename-use-reference-guards-v2-transform.d.ts +3 -0
- package/dist/transforms/rename-use-reference-guards-v2/rename-use-reference-guards-v2-transform.js +54 -0
- package/dist/transforms/transform-registry.js +2 -10
- package/package.json +1 -1
- package/transform-manifest.json +15 -55
- package/dist/transforms/expand-return-sequence/expand-return-sequence-transform.d.ts +0 -2
- package/dist/transforms/expand-return-sequence/expand-return-sequence-transform.js +0 -91
- package/dist/transforms/expand-sequence-expressions/expand-expression-statement-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions/expand-expression-statement-sequence.js +0 -57
- package/dist/transforms/expand-sequence-expressions/expand-sequence-expressions-transform.d.ts +0 -2
- package/dist/transforms/expand-sequence-expressions/expand-sequence-expressions-transform.js +0 -34
- package/dist/transforms/expand-sequence-expressions/expand-variable-declaration-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions/expand-variable-declaration-sequence.js +0 -93
- package/dist/transforms/expand-sequence-expressions-v2/expand-expression-statement-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions-v2/expand-expression-statement-sequence.js +0 -55
- package/dist/transforms/expand-sequence-expressions-v2/expand-return-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions-v2/expand-return-sequence.js +0 -86
- package/dist/transforms/expand-sequence-expressions-v2/expand-sequence-expressions-v2-transform.d.ts +0 -2
- package/dist/transforms/expand-sequence-expressions-v2/expand-sequence-expressions-v2-transform.js +0 -41
- package/dist/transforms/expand-sequence-expressions-v2/expand-variable-declaration-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions-v2/expand-variable-declaration-sequence.js +0 -93
- package/dist/transforms/expand-sequence-expressions-v3/expand-expression-statement-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions-v3/expand-expression-statement-sequence.js +0 -64
- package/dist/transforms/expand-sequence-expressions-v3/expand-return-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions-v3/expand-return-sequence.js +0 -91
- package/dist/transforms/expand-sequence-expressions-v3/expand-sequence-expressions-v3-transform.d.ts +0 -2
- package/dist/transforms/expand-sequence-expressions-v3/expand-sequence-expressions-v3-transform.js +0 -48
- package/dist/transforms/expand-sequence-expressions-v3/expand-throw-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions-v3/expand-throw-sequence.js +0 -101
- package/dist/transforms/expand-sequence-expressions-v3/expand-variable-declaration-sequence.d.ts +0 -3
- package/dist/transforms/expand-sequence-expressions-v3/expand-variable-declaration-sequence.js +0 -99
- package/dist/transforms/expand-throw-sequence/expand-throw-sequence-transform.d.ts +0 -2
- package/dist/transforms/expand-throw-sequence/expand-throw-sequence-transform.js +0 -117
- package/dist/transforms/rename-binding/get-target-name.d.ts +0 -4
- package/dist/transforms/rename-binding/get-target-name.js +0 -25
- package/dist/transforms/rename-binding/is-valid-binding-identifier.d.ts +0 -1
- package/dist/transforms/rename-binding/is-valid-binding-identifier.js +0 -10
- package/dist/transforms/rename-use-reference-guards/get-target-name.d.ts +0 -2
- package/dist/transforms/rename-use-reference-guards/get-target-name.js +0 -23
- package/dist/transforms/rename-use-reference-guards/is-valid-binding-identifier.d.ts +0 -1
- package/dist/transforms/rename-use-reference-guards/is-valid-binding-identifier.js +0 -10
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes JavaScript/TypeScript code through the parse → generate → format pipeline.
|
|
3
|
+
*
|
|
4
|
+
* This ensures consistent whitespace and formatting regardless of the original source.
|
|
5
|
+
* Use this when comparing code that may have gone through different pipelines
|
|
6
|
+
* (e.g., comparing base.js to transform output).
|
|
7
|
+
*
|
|
8
|
+
* The pipeline:
|
|
9
|
+
* 1. Parse with Babel (normalizes AST structure)
|
|
10
|
+
* 2. Generate with Babel (produces consistent whitespace)
|
|
11
|
+
* 3. Format with Prettier (final formatting)
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Normalizes code through parse → generate → format pipeline.
|
|
15
|
+
* Returns the normalized code string.
|
|
16
|
+
*/
|
|
17
|
+
export declare const normalizeCode: (code: string) => Promise<string>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes JavaScript/TypeScript code through the parse → generate → format pipeline.
|
|
3
|
+
*
|
|
4
|
+
* This ensures consistent whitespace and formatting regardless of the original source.
|
|
5
|
+
* Use this when comparing code that may have gone through different pipelines
|
|
6
|
+
* (e.g., comparing base.js to transform output).
|
|
7
|
+
*
|
|
8
|
+
* The pipeline:
|
|
9
|
+
* 1. Parse with Babel (normalizes AST structure)
|
|
10
|
+
* 2. Generate with Babel (produces consistent whitespace)
|
|
11
|
+
* 3. Format with Prettier (final formatting)
|
|
12
|
+
*/
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
import * as prettier from "prettier";
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
17
|
+
const parser = require("@babel/parser");
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
19
|
+
const parse = parser.parse;
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
21
|
+
const generate = require("@babel/generator").default;
|
|
22
|
+
const PARSER_OPTIONS = {
|
|
23
|
+
sourceType: "unambiguous",
|
|
24
|
+
plugins: ["jsx", "typescript", "importAttributes"],
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Normalizes code through parse → generate → format pipeline.
|
|
28
|
+
* Returns the normalized code string.
|
|
29
|
+
*/
|
|
30
|
+
export const normalizeCode = async (code) => {
|
|
31
|
+
const ast = parse(code, PARSER_OPTIONS);
|
|
32
|
+
const generated = generate(ast);
|
|
33
|
+
const formatted = await prettier.format(generated.code, { parser: "babel" });
|
|
34
|
+
return formatted;
|
|
35
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for the stable name prefix convention (§5.6-5.9 in workflow.md).
|
|
3
|
+
*
|
|
4
|
+
* The `$` prefix signals that a variable has been renamed stably by a transform
|
|
5
|
+
* and should be skipped by other transforms.
|
|
6
|
+
*
|
|
7
|
+
* - Stable names (`$timeoutId`): Readable AND deterministic across versions
|
|
8
|
+
* - Readable names (`timeoutId`): Semantic but order-dependent
|
|
9
|
+
*/
|
|
10
|
+
import type { Scope } from "@babel/traverse";
|
|
11
|
+
/**
|
|
12
|
+
* Returns true if the name has been stable-renamed (starts with $).
|
|
13
|
+
* Transforms should skip variables with this prefix.
|
|
14
|
+
*/
|
|
15
|
+
export declare const isStableRenamed: (name: string) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Entry for a planned rename operation.
|
|
18
|
+
*/
|
|
19
|
+
type RenameEntry = {
|
|
20
|
+
/** The Babel scope containing the binding */
|
|
21
|
+
scope: Scope;
|
|
22
|
+
/** Current name of the variable */
|
|
23
|
+
currentName: string;
|
|
24
|
+
/** Desired semantic base name (e.g., "timeoutId", "error") */
|
|
25
|
+
baseName: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Collects rename operations and applies them with automatic stability logic.
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const group = new RenameGroup();
|
|
33
|
+
*
|
|
34
|
+
* // Collect renames during traversal
|
|
35
|
+
* group.add({ scope: path.scope, currentName: "a", baseName: "timeoutId" });
|
|
36
|
+
* group.add({ scope: path.scope, currentName: "b", baseName: "timeoutId" });
|
|
37
|
+
*
|
|
38
|
+
* // Apply with automatic stability
|
|
39
|
+
* const count = group.apply();
|
|
40
|
+
* // Result: a → timeoutId, b → timeoutId2 (readable, 2+ unstable)
|
|
41
|
+
* // Or if only one: a → $timeoutId (stable, exactly 1 unstable)
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* Stability rules (per scope, per baseName):
|
|
45
|
+
* - If exactly 1 unstable variable wants this name → stable (`$baseName`)
|
|
46
|
+
* - If 2+ unstable variables want the same name → readable only (`baseName`, `baseName2`, ...)
|
|
47
|
+
*/
|
|
48
|
+
export declare class RenameGroup {
|
|
49
|
+
private entries;
|
|
50
|
+
/**
|
|
51
|
+
* Add a variable to be renamed.
|
|
52
|
+
* Already-stable names (starting with $) are automatically skipped.
|
|
53
|
+
*/
|
|
54
|
+
add(entry: RenameEntry): void;
|
|
55
|
+
/**
|
|
56
|
+
* Apply all collected renames with automatic stability logic.
|
|
57
|
+
* Returns the number of transformations applied.
|
|
58
|
+
*/
|
|
59
|
+
apply(): number;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for the stable name prefix convention (§5.6-5.9 in workflow.md).
|
|
3
|
+
*
|
|
4
|
+
* The `$` prefix signals that a variable has been renamed stably by a transform
|
|
5
|
+
* and should be skipped by other transforms.
|
|
6
|
+
*
|
|
7
|
+
* - Stable names (`$timeoutId`): Readable AND deterministic across versions
|
|
8
|
+
* - Readable names (`timeoutId`): Semantic but order-dependent
|
|
9
|
+
*/
|
|
10
|
+
const STABLE_PREFIX = "$";
|
|
11
|
+
/**
|
|
12
|
+
* Returns true if the name has been stable-renamed (starts with $).
|
|
13
|
+
* Transforms should skip variables with this prefix.
|
|
14
|
+
*/
|
|
15
|
+
export const isStableRenamed = (name) => {
|
|
16
|
+
return name.startsWith(STABLE_PREFIX);
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Creates a stable name by adding the $ prefix.
|
|
20
|
+
* Internal helper - prefer using RenameGroup which handles stability logic automatically.
|
|
21
|
+
*/
|
|
22
|
+
const makeStableName = (baseName) => {
|
|
23
|
+
return `${STABLE_PREFIX}${baseName}`;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Finds the next available name slot in a scope.
|
|
27
|
+
*/
|
|
28
|
+
const findAvailableName = (scope, baseName, startIndex, canBeStable) => {
|
|
29
|
+
let index = startIndex;
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
31
|
+
while (true) {
|
|
32
|
+
const suffix = index === 1 ? "" : String(index);
|
|
33
|
+
const candidateBase = `${baseName}${suffix}`;
|
|
34
|
+
const candidateStable = makeStableName(candidateBase);
|
|
35
|
+
const candidateReadable = candidateBase;
|
|
36
|
+
// Check if either form is already taken in this scope
|
|
37
|
+
const stableTaken = scope.hasBinding(candidateStable);
|
|
38
|
+
const readableTaken = scope.hasBinding(candidateReadable);
|
|
39
|
+
if (!stableTaken && !readableTaken) {
|
|
40
|
+
// This slot is available
|
|
41
|
+
const name = canBeStable ? candidateStable : candidateReadable;
|
|
42
|
+
return { name, nextIndex: index + 1 };
|
|
43
|
+
}
|
|
44
|
+
index++;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Collects rename operations and applies them with automatic stability logic.
|
|
49
|
+
*
|
|
50
|
+
* Usage:
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const group = new RenameGroup();
|
|
53
|
+
*
|
|
54
|
+
* // Collect renames during traversal
|
|
55
|
+
* group.add({ scope: path.scope, currentName: "a", baseName: "timeoutId" });
|
|
56
|
+
* group.add({ scope: path.scope, currentName: "b", baseName: "timeoutId" });
|
|
57
|
+
*
|
|
58
|
+
* // Apply with automatic stability
|
|
59
|
+
* const count = group.apply();
|
|
60
|
+
* // Result: a → timeoutId, b → timeoutId2 (readable, 2+ unstable)
|
|
61
|
+
* // Or if only one: a → $timeoutId (stable, exactly 1 unstable)
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* Stability rules (per scope, per baseName):
|
|
65
|
+
* - If exactly 1 unstable variable wants this name → stable (`$baseName`)
|
|
66
|
+
* - If 2+ unstable variables want the same name → readable only (`baseName`, `baseName2`, ...)
|
|
67
|
+
*/
|
|
68
|
+
export class RenameGroup {
|
|
69
|
+
entries = [];
|
|
70
|
+
/**
|
|
71
|
+
* Add a variable to be renamed.
|
|
72
|
+
* Already-stable names (starting with $) are automatically skipped.
|
|
73
|
+
*/
|
|
74
|
+
add(entry) {
|
|
75
|
+
if (isStableRenamed(entry.currentName))
|
|
76
|
+
return;
|
|
77
|
+
this.entries.push(entry);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Apply all collected renames with automatic stability logic.
|
|
81
|
+
* Returns the number of transformations applied.
|
|
82
|
+
*/
|
|
83
|
+
apply() {
|
|
84
|
+
// Group entries by (scope, baseName) using a two-level Map
|
|
85
|
+
const scopeGroups = new Map();
|
|
86
|
+
for (const entry of this.entries) {
|
|
87
|
+
let baseNameMap = scopeGroups.get(entry.scope);
|
|
88
|
+
if (!baseNameMap) {
|
|
89
|
+
baseNameMap = new Map();
|
|
90
|
+
scopeGroups.set(entry.scope, baseNameMap);
|
|
91
|
+
}
|
|
92
|
+
let group = baseNameMap.get(entry.baseName);
|
|
93
|
+
if (!group) {
|
|
94
|
+
group = [];
|
|
95
|
+
baseNameMap.set(entry.baseName, group);
|
|
96
|
+
}
|
|
97
|
+
group.push(entry);
|
|
98
|
+
}
|
|
99
|
+
let transformationsApplied = 0;
|
|
100
|
+
// Process each scope
|
|
101
|
+
for (const [scope, baseNameMap] of scopeGroups) {
|
|
102
|
+
// Process each baseName group within this scope
|
|
103
|
+
for (const [baseName, group] of baseNameMap) {
|
|
104
|
+
// Determine if names can be stable (only 1 unstable entry for this baseName)
|
|
105
|
+
const canBeStable = group.length === 1;
|
|
106
|
+
// Track which index to try next
|
|
107
|
+
let index = 1;
|
|
108
|
+
for (const entry of group) {
|
|
109
|
+
// Find next available name slot
|
|
110
|
+
const targetName = findAvailableName(scope, baseName, index, canBeStable);
|
|
111
|
+
index = targetName.nextIndex;
|
|
112
|
+
if (entry.currentName !== targetName.name) {
|
|
113
|
+
scope.rename(entry.currentName, targetName.name);
|
|
114
|
+
transformationsApplied++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return transformationsApplied;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import { allTransformIds } from "../../transforms/transform-registry.js";
|
|
3
3
|
import { transformRegistry } from "../../transforms/transform-registry.js";
|
|
4
|
+
import { transformPresets } from "../../transforms/transform-presets.js";
|
|
4
5
|
import { parseTransformManifest } from "./parse-transform-manifest.js";
|
|
5
6
|
import { checkAllSnapshots } from "./check-snapshots.js";
|
|
6
7
|
export const checkExpectedEvaluations = async (options) => {
|
|
@@ -24,6 +25,7 @@ export const checkExpectedEvaluations = async (options) => {
|
|
|
24
25
|
testCasesDirectory,
|
|
25
26
|
transformIds,
|
|
26
27
|
transformRegistry,
|
|
28
|
+
recommendedTransformIds: transformPresets.recommended,
|
|
27
29
|
});
|
|
28
30
|
const allPresent = missingManifestEntries.length === 0 && snapshotIssues.length === 0;
|
|
29
31
|
return {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Transform } from "../../core/types.js";
|
|
2
|
+
import type { SnapshotIssue } from "./check-snapshots.js";
|
|
3
|
+
type CheckRecommendedSnapshotOptions = {
|
|
4
|
+
basePath: string;
|
|
5
|
+
baseContent: string;
|
|
6
|
+
snapshotPath: string;
|
|
7
|
+
testCaseName: string;
|
|
8
|
+
transforms: Transform[];
|
|
9
|
+
};
|
|
10
|
+
export declare const checkRecommendedSnapshot: (options: CheckRecommendedSnapshotOptions) => Promise<SnapshotIssue | undefined>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as prettier from "prettier";
|
|
3
|
+
import { transformContentWithMultiple } from "./transform-content.js";
|
|
4
|
+
const formatCode = async (code) => {
|
|
5
|
+
return prettier.format(code, { parser: "babel" });
|
|
6
|
+
};
|
|
7
|
+
const fileExists = async (filePath) => {
|
|
8
|
+
try {
|
|
9
|
+
await fs.access(filePath);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export const checkRecommendedSnapshot = async (options) => {
|
|
17
|
+
const { basePath, baseContent, snapshotPath, testCaseName, transforms } = options;
|
|
18
|
+
const snapshotExists = await fileExists(snapshotPath);
|
|
19
|
+
// Run all recommended transforms to get the output
|
|
20
|
+
const result = await transformContentWithMultiple({
|
|
21
|
+
content: baseContent,
|
|
22
|
+
filePath: basePath,
|
|
23
|
+
transforms,
|
|
24
|
+
});
|
|
25
|
+
if (!result.ok) {
|
|
26
|
+
// Transform failed, skip this check
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const formattedOutput = await formatCode(result.output);
|
|
30
|
+
if (snapshotExists) {
|
|
31
|
+
const snapshotContent = await fs.readFile(snapshotPath, "utf8");
|
|
32
|
+
const formattedSnapshot = await formatCode(snapshotContent);
|
|
33
|
+
// Check if snapshot is outdated (recommended snapshots are never "unnecessary")
|
|
34
|
+
if (formattedOutput !== formattedSnapshot) {
|
|
35
|
+
return {
|
|
36
|
+
transformId: "recommended",
|
|
37
|
+
testCase: testCaseName,
|
|
38
|
+
snapshotPath,
|
|
39
|
+
issue: "outdated",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Snapshot doesn't exist - it's always required for recommended
|
|
45
|
+
return {
|
|
46
|
+
transformId: "recommended",
|
|
47
|
+
testCase: testCaseName,
|
|
48
|
+
snapshotPath,
|
|
49
|
+
issue: "missing-required",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
@@ -9,6 +9,7 @@ type CheckAllSnapshotsOptions = {
|
|
|
9
9
|
testCasesDirectory: string;
|
|
10
10
|
transformIds: string[];
|
|
11
11
|
transformRegistry: Record<string, Transform>;
|
|
12
|
+
recommendedTransformIds: string[];
|
|
12
13
|
};
|
|
13
14
|
export declare const checkAllSnapshots: (options: CheckAllSnapshotsOptions) => Promise<SnapshotIssue[]>;
|
|
14
15
|
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import * as prettier from "prettier";
|
|
4
|
+
import { normalizeCode } from "../../core/normalize-code.js";
|
|
4
5
|
import { transformContent } from "./transform-content.js";
|
|
6
|
+
import { checkRecommendedSnapshot } from "./check-recommended-snapshot.js";
|
|
5
7
|
const formatCode = async (code) => {
|
|
6
8
|
return prettier.format(code, { parser: "babel" });
|
|
7
9
|
};
|
|
@@ -84,13 +86,18 @@ const getTestCaseNames = async (testCasesDirectory) => {
|
|
|
84
86
|
return testCaseNames.toSorted();
|
|
85
87
|
};
|
|
86
88
|
export const checkAllSnapshots = async (options) => {
|
|
87
|
-
const { testCasesDirectory, transformIds, transformRegistry } = options;
|
|
89
|
+
const { testCasesDirectory, transformIds, transformRegistry, recommendedTransformIds, } = options;
|
|
88
90
|
const snapshotIssues = [];
|
|
89
91
|
const testCaseNames = await getTestCaseNames(testCasesDirectory);
|
|
92
|
+
// Get the recommended transforms in order
|
|
93
|
+
const recommendedTransforms = recommendedTransformIds
|
|
94
|
+
.map((id) => transformRegistry[id])
|
|
95
|
+
.filter((t) => t !== undefined);
|
|
90
96
|
for (const testCaseName of testCaseNames) {
|
|
91
97
|
const basePath = path.join(testCasesDirectory, testCaseName, "base.js");
|
|
92
98
|
const baseContent = await fs.readFile(basePath, "utf8");
|
|
93
|
-
const formattedBase = await
|
|
99
|
+
const formattedBase = await normalizeCode(baseContent);
|
|
100
|
+
// Check individual transform snapshots
|
|
94
101
|
for (const transformId of transformIds) {
|
|
95
102
|
const snapshotPath = path.join(testCasesDirectory, testCaseName, `${transformId}.js`);
|
|
96
103
|
const issue = await checkSnapshot({
|
|
@@ -106,6 +113,18 @@ export const checkAllSnapshots = async (options) => {
|
|
|
106
113
|
snapshotIssues.push(issue);
|
|
107
114
|
}
|
|
108
115
|
}
|
|
116
|
+
// Check recommended snapshot (always required)
|
|
117
|
+
const recommendedSnapshotPath = path.join(testCasesDirectory, testCaseName, "recommended.js");
|
|
118
|
+
const recommendedIssue = await checkRecommendedSnapshot({
|
|
119
|
+
basePath,
|
|
120
|
+
baseContent,
|
|
121
|
+
snapshotPath: recommendedSnapshotPath,
|
|
122
|
+
testCaseName,
|
|
123
|
+
transforms: recommendedTransforms,
|
|
124
|
+
});
|
|
125
|
+
if (recommendedIssue) {
|
|
126
|
+
snapshotIssues.push(recommendedIssue);
|
|
127
|
+
}
|
|
109
128
|
}
|
|
110
129
|
return snapshotIssues;
|
|
111
130
|
};
|
|
@@ -11,4 +11,9 @@ export declare const transformContent: (options: {
|
|
|
11
11
|
filePath: string;
|
|
12
12
|
transform: Transform;
|
|
13
13
|
}) => Promise<TransformContentResult>;
|
|
14
|
+
export declare const transformContentWithMultiple: (options: {
|
|
15
|
+
content: string;
|
|
16
|
+
filePath: string;
|
|
17
|
+
transforms: Transform[];
|
|
18
|
+
}) => Promise<TransformContentResult>;
|
|
14
19
|
export {};
|
|
@@ -2,24 +2,34 @@ import { buildProjectGraph } from "../../core/project-graph.js";
|
|
|
2
2
|
import { generateCode } from "../../cli/generate-code.js";
|
|
3
3
|
export const transformContent = async (options) => {
|
|
4
4
|
const { content, filePath, transform } = options;
|
|
5
|
+
return transformContentWithMultiple({
|
|
6
|
+
content,
|
|
7
|
+
filePath,
|
|
8
|
+
transforms: [transform],
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
export const transformContentWithMultiple = async (options) => {
|
|
12
|
+
const { content, filePath, transforms } = options;
|
|
5
13
|
const projectGraph = buildProjectGraph([{ path: filePath, content }]);
|
|
6
14
|
const file = projectGraph.files.get(filePath);
|
|
7
15
|
if (!file) {
|
|
8
16
|
return { ok: false, error: "Failed to parse input" };
|
|
9
17
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
for (const transform of transforms) {
|
|
19
|
+
try {
|
|
20
|
+
await transform.transform({
|
|
21
|
+
projectGraph,
|
|
22
|
+
currentFile: undefined,
|
|
23
|
+
options: {},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
error: `Transform '${transform.id}' failed: ${message}`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
23
33
|
}
|
|
24
34
|
const generated = generateCode(file.ast);
|
|
25
35
|
const output = generated === ""
|
|
@@ -1,63 +1,40 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
|
|
2
3
|
import { getFilesToProcess, } from "../../core/types.js";
|
|
3
4
|
const require = createRequire(import.meta.url);
|
|
4
5
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
5
6
|
const traverse = require("@babel/traverse").default;
|
|
6
7
|
const BASE_NAME = "caughtError";
|
|
7
|
-
const getRenamedAncestorCatchCount = (path, renamedCatches) => {
|
|
8
|
-
let count = 0;
|
|
9
|
-
for (const ancestor of path.getAncestry()) {
|
|
10
|
-
if (ancestor === path)
|
|
11
|
-
continue;
|
|
12
|
-
if (!ancestor.isCatchClause())
|
|
13
|
-
continue;
|
|
14
|
-
if (renamedCatches.has(ancestor.node))
|
|
15
|
-
count++;
|
|
16
|
-
}
|
|
17
|
-
return count;
|
|
18
|
-
};
|
|
19
|
-
const getTargetCaughtErrorName = (renamedAncestorCatchCount) => {
|
|
20
|
-
if (renamedAncestorCatchCount === 0)
|
|
21
|
-
return BASE_NAME;
|
|
22
|
-
return `${BASE_NAME}${renamedAncestorCatchCount + 1}`;
|
|
23
|
-
};
|
|
24
|
-
const getCatchParameterNameIfEligible = (path) => {
|
|
25
|
-
const parameter = path.node.param;
|
|
26
|
-
if (!parameter)
|
|
27
|
-
return;
|
|
28
|
-
if (parameter.type !== "Identifier")
|
|
29
|
-
return;
|
|
30
|
-
if (parameter.name.length > 2)
|
|
31
|
-
return;
|
|
32
|
-
return parameter.name;
|
|
33
|
-
};
|
|
34
8
|
export const renameCatchParametersTransform = {
|
|
35
9
|
id: "rename-catch-parameters",
|
|
36
|
-
description: "Renames
|
|
10
|
+
description: "Renames catch clause parameters to $caughtError/$caughtError2/...",
|
|
37
11
|
scope: "file",
|
|
38
12
|
parallelizable: true,
|
|
39
13
|
transform(context) {
|
|
40
14
|
let nodesVisited = 0;
|
|
41
15
|
let transformationsApplied = 0;
|
|
42
16
|
for (const fileInfo of getFilesToProcess(context)) {
|
|
43
|
-
const
|
|
17
|
+
const group = new RenameGroup();
|
|
44
18
|
traverse(fileInfo.ast, {
|
|
45
19
|
CatchClause(path) {
|
|
46
20
|
nodesVisited++;
|
|
47
|
-
const
|
|
48
|
-
if (!
|
|
21
|
+
const parameter = path.node.param;
|
|
22
|
+
if (!parameter)
|
|
49
23
|
return;
|
|
50
|
-
|
|
51
|
-
const targetName = getTargetCaughtErrorName(renamedAncestorCatchCount);
|
|
52
|
-
if (currentName === targetName)
|
|
24
|
+
if (parameter.type !== "Identifier")
|
|
53
25
|
return;
|
|
54
|
-
|
|
26
|
+
const currentName = parameter.name;
|
|
27
|
+
// Skip already-stable names
|
|
28
|
+
if (isStableRenamed(currentName))
|
|
55
29
|
return;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
30
|
+
group.add({
|
|
31
|
+
scope: path.scope,
|
|
32
|
+
currentName,
|
|
33
|
+
baseName: BASE_NAME,
|
|
34
|
+
});
|
|
59
35
|
},
|
|
60
36
|
});
|
|
37
|
+
transformationsApplied += group.apply();
|
|
61
38
|
}
|
|
62
39
|
return Promise.resolve({
|
|
63
40
|
nodesVisited,
|
package/dist/transforms/rename-destructured-aliases/rename-destructured-aliases-transform.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { isIdentifierName, isKeyword, isStrictBindReservedWord, } from "@babel/helper-validator-identifier";
|
|
3
3
|
import { getFilesToProcess, } from "../../core/types.js";
|
|
4
|
+
import { RenameGroup } from "../../core/stable-naming.js";
|
|
4
5
|
import { isBindingContext } from "./binding-context.js";
|
|
5
6
|
const require = createRequire(import.meta.url);
|
|
6
7
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
@@ -22,7 +23,7 @@ const isValidBindingIdentifier = (name) => {
|
|
|
22
23
|
return false;
|
|
23
24
|
return true;
|
|
24
25
|
};
|
|
25
|
-
const processObjectPattern = (path,
|
|
26
|
+
const processObjectPattern = (path, group) => {
|
|
26
27
|
// Only process in binding contexts, not assignment expressions
|
|
27
28
|
if (!isBindingContext(path))
|
|
28
29
|
return;
|
|
@@ -84,14 +85,12 @@ const processObjectPattern = (path, stats) => {
|
|
|
84
85
|
referencePath.scope.hasBinding(keyName));
|
|
85
86
|
if (wouldBeShadowed)
|
|
86
87
|
continue;
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
aliasIdentifier.name = keyName;
|
|
94
|
-
stats.transformationsApplied++;
|
|
88
|
+
// Add to rename group for batch processing with stability logic
|
|
89
|
+
group.add({
|
|
90
|
+
scope: bindingScope,
|
|
91
|
+
currentName: aliasName,
|
|
92
|
+
baseName: keyName,
|
|
93
|
+
});
|
|
95
94
|
}
|
|
96
95
|
};
|
|
97
96
|
export const renameDestructuredAliasesTransform = {
|
|
@@ -101,18 +100,20 @@ export const renameDestructuredAliasesTransform = {
|
|
|
101
100
|
parallelizable: true,
|
|
102
101
|
transform(context) {
|
|
103
102
|
let nodesVisited = 0;
|
|
104
|
-
|
|
103
|
+
let transformationsApplied = 0;
|
|
105
104
|
for (const fileInfo of getFilesToProcess(context)) {
|
|
105
|
+
const group = new RenameGroup();
|
|
106
106
|
traverse(fileInfo.ast, {
|
|
107
107
|
ObjectPattern(path) {
|
|
108
108
|
nodesVisited++;
|
|
109
|
-
processObjectPattern(path,
|
|
109
|
+
processObjectPattern(path, group);
|
|
110
110
|
},
|
|
111
111
|
});
|
|
112
|
+
transformationsApplied += group.apply();
|
|
112
113
|
}
|
|
113
114
|
return Promise.resolve({
|
|
114
115
|
nodesVisited,
|
|
115
|
-
transformationsApplied
|
|
116
|
+
transformationsApplied,
|
|
116
117
|
});
|
|
117
118
|
},
|
|
118
119
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { NodePath } from "@babel/traverse";
|
|
2
2
|
import type { ArrowFunctionExpression, FunctionDeclaration, FunctionExpression } from "@babel/types";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}) => void;
|
|
3
|
+
import { type RenameGroup } from "../../core/stable-naming.js";
|
|
4
|
+
export declare const processEventHandlerFunction: (path: NodePath<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression>, group: RenameGroup) => void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isStableRenamed } from "../../core/stable-naming.js";
|
|
1
2
|
import { getEventParameterUsage, isHighConfidenceEventParameter, } from "./event-parameter-usage.js";
|
|
2
3
|
import { getTargetEventName } from "./get-target-event-name.js";
|
|
3
4
|
import { canRenameBindingSafely } from "./can-rename-binding-safely.js";
|
|
@@ -23,7 +24,7 @@ const wouldShadowReferencedImplicitGlobal = (functionPath, targetName) => {
|
|
|
23
24
|
});
|
|
24
25
|
return foundReference;
|
|
25
26
|
};
|
|
26
|
-
export const processEventHandlerFunction = (path,
|
|
27
|
+
export const processEventHandlerFunction = (path, group) => {
|
|
27
28
|
const parameters = path.node.params;
|
|
28
29
|
if (parameters.length !== 1)
|
|
29
30
|
return;
|
|
@@ -32,12 +33,12 @@ export const processEventHandlerFunction = (path, stats) => {
|
|
|
32
33
|
return;
|
|
33
34
|
if (parameter.type !== "Identifier")
|
|
34
35
|
return;
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
if (
|
|
36
|
+
const currentName = parameter.name;
|
|
37
|
+
// Skip already-stable names
|
|
38
|
+
if (isStableRenamed(currentName))
|
|
38
39
|
return;
|
|
39
40
|
const bindingScope = path.scope;
|
|
40
|
-
const binding = bindingScope.getBinding(
|
|
41
|
+
const binding = bindingScope.getBinding(currentName);
|
|
41
42
|
if (!binding)
|
|
42
43
|
return;
|
|
43
44
|
if (!binding.constant)
|
|
@@ -46,14 +47,17 @@ export const processEventHandlerFunction = (path, stats) => {
|
|
|
46
47
|
if (!isHighConfidenceEventParameter(usage))
|
|
47
48
|
return;
|
|
48
49
|
const targetName = getTargetEventName(usage);
|
|
49
|
-
if (
|
|
50
|
+
if (currentName === targetName)
|
|
50
51
|
return;
|
|
51
52
|
if (wouldShadowReferencedImplicitGlobal(path, targetName))
|
|
52
53
|
return;
|
|
53
54
|
if (wouldShadowReferencedOuterBinding(path, targetName))
|
|
54
55
|
return;
|
|
55
|
-
if (!canRenameBindingSafely(bindingScope,
|
|
56
|
+
if (!canRenameBindingSafely(bindingScope, currentName, targetName))
|
|
56
57
|
return;
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
group.add({
|
|
59
|
+
scope: bindingScope,
|
|
60
|
+
currentName,
|
|
61
|
+
baseName: targetName,
|
|
62
|
+
});
|
|
59
63
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Transform } from "../../core/types.js";
|
|
2
2
|
export declare const renameEventParametersTransform: Transform;
|