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.