@samitouri / QOS-React-1 / commits / 90c6d1b218

[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},