@@ -897,17 +897,114 @@ function transformDestructuredProperties(
897897) : ts . ConciseBody {
898898 const elemName = elemParam ?. name ;
899899
900- // Collect destructured property names if the param is an object destructuring pattern
901- const destructuredProps = new Set < string > ( ) ;
900+ let transformedBody : ts . ConciseBody = body ;
901+
902+ const prependStatements = (
903+ statements : readonly ts . Statement [ ] ,
904+ currentBody : ts . ConciseBody ,
905+ ) : ts . ConciseBody => {
906+ if ( statements . length === 0 ) return currentBody ;
907+
908+ if ( ts . isBlock ( currentBody ) ) {
909+ return factory . updateBlock (
910+ currentBody ,
911+ factory . createNodeArray ( [
912+ ...statements ,
913+ ...currentBody . statements ,
914+ ] ) ,
915+ ) ;
916+ }
917+
918+ return factory . createBlock (
919+ [
920+ ...statements ,
921+ factory . createReturnStatement ( currentBody as ts . Expression ) ,
922+ ] ,
923+ true ,
924+ ) ;
925+ } ;
926+
927+ const destructuredProps = new Map < string , ( ) => ts . Expression > ( ) ;
928+ const computedInitializers : ts . VariableStatement [ ] = [ ] ;
929+ const usedTempNames = new Set < string > ( ) ;
930+
931+ const registerTempName = ( base : string ) : string => {
932+ let candidate = `__ct_${ base || "prop" } _key` ;
933+ let counter = 1 ;
934+ while ( usedTempNames . has ( candidate ) ) {
935+ candidate = `__ct_${ base || "prop" } _key_${ counter ++ } ` ;
936+ }
937+ usedTempNames . add ( candidate ) ;
938+ return candidate ;
939+ } ;
940+
902941 if ( elemName && ts . isObjectBindingPattern ( elemName ) ) {
903942 for ( const element of elemName . elements ) {
904943 if ( ts . isBindingElement ( element ) && ts . isIdentifier ( element . name ) ) {
905- destructuredProps . add ( element . name . text ) ;
944+ const alias = element . name . text ;
945+ const propertyName = element . propertyName ;
946+ usedTempNames . add ( alias ) ;
947+
948+ destructuredProps . set ( alias , ( ) => {
949+ const target = factory . createIdentifier ( "element" ) ;
950+
951+ if ( ! propertyName ) {
952+ return factory . createPropertyAccessExpression (
953+ target ,
954+ factory . createIdentifier ( alias ) ,
955+ ) ;
956+ }
957+
958+ if ( ts . isIdentifier ( propertyName ) ) {
959+ return factory . createPropertyAccessExpression (
960+ target ,
961+ factory . createIdentifier ( propertyName . text ) ,
962+ ) ;
963+ }
964+
965+ if (
966+ ts . isStringLiteral ( propertyName ) ||
967+ ts . isNumericLiteral ( propertyName )
968+ ) {
969+ return factory . createElementAccessExpression ( target , propertyName ) ;
970+ }
971+
972+ if ( ts . isComputedPropertyName ( propertyName ) ) {
973+ const tempName = registerTempName ( alias ) ;
974+ const tempIdentifier = factory . createIdentifier ( tempName ) ;
975+
976+ computedInitializers . push (
977+ factory . createVariableStatement (
978+ undefined ,
979+ factory . createVariableDeclarationList (
980+ [
981+ factory . createVariableDeclaration (
982+ tempIdentifier ,
983+ undefined ,
984+ undefined ,
985+ propertyName . expression ,
986+ ) ,
987+ ] ,
988+ ts . NodeFlags . Const ,
989+ ) ,
990+ ) ,
991+ ) ;
992+
993+ return factory . createElementAccessExpression (
994+ target ,
995+ tempIdentifier ,
996+ ) ;
997+ }
998+
999+ return factory . createPropertyAccessExpression (
1000+ target ,
1001+ factory . createIdentifier ( alias ) ,
1002+ ) ;
1003+ } ) ;
9061004 }
9071005 }
9081006 }
9091007
910- // Collect array destructured identifiers: [date, pizza] -> {date: 0, pizza: 1}
9111008 const arrayDestructuredVars = new Map < string , number > ( ) ;
9121009 if ( elemName && ts . isArrayBindingPattern ( elemName ) ) {
9131010 let index = 0 ;
@@ -919,35 +1016,28 @@ function transformDestructuredProperties(
9191016 }
9201017 }
9211018
922- // If param was object-destructured, replace property references with element.prop
9231019 if ( destructuredProps . size > 0 ) {
9241020 const visitor : ts . Visitor = ( node ) => {
9251021 if ( ts . isIdentifier ( node ) && destructuredProps . has ( node . text ) ) {
926- // Check if this identifier is not part of a property access already
927- // (e.g., don't transform the 'x' in 'something.x')
9281022 if (
9291023 ! node . parent ||
9301024 ! ( ts . isPropertyAccessExpression ( node . parent ) &&
9311025 node . parent . name === node )
9321026 ) {
933- return factory . createPropertyAccessExpression (
934- factory . createIdentifier ( "element" ) ,
935- factory . createIdentifier ( node . text ) ,
936- ) ;
1027+ const accessFactory = destructuredProps . get ( node . text ) ! ;
1028+ return accessFactory ( ) ;
9371029 }
9381030 }
9391031 return visitEachChildWithJsx ( node , visitor , undefined ) ;
9401032 } ;
941- return ts . visitNode ( body , visitor ) as ts . ConciseBody ;
1033+ transformedBody = ts . visitNode ( transformedBody , visitor ) as ts . ConciseBody ;
9421034 }
9431035
944- // If param was array-destructured, replace variable references with element[index]
9451036 if ( arrayDestructuredVars . size > 0 ) {
9461037 const visitor : ts . Visitor = ( node ) => {
9471038 if ( ts . isIdentifier ( node ) ) {
9481039 const index = arrayDestructuredVars . get ( node . text ) ;
9491040 if ( index !== undefined ) {
950- // Check if this identifier is not part of a property access already
9511041 if (
9521042 ! node . parent ||
9531043 ! ( ts . isPropertyAccessExpression ( node . parent ) &&
@@ -962,10 +1052,14 @@ function transformDestructuredProperties(
9621052 }
9631053 return visitEachChildWithJsx ( node , visitor , undefined ) ;
9641054 } ;
965- return ts . visitNode ( body , visitor ) as ts . ConciseBody ;
1055+ transformedBody = ts . visitNode ( transformedBody , visitor ) as ts . ConciseBody ;
9661056 }
9671057
968- return body ;
1058+ if ( computedInitializers . length > 0 ) {
1059+ transformedBody = prependStatements ( computedInitializers , transformedBody ) ;
1060+ }
1061+
1062+ return transformedBody ;
9691063}
9701064
9711065/**
@@ -1155,12 +1249,32 @@ function transformMapCallback(
11551249 const captureExpressions = collectCaptures ( callback , checker ) ;
11561250
11571251 // Build map of capture name -> expression
1158- const captures = new Map < string , ts . Expression > ( ) ;
1252+ const captureEntries : Array < { name : string ; expr : ts . Expression } > = [ ] ;
1253+ const usedNames = new Set < string > ( [ "element" , "index" , "array" , "params" ] ) ;
1254+
11591255 for ( const expr of captureExpressions ) {
1160- const name = getCaptureName ( expr ) ;
1161- if ( name && ! captures . has ( name ) ) {
1162- captures . set ( name , expr ) ;
1256+ const baseName = getCaptureName ( expr ) ;
1257+ if ( ! baseName ) continue ;
1258+
1259+ // Skip if an existing entry captures an equivalent expression
1260+ const existing = captureEntries . find ( ( entry ) =>
1261+ expressionsMatch ( expr , entry . expr )
1262+ ) ;
1263+ if ( existing ) continue ;
1264+
1265+ let candidate = baseName ;
1266+ let counter = 2 ;
1267+ while ( usedNames . has ( candidate ) ) {
1268+ candidate = `${ baseName } _${ counter ++ } ` ;
11631269 }
1270+
1271+ usedNames . add ( candidate ) ;
1272+ captureEntries . push ( { name : candidate , expr } ) ;
1273+ }
1274+
1275+ const captures = new Map < string , ts . Expression > ( ) ;
1276+ for ( const entry of captureEntries ) {
1277+ captures . set ( entry . name , entry . expr ) ;
11641278 }
11651279
11661280 // Build set of captured variable names
0 commit comments