[compiler][snap] More minimization improvements (#35689)
* A few new minimization strategies, removing function params and array/object pattern elements * Ensure that we preserve the same set of errors based on not just category+reason but also description.
Joseph Savona committed
Feb 4, 2026 at 10:09 UTC
90c6d1b218bbe07eeb757ce8777805a9b7f8e3c3
1 file changed
+133
-3
compiler/packages/snap/src/minimize.ts
+133
-3
@@ -18,7 +18,7 @@ type CompileSuccess = {kind: 'success'};
18
type CompileParseError = {kind: 'parse_error'; message: string};
19
type CompileErrors = {
20
kind: 'errors';
21
- errors: Array<{category: string; reason: string}>;
21
+ errors: Array<{category: string; reason: string; description: string | null}>;
22
};
23
type CompileResult = CompileSuccess | CompileParseError | CompileErrors;
24
@@ -70,7 +70,11 @@ function compileAndGetError(
70
return {kind: 'success'};
71
} catch (e: unknown) {
72
const error = e as Error & {
73
- details?: Array<{category: string; reason: string}>;
73
+ details?: Array<{
74
+ category: string;
75
+ reason: string;
76
+ description: string | null;
77
+ }>;
78
};
79
// Check if this is a CompilerError with details
80
if (error.details && error.details.length > 0) {
@@ -79,6 +83,7 @@ function compileAndGetError(
83
errors: error.details.map(detail => ({
84
category: detail.category,
85
reason: detail.reason,
86
+ description: detail.description,
87
})),
88
};
89
}
@@ -89,6 +94,7 @@ function compileAndGetError(
94
{
95
category: error.name ?? 'Error',
96
reason: error.message,
97
+ description: null,
98
},
99
],
100
};
@@ -108,7 +114,8 @@ function errorsMatch(a: CompileErrors, b: CompileResult): boolean {
114
for (let i = 0; i < a.errors.length; i++) {
115
if (
116
a.errors[i].category !== b.errors[i].category ||
111
- a.errors[i].reason !== b.errors[i].reason
117
+ a.errors[i].reason !== b.errors[i].reason ||
118
+ a.errors[i].description !== b.errors[i].description
119
) {
120
return false;
121
}
@@ -217,6 +224,45 @@ function* removeCallArguments(ast: t.File): Generator<t.File> {
224
}
225
}
226
227
+/**
228
+ * Generator that yields ASTs with function parameters removed one at a time
229
+ */
230
+function* removeFunctionParameters(ast: t.File): Generator<t.File> {
231
+ // Collect all functions with parameters
232
+ const funcSites: Array<{funcIndex: number; paramCount: number}> = [];
233
+ let funcIndex = 0;
234
+ t.traverseFast(ast, node => {
235
+ if (t.isFunction(node) && node.params.length > 0) {
236
+ funcSites.push({funcIndex, paramCount: node.params.length});
237
+ funcIndex++;
238
+ }
239
+ });
240
+
241
+ // For each function, try removing each parameter (from end to start)
242
+ for (const {funcIndex: targetFuncIdx, paramCount} of funcSites) {
243
+ for (let paramIdx = paramCount - 1; paramIdx >= 0; paramIdx--) {
244
+ const cloned = cloneAst(ast);
245
+ let idx = 0;
246
+ let modified = false;
247
+
248
+ t.traverseFast(cloned, node => {
249
+ if (modified) return;
250
+ if (t.isFunction(node) && node.params.length > 0) {
251
+ if (idx === targetFuncIdx && paramIdx < node.params.length) {
252
+ node.params.splice(paramIdx, 1);
253
+ modified = true;
254
+ }
255
+ idx++;
256
+ }
257
+ });
258
+
259
+ if (modified) {
260
+ yield cloned;
261
+ }
262
+ }
263
+ }
264
+}
265
+
266
/**
267
* Generator that simplifies call expressions by replacing them with their arguments.
268
* For single argument: foo(x) -> x
@@ -1566,6 +1612,84 @@ function* removeObjectProperties(ast: t.File): Generator<t.File> {
1612
}
1613
}
1614
1615
+/**
1616
+ * Generator that removes elements from array destructuring patterns one at a time
1617
+ */
1618
+function* removeArrayPatternElements(ast: t.File): Generator<t.File> {
1619
+ // Collect all array patterns with elements
1620
+ const patternSites: Array<{patternIndex: number; elementCount: number}> = [];
1621
+ let patternIndex = 0;
1622
+ t.traverseFast(ast, node => {
1623
+ if (t.isArrayPattern(node) && node.elements.length > 0) {
1624
+ patternSites.push({patternIndex, elementCount: node.elements.length});
1625
+ patternIndex++;
1626
+ }
1627
+ });
1628
+
1629
+ // For each pattern, try removing each element (from end to start)
1630
+ for (const {patternIndex: targetPatternIdx, elementCount} of patternSites) {
1631
+ for (let elemIdx = elementCount - 1; elemIdx >= 0; elemIdx--) {
1632
+ const cloned = cloneAst(ast);
1633
+ let idx = 0;
1634
+ let modified = false;
1635
+
1636
+ t.traverseFast(cloned, node => {
1637
+ if (modified) return;
1638
+ if (t.isArrayPattern(node) && node.elements.length > 0) {
1639
+ if (idx === targetPatternIdx && elemIdx < node.elements.length) {
1640
+ node.elements.splice(elemIdx, 1);
1641
+ modified = true;
1642
+ }
1643
+ idx++;
1644
+ }
1645
+ });
1646
+
1647
+ if (modified) {
1648
+ yield cloned;
1649
+ }
1650
+ }
1651
+ }
1652
+}
1653
+
1654
+/**
1655
+ * Generator that removes properties from object destructuring patterns one at a time
1656
+ */
1657
+function* removeObjectPatternProperties(ast: t.File): Generator<t.File> {
1658
+ // Collect all object patterns with properties
1659
+ const patternSites: Array<{patternIndex: number; propCount: number}> = [];
1660
+ let patternIndex = 0;
1661
+ t.traverseFast(ast, node => {
1662
+ if (t.isObjectPattern(node) && node.properties.length > 0) {
1663
+ patternSites.push({patternIndex, propCount: node.properties.length});
1664
+ patternIndex++;
1665
+ }
1666
+ });
1667
+
1668
+ // For each pattern, try removing each property (from end to start)
1669
+ for (const {patternIndex: targetPatternIdx, propCount} of patternSites) {
1670
+ for (let propIdx = propCount - 1; propIdx >= 0; propIdx--) {
1671
+ const cloned = cloneAst(ast);
1672
+ let idx = 0;
1673
+ let modified = false;
1674
+
1675
+ t.traverseFast(cloned, node => {
1676
+ if (modified) return;
1677
+ if (t.isObjectPattern(node) && node.properties.length > 0) {
1678
+ if (idx === targetPatternIdx && propIdx < node.properties.length) {
1679
+ node.properties.splice(propIdx, 1);
1680
+ modified = true;
1681
+ }
1682
+ idx++;
1683
+ }
1684
+ });
1685
+
1686
+ if (modified) {
1687
+ yield cloned;
1688
+ }
1689
+ }
1690
+ }
1691
+}
1692
+
1693
/**
1694
* Generator that simplifies assignment expressions (a = b) -> a or b
1695
*/
@@ -1852,8 +1976,14 @@ function* simplifyIdentifiersRenameRef(ast: t.File): Generator<t.File> {
1976
const simplificationStrategies = [
1977
{name: 'removeStatements', generator: removeStatements},
1978
{name: 'removeCallArguments', generator: removeCallArguments},
1979
+ {name: 'removeFunctionParameters', generator: removeFunctionParameters},
1980
{name: 'removeArrayElements', generator: removeArrayElements},
1981
{name: 'removeObjectProperties', generator: removeObjectProperties},
1982
+ {name: 'removeArrayPatternElements', generator: removeArrayPatternElements},
1983
+ {
1984
+ name: 'removeObjectPatternProperties',
1985
+ generator: removeObjectPatternProperties,
1986
+ },
1987
{name: 'removeJSXAttributes', generator: removeJSXAttributes},
1988
{name: 'removeJSXChildren', generator: removeJSXChildren},
1989
{name: 'removeJSXFragmentChildren', generator: removeJSXFragmentChildren},