compiler/packages/snap/src/minimize.ts TYPESCRIPT 2,300 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,300.
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 */78import type {PluginObj} from '@babel/core';9import {transformFromAstSync} from '@babel/core';10import generate from '@babel/generator';11import traverse from '@babel/traverse';12import * as t from '@babel/types';13import type {parseConfigPragmaForTests as ParseConfigPragma} from 'babel-plugin-react-compiler/src/Utils/TestUtils';14import {parseInput} from './compiler.js';15import {16  PARSE_CONFIG_PRAGMA_IMPORT,17  BABEL_PLUGIN_SRC,18  BABEL_PLUGIN_RUST_SRC,19} from './constants.js';2021type CompileSuccess = {kind: 'success'};22type CompileParseError = {kind: 'parse_error'; message: string};23type CompileErrors = {24  kind: 'errors';25  errors: Array<{category: string; reason: string; description: string | null}>;26};27type CompileResult = CompileSuccess | CompileParseError | CompileErrors;2829/**30 * Compile code and extract error information31 */32function compileAndGetError(33  code: string,34  filename: string,35  language: 'flow' | 'typescript',36  sourceType: 'module' | 'script',37  plugin: PluginObj,38  parseConfigPragmaFn: typeof ParseConfigPragma,39): CompileResult {40  let ast: t.File;41  try {42    ast = parseInput(code, filename, language, sourceType);43  } catch (e: unknown) {44    return {kind: 'parse_error', message: (e as Error).message};45  }4647  const firstLine = code.substring(0, code.indexOf('\n'));48  const config = parseConfigPragmaFn(firstLine, {compilationMode: 'all'});49  const options = {50    ...config,51    environment: {52      ...config.environment,53    },54    logger: {55      logEvent: () => {},56      debugLogIRs: () => {},57    },58    enableReanimatedCheck: false,59  };6061  try {62    transformFromAstSync(ast, code, {63      filename: '/' + filename,64      highlightCode: false,65      retainLines: true,66      compact: true,67      plugins: [[plugin, options]],68      sourceType: 'module',69      ast: false,70      cloneInputAst: true,71      configFile: false,72      babelrc: false,73    });74    return {kind: 'success'};75  } catch (e: unknown) {76    const error = e as Error & {77      details?: Array<{78        category: string;79        reason: string;80        description: string | null;81      }>;82    };83    // Check if this is a CompilerError with details84    if (error.details && error.details.length > 0) {85      return {86        kind: 'errors',87        errors: error.details.map(detail => ({88          category: detail.category,89          reason: detail.reason,90          description: detail.description,91        })),92      };93    }94    // Fallback for other errors - use error name/message95    return {96      kind: 'errors',97      errors: [98        {99          category: error.name ?? 'Error',100          reason: error.message,101          description: null,102        },103      ],104    };105  }106}107108/**109 * Check if two compile errors match110 */111function errorsMatch(a: CompileErrors, b: CompileResult): boolean {112  if (b.kind !== 'errors') {113    return false;114  }115  if (a.errors.length !== b.errors.length) {116    return false;117  }118  for (let i = 0; i < a.errors.length; i++) {119    if (120      a.errors[i].category !== b.errors[i].category ||121      a.errors[i].reason !== b.errors[i].reason ||122      a.errors[i].description !== b.errors[i].description123    ) {124      return false;125    }126  }127  return true;128}129130/**131 * Convert AST to code string132 */133function astToCode(ast: t.File): string {134  return generate(ast).code;135}136137/**138 * Clone an AST node deeply139 */140function cloneAst(ast: t.File): t.File {141  return t.cloneNode(ast, true);142}143144/**145 * Generator that yields ASTs with statements removed one at a time146 */147function* removeStatements(ast: t.File): Generator<t.File> {148  // Collect all statement locations: which container (by index) and which statement index149  const statementLocations: Array<{containerIndex: number; stmtIndex: number}> =150    [];151  let containerIndex = 0;152153  t.traverseFast(ast, node => {154    if (t.isBlockStatement(node) || t.isProgram(node)) {155      const body = node.body as t.Statement[];156      // Iterate in reverse order so removing later statements first157      for (let i = body.length - 1; i >= 0; i--) {158        statementLocations.push({containerIndex, stmtIndex: i});159      }160      containerIndex++;161    }162  });163164  for (const {165    containerIndex: targetContainerIdx,166    stmtIndex,167  } of statementLocations) {168    const cloned = cloneAst(ast);169    let idx = 0;170    let modified = false;171172    t.traverseFast(cloned, node => {173      if (modified) return;174      if (t.isBlockStatement(node) || t.isProgram(node)) {175        if (idx === targetContainerIdx) {176          const body = node.body as t.Statement[];177          if (stmtIndex < body.length) {178            body.splice(stmtIndex, 1);179            modified = true;180          }181        }182        idx++;183      }184    });185186    if (modified) {187      yield cloned;188    }189  }190}191192/**193 * Generator that yields ASTs with call arguments removed one at a time194 */195function* removeCallArguments(ast: t.File): Generator<t.File> {196  // Collect all call expressions with their argument counts197  const callSites: Array<{callIndex: number; argCount: number}> = [];198  let callIndex = 0;199  t.traverseFast(ast, node => {200    if (t.isCallExpression(node) && node.arguments.length > 0) {201      callSites.push({callIndex, argCount: node.arguments.length});202      callIndex++;203    }204  });205206  // For each call site, try removing each argument one at a time (from end to start)207  for (const {callIndex: targetCallIdx, argCount} of callSites) {208    for (let argIdx = argCount - 1; argIdx >= 0; argIdx--) {209      const cloned = cloneAst(ast);210      let idx = 0;211      let modified = false;212213      t.traverseFast(cloned, node => {214        if (modified) return;215        if (t.isCallExpression(node) && node.arguments.length > 0) {216          if (idx === targetCallIdx && argIdx < node.arguments.length) {217            node.arguments.splice(argIdx, 1);218            modified = true;219          }220          idx++;221        }222      });223224      if (modified) {225        yield cloned;226      }227    }228  }229}230231/**232 * Generator that yields ASTs with function parameters removed one at a time233 */234function* removeFunctionParameters(ast: t.File): Generator<t.File> {235  // Collect all functions with parameters236  const funcSites: Array<{funcIndex: number; paramCount: number}> = [];237  let funcIndex = 0;238  t.traverseFast(ast, node => {239    if (t.isFunction(node) && node.params.length > 0) {240      funcSites.push({funcIndex, paramCount: node.params.length});241      funcIndex++;242    }243  });244245  // For each function, try removing each parameter (from end to start)246  for (const {funcIndex: targetFuncIdx, paramCount} of funcSites) {247    for (let paramIdx = paramCount - 1; paramIdx >= 0; paramIdx--) {248      const cloned = cloneAst(ast);249      let idx = 0;250      let modified = false;251252      t.traverseFast(cloned, node => {253        if (modified) return;254        if (t.isFunction(node) && node.params.length > 0) {255          if (idx === targetFuncIdx && paramIdx < node.params.length) {256            node.params.splice(paramIdx, 1);257            modified = true;258          }259          idx++;260        }261      });262263      if (modified) {264        yield cloned;265      }266    }267  }268}269270/**271 * Generator that simplifies call expressions by replacing them with their arguments.272 * For single argument: foo(x) -> x273 * For multiple arguments: foo(x, y) -> [x, y]274 */275function* simplifyCallExpressions(ast: t.File): Generator<t.File> {276  // Count call expressions with arguments277  let callCount = 0;278  t.traverseFast(ast, node => {279    if (t.isCallExpression(node) && node.arguments.length > 0) {280      callCount++;281    }282  });283284  // For each call, try replacing with arguments285  for (let targetIdx = 0; targetIdx < callCount; targetIdx++) {286    const cloned = cloneAst(ast);287    let idx = 0;288    let modified = false;289290    traverse(cloned, {291      CallExpression(path) {292        if (modified) return;293        if (path.node.arguments.length > 0 && idx === targetIdx) {294          const args = path.node.arguments;295          // Filter to only Expression arguments (not SpreadElement)296          const exprArgs = args.filter((arg): arg is t.Expression =>297            t.isExpression(arg),298          );299          if (exprArgs.length === 0) {300            idx++;301            return;302          }303          if (exprArgs.length === 1) {304            // Single argument: replace call with the argument305            path.replaceWith(exprArgs[0]);306          } else {307            // Multiple arguments: replace call with array of arguments308            path.replaceWith(t.arrayExpression(exprArgs));309          }310          modified = true;311        }312        idx++;313      },314    });315316    if (modified) {317      yield cloned;318    }319  }320321  // Also try replacing with each individual argument for multi-arg calls322  for (let targetIdx = 0; targetIdx < callCount; targetIdx++) {323    // First, find the arg count for this call324    let argCount = 0;325    let currentIdx = 0;326    t.traverseFast(ast, node => {327      if (t.isCallExpression(node) && node.arguments.length > 0) {328        if (currentIdx === targetIdx) {329          argCount = node.arguments.length;330        }331        currentIdx++;332      }333    });334335    // Try replacing with each argument individually336    for (let argIdx = 0; argIdx < argCount; argIdx++) {337      const cloned = cloneAst(ast);338      let idx = 0;339      let modified = false;340341      traverse(cloned, {342        CallExpression(path) {343          if (modified) return;344          if (path.node.arguments.length > 0 && idx === targetIdx) {345            const arg = path.node.arguments[argIdx];346            if (t.isExpression(arg)) {347              path.replaceWith(arg);348              modified = true;349            }350          }351          idx++;352        },353      });354355      if (modified) {356        yield cloned;357      }358    }359  }360}361362/**363 * Generator that simplifies conditional expressions (a ? b : c) -> a, b, or c364 */365function* simplifyConditionals(ast: t.File): Generator<t.File> {366  // Count conditionals367  let condCount = 0;368  t.traverseFast(ast, node => {369    if (t.isConditionalExpression(node)) {370      condCount++;371    }372  });373374  // Try replacing with test condition375  for (let targetIdx = 0; targetIdx < condCount; targetIdx++) {376    const cloned = cloneAst(ast);377    let modified = false;378    let idx = 0;379380    traverse(cloned, {381      ConditionalExpression(path) {382        if (modified) return;383        if (idx === targetIdx) {384          path.replaceWith(path.node.test);385          modified = true;386        }387        idx++;388      },389    });390391    if (modified) {392      yield cloned;393    }394  }395396  // Try replacing with consequent397  for (let targetIdx = 0; targetIdx < condCount; targetIdx++) {398    const cloned = cloneAst(ast);399    let modified = false;400    let idx = 0;401402    traverse(cloned, {403      ConditionalExpression(path) {404        if (modified) return;405        if (idx === targetIdx) {406          path.replaceWith(path.node.consequent);407          modified = true;408        }409        idx++;410      },411    });412413    if (modified) {414      yield cloned;415    }416  }417418  // Also try replacing with alternate419  for (let targetIdx = 0; targetIdx < condCount; targetIdx++) {420    const cloned = cloneAst(ast);421    let modified = false;422    let idx = 0;423424    traverse(cloned, {425      ConditionalExpression(path) {426        if (modified) return;427        if (idx === targetIdx) {428          path.replaceWith(path.node.alternate);429          modified = true;430        }431        idx++;432      },433    });434435    if (modified) {436      yield cloned;437    }438  }439}440441/**442 * Generator that simplifies logical expressions (a && b) -> a or b443 */444function* simplifyLogicalExpressions(ast: t.File): Generator<t.File> {445  // Count logical expressions446  let logicalCount = 0;447  t.traverseFast(ast, node => {448    if (t.isLogicalExpression(node)) {449      logicalCount++;450    }451  });452453  // Try replacing with left side454  for (let targetIdx = 0; targetIdx < logicalCount; targetIdx++) {455    const cloned = cloneAst(ast);456    let idx = 0;457    let modified = false;458459    traverse(cloned, {460      LogicalExpression(path) {461        if (modified) return;462        if (idx === targetIdx) {463          path.replaceWith(path.node.left);464          modified = true;465        }466        idx++;467      },468    });469470    if (modified) {471      yield cloned;472    }473  }474475  // Try replacing with right side476  for (let targetIdx = 0; targetIdx < logicalCount; targetIdx++) {477    const cloned = cloneAst(ast);478    let idx = 0;479    let modified = false;480481    traverse(cloned, {482      LogicalExpression(path) {483        if (modified) return;484        if (idx === targetIdx) {485          path.replaceWith(path.node.right);486          modified = true;487        }488        idx++;489      },490    });491492    if (modified) {493      yield cloned;494    }495  }496}497498/**499 * Generator that simplifies optional chains (a?.b) -> a.b500 */501function* simplifyOptionalChains(ast: t.File): Generator<t.File> {502  // Count optional expressions503  let optionalCount = 0;504  t.traverseFast(ast, node => {505    if (506      t.isOptionalMemberExpression(node) ||507      t.isOptionalCallExpression(node)508    ) {509      optionalCount++;510    }511  });512513  for (let targetIdx = 0; targetIdx < optionalCount; targetIdx++) {514    const cloned = cloneAst(ast);515    let idx = 0;516    let modified = false;517518    traverse(cloned, {519      OptionalMemberExpression(path) {520        if (modified) return;521        if (idx === targetIdx) {522          const {object, property, computed} = path.node;523          path.replaceWith(t.memberExpression(object, property, computed));524          modified = true;525        }526        idx++;527      },528      OptionalCallExpression(path) {529        if (modified) return;530        if (idx === targetIdx) {531          const {callee, arguments: args} = path.node;532          if (t.isExpression(callee)) {533            path.replaceWith(t.callExpression(callee, args));534            modified = true;535          }536        }537        idx++;538      },539    });540541    if (modified) {542      yield cloned;543    }544  }545}546547/**548 * Generator that simplifies await expressions: await expr -> expr549 */550function* simplifyAwaitExpressions(ast: t.File): Generator<t.File> {551  // Count await expressions552  let awaitCount = 0;553  t.traverseFast(ast, node => {554    if (t.isAwaitExpression(node)) {555      awaitCount++;556    }557  });558559  for (let targetIdx = 0; targetIdx < awaitCount; targetIdx++) {560    const cloned = cloneAst(ast);561    let idx = 0;562    let modified = false;563564    traverse(cloned, {565      AwaitExpression(path) {566        if (modified) return;567        if (idx === targetIdx) {568          path.replaceWith(path.node.argument);569          modified = true;570        }571        idx++;572      },573    });574575    if (modified) {576      yield cloned;577    }578  }579}580581/**582 * Generator that simplifies if statements:583 * - Replace with test expression (as expression statement)584 * - Replace with consequent block585 * - Replace with alternate block (if present)586 */587function* simplifyIfStatements(ast: t.File): Generator<t.File> {588  // Count if statements589  let ifCount = 0;590  t.traverseFast(ast, node => {591    if (t.isIfStatement(node)) {592      ifCount++;593    }594  });595596  // Try replacing with test expression597  for (let targetIdx = 0; targetIdx < ifCount; targetIdx++) {598    const cloned = cloneAst(ast);599    let idx = 0;600    let modified = false;601602    traverse(cloned, {603      IfStatement(path) {604        if (modified) return;605        if (idx === targetIdx) {606          path.replaceWith(t.expressionStatement(path.node.test));607          modified = true;608        }609        idx++;610      },611    });612613    if (modified) {614      yield cloned;615    }616  }617618  // Try replacing with consequent619  for (let targetIdx = 0; targetIdx < ifCount; targetIdx++) {620    const cloned = cloneAst(ast);621    let idx = 0;622    let modified = false;623624    traverse(cloned, {625      IfStatement(path) {626        if (modified) return;627        if (idx === targetIdx) {628          path.replaceWith(path.node.consequent);629          modified = true;630        }631        idx++;632      },633    });634635    if (modified) {636      yield cloned;637    }638  }639640  // Try replacing with alternate (if present)641  for (let targetIdx = 0; targetIdx < ifCount; targetIdx++) {642    const cloned = cloneAst(ast);643    let idx = 0;644    let modified = false;645646    traverse(cloned, {647      IfStatement(path) {648        if (modified) return;649        if (idx === targetIdx && path.node.alternate) {650          path.replaceWith(path.node.alternate);651          modified = true;652        }653        idx++;654      },655    });656657    if (modified) {658      yield cloned;659    }660  }661}662663/**664 * Generator that simplifies switch statements:665 * - Replace with discriminant expression666 * - Replace with each case's consequent statements667 */668function* simplifySwitchStatements(ast: t.File): Generator<t.File> {669  // Count switch statements670  let switchCount = 0;671  t.traverseFast(ast, node => {672    if (t.isSwitchStatement(node)) {673      switchCount++;674    }675  });676677  // Try replacing with discriminant678  for (let targetIdx = 0; targetIdx < switchCount; targetIdx++) {679    const cloned = cloneAst(ast);680    let idx = 0;681    let modified = false;682683    traverse(cloned, {684      SwitchStatement(path) {685        if (modified) return;686        if (idx === targetIdx) {687          path.replaceWith(t.expressionStatement(path.node.discriminant));688          modified = true;689        }690        idx++;691      },692    });693694    if (modified) {695      yield cloned;696    }697  }698699  // For each switch, try replacing with each case's body700  for (let targetIdx = 0; targetIdx < switchCount; targetIdx++) {701    // Find case count for this switch702    let caseCount = 0;703    let currentIdx = 0;704    t.traverseFast(ast, node => {705      if (t.isSwitchStatement(node)) {706        if (currentIdx === targetIdx) {707          caseCount = node.cases.length;708        }709        currentIdx++;710      }711    });712713    for (let caseIdx = 0; caseIdx < caseCount; caseIdx++) {714      const cloned = cloneAst(ast);715      let idx = 0;716      let modified = false;717718      traverse(cloned, {719        SwitchStatement(path) {720          if (modified) return;721          if (idx === targetIdx) {722            const switchCase = path.node.cases[caseIdx];723            if (switchCase && switchCase.consequent.length > 0) {724              path.replaceWithMultiple(switchCase.consequent);725              modified = true;726            }727          }728          idx++;729        },730      });731732      if (modified) {733        yield cloned;734      }735    }736  }737}738739/**740 * Generator that simplifies while statements:741 * - Replace with test expression742 * - Replace with body743 */744function* simplifyWhileStatements(ast: t.File): Generator<t.File> {745  // Count while statements746  let whileCount = 0;747  t.traverseFast(ast, node => {748    if (t.isWhileStatement(node)) {749      whileCount++;750    }751  });752753  // Try replacing with test754  for (let targetIdx = 0; targetIdx < whileCount; targetIdx++) {755    const cloned = cloneAst(ast);756    let idx = 0;757    let modified = false;758759    traverse(cloned, {760      WhileStatement(path) {761        if (modified) return;762        if (idx === targetIdx) {763          path.replaceWith(t.expressionStatement(path.node.test));764          modified = true;765        }766        idx++;767      },768    });769770    if (modified) {771      yield cloned;772    }773  }774775  // Try replacing with body776  for (let targetIdx = 0; targetIdx < whileCount; targetIdx++) {777    const cloned = cloneAst(ast);778    let idx = 0;779    let modified = false;780781    traverse(cloned, {782      WhileStatement(path) {783        if (modified) return;784        if (idx === targetIdx) {785          path.replaceWith(path.node.body);786          modified = true;787        }788        idx++;789      },790    });791792    if (modified) {793      yield cloned;794    }795  }796}797798/**799 * Generator that simplifies do-while statements:800 * - Replace with test expression801 * - Replace with body802 */803function* simplifyDoWhileStatements(ast: t.File): Generator<t.File> {804  // Count do-while statements805  let doWhileCount = 0;806  t.traverseFast(ast, node => {807    if (t.isDoWhileStatement(node)) {808      doWhileCount++;809    }810  });811812  // Try replacing with test813  for (let targetIdx = 0; targetIdx < doWhileCount; targetIdx++) {814    const cloned = cloneAst(ast);815    let idx = 0;816    let modified = false;817818    traverse(cloned, {819      DoWhileStatement(path) {820        if (modified) return;821        if (idx === targetIdx) {822          path.replaceWith(t.expressionStatement(path.node.test));823          modified = true;824        }825        idx++;826      },827    });828829    if (modified) {830      yield cloned;831    }832  }833834  // Try replacing with body835  for (let targetIdx = 0; targetIdx < doWhileCount; targetIdx++) {836    const cloned = cloneAst(ast);837    let idx = 0;838    let modified = false;839840    traverse(cloned, {841      DoWhileStatement(path) {842        if (modified) return;843        if (idx === targetIdx) {844          path.replaceWith(path.node.body);845          modified = true;846        }847        idx++;848      },849    });850851    if (modified) {852      yield cloned;853    }854  }855}856857/**858 * Generator that simplifies for statements:859 * - Replace with init (if expression)860 * - Replace with test expression861 * - Replace with update expression862 * - Replace with body863 */864function* simplifyForStatements(ast: t.File): Generator<t.File> {865  // Count for statements866  let forCount = 0;867  t.traverseFast(ast, node => {868    if (t.isForStatement(node)) {869      forCount++;870    }871  });872873  // Try replacing with init (if it's an expression)874  for (let targetIdx = 0; targetIdx < forCount; targetIdx++) {875    const cloned = cloneAst(ast);876    let idx = 0;877    let modified = false;878879    traverse(cloned, {880      ForStatement(path) {881        if (modified) return;882        if (idx === targetIdx && path.node.init) {883          if (t.isExpression(path.node.init)) {884            path.replaceWith(t.expressionStatement(path.node.init));885          } else {886            // It's a VariableDeclaration887            path.replaceWith(path.node.init);888          }889          modified = true;890        }891        idx++;892      },893    });894895    if (modified) {896      yield cloned;897    }898  }899900  // Try replacing with test901  for (let targetIdx = 0; targetIdx < forCount; targetIdx++) {902    const cloned = cloneAst(ast);903    let idx = 0;904    let modified = false;905906    traverse(cloned, {907      ForStatement(path) {908        if (modified) return;909        if (idx === targetIdx && path.node.test) {910          path.replaceWith(t.expressionStatement(path.node.test));911          modified = true;912        }913        idx++;914      },915    });916917    if (modified) {918      yield cloned;919    }920  }921922  // Try replacing with update923  for (let targetIdx = 0; targetIdx < forCount; targetIdx++) {924    const cloned = cloneAst(ast);925    let idx = 0;926    let modified = false;927928    traverse(cloned, {929      ForStatement(path) {930        if (modified) return;931        if (idx === targetIdx && path.node.update) {932          path.replaceWith(t.expressionStatement(path.node.update));933          modified = true;934        }935        idx++;936      },937    });938939    if (modified) {940      yield cloned;941    }942  }943944  // Try replacing with body945  for (let targetIdx = 0; targetIdx < forCount; targetIdx++) {946    const cloned = cloneAst(ast);947    let idx = 0;948    let modified = false;949950    traverse(cloned, {951      ForStatement(path) {952        if (modified) return;953        if (idx === targetIdx) {954          path.replaceWith(path.node.body);955          modified = true;956        }957        idx++;958      },959    });960961    if (modified) {962      yield cloned;963    }964  }965}966967/**968 * Generator that simplifies for-in statements:969 * - Replace with left (variable declaration or expression)970 * - Replace with right expression971 * - Replace with body972 */973function* simplifyForInStatements(ast: t.File): Generator<t.File> {974  // Count for-in statements975  let forInCount = 0;976  t.traverseFast(ast, node => {977    if (t.isForInStatement(node)) {978      forInCount++;979    }980  });981982  // Try replacing with left983  for (let targetIdx = 0; targetIdx < forInCount; targetIdx++) {984    const cloned = cloneAst(ast);985    let idx = 0;986    let modified = false;987988    traverse(cloned, {989      ForInStatement(path) {990        if (modified) return;991        if (idx === targetIdx) {992          const left = path.node.left;993          if (t.isExpression(left)) {994            path.replaceWith(t.expressionStatement(left));995          } else {996            path.replaceWith(left);997          }998          modified = true;999        }1000        idx++;1001      },1002    });10031004    if (modified) {1005      yield cloned;1006    }1007  }10081009  // Try replacing with right1010  for (let targetIdx = 0; targetIdx < forInCount; targetIdx++) {1011    const cloned = cloneAst(ast);1012    let idx = 0;1013    let modified = false;10141015    traverse(cloned, {1016      ForInStatement(path) {1017        if (modified) return;1018        if (idx === targetIdx) {1019          path.replaceWith(t.expressionStatement(path.node.right));1020          modified = true;1021        }1022        idx++;1023      },1024    });10251026    if (modified) {1027      yield cloned;1028    }1029  }10301031  // Try replacing with body1032  for (let targetIdx = 0; targetIdx < forInCount; targetIdx++) {1033    const cloned = cloneAst(ast);1034    let idx = 0;1035    let modified = false;10361037    traverse(cloned, {1038      ForInStatement(path) {1039        if (modified) return;1040        if (idx === targetIdx) {1041          path.replaceWith(path.node.body);1042          modified = true;1043        }1044        idx++;1045      },1046    });10471048    if (modified) {1049      yield cloned;1050    }1051  }1052}10531054/**1055 * Generator that simplifies for-of statements:1056 * - Replace with left (variable declaration or expression)1057 * - Replace with right expression1058 * - Replace with body1059 */1060function* simplifyForOfStatements(ast: t.File): Generator<t.File> {1061  // Count for-of statements1062  let forOfCount = 0;1063  t.traverseFast(ast, node => {1064    if (t.isForOfStatement(node)) {1065      forOfCount++;1066    }1067  });10681069  // Try replacing with left1070  for (let targetIdx = 0; targetIdx < forOfCount; targetIdx++) {1071    const cloned = cloneAst(ast);1072    let idx = 0;1073    let modified = false;10741075    traverse(cloned, {1076      ForOfStatement(path) {1077        if (modified) return;1078        if (idx === targetIdx) {1079          const left = path.node.left;1080          if (t.isExpression(left)) {1081            path.replaceWith(t.expressionStatement(left));1082          } else {1083            path.replaceWith(left);1084          }1085          modified = true;1086        }1087        idx++;1088      },1089    });10901091    if (modified) {1092      yield cloned;1093    }1094  }10951096  // Try replacing with right1097  for (let targetIdx = 0; targetIdx < forOfCount; targetIdx++) {1098    const cloned = cloneAst(ast);1099    let idx = 0;1100    let modified = false;11011102    traverse(cloned, {1103      ForOfStatement(path) {1104        if (modified) return;1105        if (idx === targetIdx) {1106          path.replaceWith(t.expressionStatement(path.node.right));1107          modified = true;1108        }1109        idx++;1110      },1111    });11121113    if (modified) {1114      yield cloned;1115    }1116  }11171118  // Try replacing with body1119  for (let targetIdx = 0; targetIdx < forOfCount; targetIdx++) {1120    const cloned = cloneAst(ast);1121    let idx = 0;1122    let modified = false;11231124    traverse(cloned, {1125      ForOfStatement(path) {1126        if (modified) return;1127        if (idx === targetIdx) {1128          path.replaceWith(path.node.body);1129          modified = true;1130        }1131        idx++;1132      },1133    });11341135    if (modified) {1136      yield cloned;1137    }1138  }1139}11401141/**1142 * Generator that simplifies variable declarations by removing init expressions.1143 * let x = expr; -> let x;1144 * var x = expr; -> var x;1145 * Note: const without init is invalid, so we skip const declarations.1146 */1147function* simplifyVariableDeclarations(ast: t.File): Generator<t.File> {1148  // Collect all variable declarators with init expressions (excluding const)1149  const declaratorSites: Array<{declIndex: number}> = [];1150  let declIndex = 0;1151  t.traverseFast(ast, node => {1152    if (t.isVariableDeclaration(node) && node.kind !== 'const') {1153      for (const declarator of node.declarations) {1154        if (declarator.init) {1155          declaratorSites.push({declIndex});1156          declIndex++;1157        }1158      }1159    }1160  });11611162  // Try removing init from each declarator1163  for (const {declIndex: targetDeclIdx} of declaratorSites) {1164    const cloned = cloneAst(ast);1165    let idx = 0;1166    let modified = false;11671168    t.traverseFast(cloned, node => {1169      if (modified) return;1170      if (t.isVariableDeclaration(node) && node.kind !== 'const') {1171        for (const declarator of node.declarations) {1172          if (declarator.init) {1173            if (idx === targetDeclIdx) {1174              declarator.init = null;1175              modified = true;1176              return;1177            }1178            idx++;1179          }1180        }1181      }1182    });11831184    if (modified) {1185      yield cloned;1186    }1187  }1188}11891190/**1191 * Generator that simplifies try/catch/finally statements:1192 * - Replace with try block contents1193 * - Replace with catch block contents (if present)1194 * - Replace with finally block contents (if present)1195 */1196function* simplifyTryStatements(ast: t.File): Generator<t.File> {1197  // Count try statements1198  let tryCount = 0;1199  t.traverseFast(ast, node => {1200    if (t.isTryStatement(node)) {1201      tryCount++;1202    }1203  });12041205  // Try replacing with try block contents1206  for (let targetIdx = 0; targetIdx < tryCount; targetIdx++) {1207    const cloned = cloneAst(ast);1208    let idx = 0;1209    let modified = false;12101211    traverse(cloned, {1212      TryStatement(path) {1213        if (modified) return;1214        if (idx === targetIdx) {1215          path.replaceWith(path.node.block);1216          modified = true;1217        }1218        idx++;1219      },1220    });12211222    if (modified) {1223      yield cloned;1224    }1225  }12261227  // Try replacing with catch block contents (if present)1228  for (let targetIdx = 0; targetIdx < tryCount; targetIdx++) {1229    const cloned = cloneAst(ast);1230    let idx = 0;1231    let modified = false;12321233    traverse(cloned, {1234      TryStatement(path) {1235        if (modified) return;1236        if (idx === targetIdx && path.node.handler) {1237          path.replaceWith(path.node.handler.body);1238          modified = true;1239        }1240        idx++;1241      },1242    });12431244    if (modified) {1245      yield cloned;1246    }1247  }12481249  // Try replacing with finally block contents (if present)1250  for (let targetIdx = 0; targetIdx < tryCount; targetIdx++) {1251    const cloned = cloneAst(ast);1252    let idx = 0;1253    let modified = false;12541255    traverse(cloned, {1256      TryStatement(path) {1257        if (modified) return;1258        if (idx === targetIdx && path.node.finalizer) {1259          path.replaceWith(path.node.finalizer);1260          modified = true;1261        }1262        idx++;1263      },1264    });12651266    if (modified) {1267      yield cloned;1268    }1269  }1270}12711272/**1273 * Generator that simplifies single-statement block statements:1274 * { statement } -> statement1275 */1276function* simplifySingleStatementBlocks(ast: t.File): Generator<t.File> {1277  // Count block statements with exactly one statement1278  let blockCount = 0;1279  t.traverseFast(ast, node => {1280    if (t.isBlockStatement(node) && node.body.length === 1) {1281      blockCount++;1282    }1283  });12841285  for (let targetIdx = 0; targetIdx < blockCount; targetIdx++) {1286    const cloned = cloneAst(ast);1287    let idx = 0;1288    let modified = false;12891290    traverse(cloned, {1291      BlockStatement(path) {1292        if (modified) return;1293        if (path.node.body.length === 1 && idx === targetIdx) {1294          // Don't unwrap blocks that require BlockStatement syntax1295          if (1296            t.isFunction(path.parent) ||1297            t.isCatchClause(path.parent) ||1298            t.isClassMethod(path.parent) ||1299            t.isObjectMethod(path.parent) ||1300            t.isTryStatement(path.parent)1301          ) {1302            idx++;1303            return;1304          }1305          path.replaceWith(path.node.body[0]);1306          modified = true;1307        }1308        idx++;1309      },1310    });13111312    if (modified) {1313      yield cloned;1314    }1315  }1316}13171318/**1319 * Generator that removes array elements one at a time1320 */1321function* removeArrayElements(ast: t.File): Generator<t.File> {1322  // Collect all array expressions with their element counts1323  const arraySites: Array<{arrayIndex: number; elementCount: number}> = [];1324  let arrayIndex = 0;1325  t.traverseFast(ast, node => {1326    if (t.isArrayExpression(node) && node.elements.length > 0) {1327      arraySites.push({arrayIndex, elementCount: node.elements.length});1328      arrayIndex++;1329    }1330  });13311332  // For each array, try removing each element one at a time (from end to start)1333  for (const {arrayIndex: targetArrayIdx, elementCount} of arraySites) {1334    for (let elemIdx = elementCount - 1; elemIdx >= 0; elemIdx--) {1335      const cloned = cloneAst(ast);1336      let idx = 0;1337      let modified = false;13381339      t.traverseFast(cloned, node => {1340        if (modified) return;1341        if (t.isArrayExpression(node) && node.elements.length > 0) {1342          if (idx === targetArrayIdx && elemIdx < node.elements.length) {1343            node.elements.splice(elemIdx, 1);1344            modified = true;1345          }1346          idx++;1347        }1348      });13491350      if (modified) {1351        yield cloned;1352      }1353    }1354  }1355}13561357/**1358 * Generator that removes JSX element attributes (props) one at a time1359 */1360function* removeJSXAttributes(ast: t.File): Generator<t.File> {1361  // Collect all JSX elements with their attribute counts1362  const jsxSites: Array<{jsxIndex: number; attrCount: number}> = [];1363  let jsxIndex = 0;1364  t.traverseFast(ast, node => {1365    if (t.isJSXOpeningElement(node) && node.attributes.length > 0) {1366      jsxSites.push({jsxIndex, attrCount: node.attributes.length});1367      jsxIndex++;1368    }1369  });13701371  // For each JSX element, try removing each attribute one at a time (from end to start)1372  for (const {jsxIndex: targetJsxIdx, attrCount} of jsxSites) {1373    for (let attrIdx = attrCount - 1; attrIdx >= 0; attrIdx--) {1374      const cloned = cloneAst(ast);1375      let idx = 0;1376      let modified = false;13771378      t.traverseFast(cloned, node => {1379        if (modified) return;1380        if (t.isJSXOpeningElement(node) && node.attributes.length > 0) {1381          if (idx === targetJsxIdx && attrIdx < node.attributes.length) {1382            node.attributes.splice(attrIdx, 1);1383            modified = true;1384          }1385          idx++;1386        }1387      });13881389      if (modified) {1390        yield cloned;1391      }1392    }1393  }1394}13951396/**1397 * Generator that removes JSX element children one at a time1398 */1399function* removeJSXChildren(ast: t.File): Generator<t.File> {1400  // Collect all JSX elements with children1401  const jsxSites: Array<{jsxIndex: number; childCount: number}> = [];1402  let jsxIndex = 0;1403  t.traverseFast(ast, node => {1404    if (t.isJSXElement(node) && node.children.length > 0) {1405      jsxSites.push({jsxIndex, childCount: node.children.length});1406      jsxIndex++;1407    }1408  });14091410  // For each JSX element, try removing each child one at a time (from end to start)1411  for (const {jsxIndex: targetJsxIdx, childCount} of jsxSites) {1412    for (let childIdx = childCount - 1; childIdx >= 0; childIdx--) {1413      const cloned = cloneAst(ast);1414      let idx = 0;1415      let modified = false;14161417      t.traverseFast(cloned, node => {1418        if (modified) return;1419        if (t.isJSXElement(node) && node.children.length > 0) {1420          if (idx === targetJsxIdx && childIdx < node.children.length) {1421            node.children.splice(childIdx, 1);1422            modified = true;1423          }1424          idx++;1425        }1426      });14271428      if (modified) {1429        yield cloned;1430      }1431    }1432  }1433}14341435/**1436 * Generator that removes JSX fragment children one at a time1437 */1438function* removeJSXFragmentChildren(ast: t.File): Generator<t.File> {1439  // Collect all JSX fragments with children1440  const fragmentSites: Array<{fragIndex: number; childCount: number}> = [];1441  let fragIndex = 0;1442  t.traverseFast(ast, node => {1443    if (t.isJSXFragment(node) && node.children.length > 0) {1444      fragmentSites.push({fragIndex, childCount: node.children.length});1445      fragIndex++;1446    }1447  });14481449  // For each fragment, try removing each child one at a time (from end to start)1450  for (const {fragIndex: targetFragIdx, childCount} of fragmentSites) {1451    for (let childIdx = childCount - 1; childIdx >= 0; childIdx--) {1452      const cloned = cloneAst(ast);1453      let idx = 0;1454      let modified = false;14551456      t.traverseFast(cloned, node => {1457        if (modified) return;1458        if (t.isJSXFragment(node) && node.children.length > 0) {1459          if (idx === targetFragIdx && childIdx < node.children.length) {1460            node.children.splice(childIdx, 1);1461            modified = true;1462          }1463          idx++;1464        }1465      });14661467      if (modified) {1468        yield cloned;1469      }1470    }1471  }1472}14731474/**1475 * Generator that replaces single-element arrays with the element itself1476 */1477function* simplifySingleElementArrays(ast: t.File): Generator<t.File> {1478  // Count single-element arrays1479  let arrayCount = 0;1480  t.traverseFast(ast, node => {1481    if (t.isArrayExpression(node) && node.elements.length === 1) {1482      arrayCount++;1483    }1484  });14851486  for (let targetIdx = 0; targetIdx < arrayCount; targetIdx++) {1487    const cloned = cloneAst(ast);1488    let idx = 0;1489    let modified = false;14901491    traverse(cloned, {1492      ArrayExpression(path) {1493        if (modified) return;1494        if (path.node.elements.length === 1 && idx === targetIdx) {1495          const elem = path.node.elements[0];1496          if (t.isExpression(elem)) {1497            path.replaceWith(elem);1498            modified = true;1499          }1500        }1501        idx++;1502      },1503    });15041505    if (modified) {1506      yield cloned;1507    }1508  }1509}15101511/**1512 * Generator that replaces single-property objects with the property value.1513 * For regular properties: {key: value} -> value1514 * For computed properties: {[key]: value} -> key (also try value)1515 */1516function* simplifySinglePropertyObjects(ast: t.File): Generator<t.File> {1517  // Count single-property objects1518  let objectCount = 0;1519  t.traverseFast(ast, node => {1520    if (t.isObjectExpression(node) && node.properties.length === 1) {1521      objectCount++;1522    }1523  });15241525  // Try replacing with value1526  for (let targetIdx = 0; targetIdx < objectCount; targetIdx++) {1527    const cloned = cloneAst(ast);1528    let idx = 0;1529    let modified = false;15301531    traverse(cloned, {1532      ObjectExpression(path) {1533        if (modified) return;1534        if (path.node.properties.length === 1 && idx === targetIdx) {1535          const prop = path.node.properties[0];1536          if (t.isObjectProperty(prop) && t.isExpression(prop.value)) {1537            path.replaceWith(prop.value);1538            modified = true;1539          }1540        }1541        idx++;1542      },1543    });15441545    if (modified) {1546      yield cloned;1547    }1548  }15491550  // For computed properties, also try replacing with key1551  for (let targetIdx = 0; targetIdx < objectCount; targetIdx++) {1552    const cloned = cloneAst(ast);1553    let idx = 0;1554    let modified = false;15551556    traverse(cloned, {1557      ObjectExpression(path) {1558        if (modified) return;1559        if (path.node.properties.length === 1 && idx === targetIdx) {1560          const prop = path.node.properties[0];1561          if (1562            t.isObjectProperty(prop) &&1563            prop.computed &&1564            t.isExpression(prop.key)1565          ) {1566            path.replaceWith(prop.key);1567            modified = true;1568          }1569        }1570        idx++;1571      },1572    });15731574    if (modified) {1575      yield cloned;1576    }1577  }1578}15791580/**1581 * Generator that removes object properties one at a time1582 */1583function* removeObjectProperties(ast: t.File): Generator<t.File> {1584  // Collect all object expressions with their property counts1585  const objectSites: Array<{objectIndex: number; propCount: number}> = [];1586  let objectIndex = 0;1587  t.traverseFast(ast, node => {1588    if (t.isObjectExpression(node) && node.properties.length > 0) {1589      objectSites.push({objectIndex, propCount: node.properties.length});1590      objectIndex++;1591    }1592  });15931594  // For each object, try removing each property one at a time (from end to start)1595  for (const {objectIndex: targetObjIdx, propCount} of objectSites) {1596    for (let propIdx = propCount - 1; propIdx >= 0; propIdx--) {1597      const cloned = cloneAst(ast);1598      let idx = 0;1599      let modified = false;16001601      t.traverseFast(cloned, node => {1602        if (modified) return;1603        if (t.isObjectExpression(node) && node.properties.length > 0) {1604          if (idx === targetObjIdx && propIdx < node.properties.length) {1605            node.properties.splice(propIdx, 1);1606            modified = true;1607          }1608          idx++;1609        }1610      });16111612      if (modified) {1613        yield cloned;1614      }1615    }1616  }1617}16181619/**1620 * Generator that removes elements from array destructuring patterns one at a time1621 */1622function* removeArrayPatternElements(ast: t.File): Generator<t.File> {1623  // Collect all array patterns with elements1624  const patternSites: Array<{patternIndex: number; elementCount: number}> = [];1625  let patternIndex = 0;1626  t.traverseFast(ast, node => {1627    if (t.isArrayPattern(node) && node.elements.length > 0) {1628      patternSites.push({patternIndex, elementCount: node.elements.length});1629      patternIndex++;1630    }1631  });16321633  // For each pattern, try removing each element (from end to start)1634  for (const {patternIndex: targetPatternIdx, elementCount} of patternSites) {1635    for (let elemIdx = elementCount - 1; elemIdx >= 0; elemIdx--) {1636      const cloned = cloneAst(ast);1637      let idx = 0;1638      let modified = false;16391640      t.traverseFast(cloned, node => {1641        if (modified) return;1642        if (t.isArrayPattern(node) && node.elements.length > 0) {1643          if (idx === targetPatternIdx && elemIdx < node.elements.length) {1644            node.elements.splice(elemIdx, 1);1645            modified = true;1646          }1647          idx++;1648        }1649      });16501651      if (modified) {1652        yield cloned;1653      }1654    }1655  }1656}16571658/**1659 * Generator that removes properties from object destructuring patterns one at a time1660 */1661function* removeObjectPatternProperties(ast: t.File): Generator<t.File> {1662  // Collect all object patterns with properties1663  const patternSites: Array<{patternIndex: number; propCount: number}> = [];1664  let patternIndex = 0;1665  t.traverseFast(ast, node => {1666    if (t.isObjectPattern(node) && node.properties.length > 0) {1667      patternSites.push({patternIndex, propCount: node.properties.length});1668      patternIndex++;1669    }1670  });16711672  // For each pattern, try removing each property (from end to start)1673  for (const {patternIndex: targetPatternIdx, propCount} of patternSites) {1674    for (let propIdx = propCount - 1; propIdx >= 0; propIdx--) {1675      const cloned = cloneAst(ast);1676      let idx = 0;1677      let modified = false;16781679      t.traverseFast(cloned, node => {1680        if (modified) return;1681        if (t.isObjectPattern(node) && node.properties.length > 0) {1682          if (idx === targetPatternIdx && propIdx < node.properties.length) {1683            node.properties.splice(propIdx, 1);1684            modified = true;1685          }1686          idx++;1687        }1688      });16891690      if (modified) {1691        yield cloned;1692      }1693    }1694  }1695}16961697/**1698 * Generator that simplifies assignment expressions (a = b) -> a or b1699 */1700function* simplifyAssignmentExpressions(ast: t.File): Generator<t.File> {1701  // Count assignment expressions1702  let assignmentCount = 0;1703  t.traverseFast(ast, node => {1704    if (t.isAssignmentExpression(node)) {1705      assignmentCount++;1706    }1707  });17081709  // Try replacing with left side (assignment target)1710  for (let targetIdx = 0; targetIdx < assignmentCount; targetIdx++) {1711    const cloned = cloneAst(ast);1712    let idx = 0;1713    let modified = false;17141715    traverse(cloned, {1716      AssignmentExpression(path) {1717        if (modified) return;1718        if (idx === targetIdx) {1719          const left = path.node.left;1720          if (t.isExpression(left)) {1721            path.replaceWith(left);1722            modified = true;1723          }1724        }1725        idx++;1726      },1727    });17281729    if (modified) {1730      yield cloned;1731    }1732  }17331734  // Try replacing with right side (assignment value)1735  for (let targetIdx = 0; targetIdx < assignmentCount; targetIdx++) {1736    const cloned = cloneAst(ast);1737    let idx = 0;1738    let modified = false;17391740    traverse(cloned, {1741      AssignmentExpression(path) {1742        if (modified) return;1743        if (idx === targetIdx) {1744          path.replaceWith(path.node.right);1745          modified = true;1746        }1747        idx++;1748      },1749    });17501751    if (modified) {1752      yield cloned;1753    }1754  }1755}17561757/**1758 * Generator that simplifies binary expressions (a + b) -> a or b1759 */1760function* simplifyBinaryExpressions(ast: t.File): Generator<t.File> {1761  // Count binary expressions1762  let binaryCount = 0;1763  t.traverseFast(ast, node => {1764    if (t.isBinaryExpression(node)) {1765      binaryCount++;1766    }1767  });17681769  // Try replacing with left side1770  for (let targetIdx = 0; targetIdx < binaryCount; targetIdx++) {1771    const cloned = cloneAst(ast);1772    let idx = 0;1773    let modified = false;17741775    traverse(cloned, {1776      BinaryExpression(path) {1777        if (modified) return;1778        if (idx === targetIdx) {1779          path.replaceWith(path.node.left);1780          modified = true;1781        }1782        idx++;1783      },1784    });17851786    if (modified) {1787      yield cloned;1788    }1789  }17901791  // Try replacing with right side1792  for (let targetIdx = 0; targetIdx < binaryCount; targetIdx++) {1793    const cloned = cloneAst(ast);1794    let idx = 0;1795    let modified = false;17961797    traverse(cloned, {1798      BinaryExpression(path) {1799        if (modified) return;1800        if (idx === targetIdx) {1801          path.replaceWith(path.node.right);1802          modified = true;1803        }1804        idx++;1805      },1806    });18071808    if (modified) {1809      yield cloned;1810    }1811  }1812}18131814/**1815 * Generator that simplifies member expressions (obj.value) -> obj1816 * For computed expressions: obj[key] -> obj or key1817 */1818function* simplifyMemberExpressions(ast: t.File): Generator<t.File> {1819  // Count member expressions1820  let memberCount = 0;1821  t.traverseFast(ast, node => {1822    if (t.isMemberExpression(node)) {1823      memberCount++;1824    }1825  });18261827  // Try replacing with object1828  for (let targetIdx = 0; targetIdx < memberCount; targetIdx++) {1829    const cloned = cloneAst(ast);1830    let idx = 0;1831    let modified = false;18321833    traverse(cloned, {1834      MemberExpression(path) {1835        if (modified) return;1836        if (idx === targetIdx) {1837          path.replaceWith(path.node.object);1838          modified = true;1839        }1840        idx++;1841      },1842    });18431844    if (modified) {1845      yield cloned;1846    }1847  }18481849  // For computed expressions, also try replacing with key1850  for (let targetIdx = 0; targetIdx < memberCount; targetIdx++) {1851    const cloned = cloneAst(ast);1852    let idx = 0;1853    let modified = false;18541855    traverse(cloned, {1856      MemberExpression(path) {1857        if (modified) return;1858        if (idx === targetIdx && path.node.computed) {1859          const property = path.node.property;1860          if (t.isExpression(property)) {1861            path.replaceWith(property);1862            modified = true;1863          }1864        }1865        idx++;1866      },1867    });18681869    if (modified) {1870      yield cloned;1871    }1872  }1873}18741875/**1876 * Helper to collect all unique identifier names in the AST1877 */1878function collectUniqueIdentifierNames(ast: t.File): Set<string> {1879  const names = new Set<string>();1880  t.traverseFast(ast, node => {1881    if (t.isIdentifier(node)) {1882      names.add(node.name);1883    }1884  });1885  return names;1886}18871888/**1889 * Helper to rename all occurrences of an identifier throughout the AST1890 */1891function renameAllIdentifiers(1892  ast: t.File,1893  oldName: string,1894  newName: string,1895): boolean {1896  let modified = false;1897  t.traverseFast(ast, node => {1898    if (t.isIdentifier(node) && node.name === oldName) {1899      node.name = newName;1900      modified = true;1901    }1902  });1903  return modified;1904}19051906/**1907 * Generator that simplifies identifiers by removing "on" prefix.1908 * onClick -> Click1909 */1910function* simplifyIdentifiersRemoveOnPrefix(ast: t.File): Generator<t.File> {1911  const names = collectUniqueIdentifierNames(ast);19121913  for (const name of names) {1914    // Check if name starts with "on" followed by uppercase letter1915    if (1916      name.length > 2 &&1917      name.startsWith('on') &&1918      name[2] === name[2].toUpperCase()1919    ) {1920      const newName = name.slice(2);1921      // Skip if the new name would conflict with an existing identifier1922      if (names.has(newName)) {1923        continue;1924      }1925      const cloned = cloneAst(ast);1926      if (renameAllIdentifiers(cloned, name, newName)) {1927        yield cloned;1928      }1929    }1930  }1931}19321933/**1934 * Generator that simplifies identifiers by removing "Ref" suffix.1935 * inputRef -> input1936 */1937function* simplifyIdentifiersRemoveRefSuffix(ast: t.File): Generator<t.File> {1938  const names = collectUniqueIdentifierNames(ast);19391940  for (const name of names) {1941    // Check if name ends with "Ref" and has more characters before it1942    if (name.length > 3 && name.endsWith('Ref')) {1943      const newName = name.slice(0, -3);1944      // Skip if the new name would conflict with an existing identifier1945      if (names.has(newName)) {1946        continue;1947      }1948      // Skip if new name would be empty or just whitespace1949      if (newName.length === 0) {1950        continue;1951      }1952      const cloned = cloneAst(ast);1953      if (renameAllIdentifiers(cloned, name, newName)) {1954        yield cloned;1955      }1956    }1957  }1958}19591960/**1961 * Generator that rewrites "ref" identifier to "ref_" to avoid conflicts.1962 */1963function* simplifyIdentifiersRenameRef(ast: t.File): Generator<t.File> {1964  const names = collectUniqueIdentifierNames(ast);19651966  if (names.has('ref')) {1967    // Only rename if ref_ doesn't already exist1968    if (!names.has('ref_')) {1969      const cloned = cloneAst(ast);1970      if (renameAllIdentifiers(cloned, 'ref', 'ref_')) {1971        yield cloned;1972      }1973    }1974  }1975}19761977/**1978 * All simplification strategies in order of priority (coarse to fine)1979 */1980const simplificationStrategies = [1981  {name: 'removeStatements', generator: removeStatements},1982  {name: 'removeCallArguments', generator: removeCallArguments},1983  {name: 'removeFunctionParameters', generator: removeFunctionParameters},1984  {name: 'removeArrayElements', generator: removeArrayElements},1985  {name: 'removeObjectProperties', generator: removeObjectProperties},1986  {name: 'removeArrayPatternElements', generator: removeArrayPatternElements},1987  {1988    name: 'removeObjectPatternProperties',1989    generator: removeObjectPatternProperties,1990  },1991  {name: 'removeJSXAttributes', generator: removeJSXAttributes},1992  {name: 'removeJSXChildren', generator: removeJSXChildren},1993  {name: 'removeJSXFragmentChildren', generator: removeJSXFragmentChildren},1994  {name: 'simplifyCallExpressions', generator: simplifyCallExpressions},1995  {name: 'simplifyConditionals', generator: simplifyConditionals},1996  {name: 'simplifyLogicalExpressions', generator: simplifyLogicalExpressions},1997  {name: 'simplifyBinaryExpressions', generator: simplifyBinaryExpressions},1998  {1999    name: 'simplifyAssignmentExpressions',2000    generator: simplifyAssignmentExpressions,

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.