11import * as ts from 'typescript' ;
22import {
3+ ArrayLiteralExpressionEditOptions ,
34 Identifier ,
45 ImportDeclarationMeta ,
6+ ObjectLiteralExpressionEditOptions ,
57 PropertyAssignment ,
68} from '../types' ;
79import { SIDE_EFFECTS_IMPORT_TEMPLATE_NAME } from '../util' ;
@@ -26,8 +28,8 @@ import { TypeScriptExpressionCollector } from './TypeScriptExpressionCollector';
2628export const newMemberInObjectLiteralTransformerFactory = (
2729 newProperty : ts . PropertyAssignment ,
2830 visitCondition : ( node : ts . Node ) => boolean ,
29- multiline : boolean ,
30- expressionCollector : TypeScriptExpressionCollector
31+ expressionCollector : TypeScriptExpressionCollector ,
32+ options : ObjectLiteralExpressionEditOptions
3133) : ts . TransformerFactory < ts . SourceFile > => {
3234 return < T extends ts . Node > ( context : ts . TransformationContext ) => {
3335 return ( rootNode : T ) => {
@@ -73,7 +75,8 @@ export const newMemberInObjectLiteralTransformerFactory = (
7375 context ,
7476 node ,
7577 expressionCollector ,
76- multiline
78+ options ?. multiline ,
79+ options ?. override
7780 ) ;
7881 }
7982
@@ -91,53 +94,14 @@ export const newMemberInObjectLiteralTransformerFactory = (
9194 } ;
9295} ;
9396
94- /**
95- * Creates a {@link ts.TransformerFactory} that updates a member in a {@link ts.ObjectLiteralExpression}.
96- */
97- export const updateForObjectLiteralMemberTransformerFactory = (
98- visitCondition : ( node : ts . ObjectLiteralExpression ) => boolean ,
99- targetMember : PropertyAssignment
100- ) : ts . TransformerFactory < ts . SourceFile > => {
101- return < T extends ts . Node > ( context : ts . TransformationContext ) => {
102- return ( rootNode : T ) => {
103- const visitor = ( node : ts . Node ) : ts . VisitResult < ts . Node > => {
104- if ( ts . isObjectLiteralExpression ( node ) && visitCondition ( node ) ) {
105- const newProperties = node . properties . map ( ( property ) => {
106- const isPropertyAssignment = ts . isPropertyAssignment ( property ) ;
107- if (
108- isPropertyAssignment &&
109- ts . isIdentifier ( property . name ) &&
110- property . name . text === targetMember . name
111- ) {
112- return context . factory . updatePropertyAssignment (
113- property ,
114- property . name ,
115- targetMember . value
116- ) ;
117- }
118- return property ;
119- } ) ;
120-
121- return context . factory . updateObjectLiteralExpression (
122- node ,
123- newProperties
124- ) ;
125- }
126- return ts . visitEachChild ( node , visitor , context ) ;
127- } ;
128- return ts . visitNode ( rootNode , visitor , ts . isSourceFile ) ;
129- } ;
130- } ;
131- } ;
132-
13397/**
13498 * Creates a {@link ts.TransformerFactory} that adds a new element to a {@link ts.ArrayLiteralExpression}.
13599 */
136100export const newMemberInArrayLiteralTransformerFactory = (
137101 visitCondition : ( node : ts . ArrayLiteralExpression ) => boolean ,
138102 elements : ts . Expression [ ] ,
139- prepend : boolean = false ,
140- anchorElement ?: ts . StringLiteral | ts . NumericLiteral | PropertyAssignment
103+ anchorElement ?: ts . StringLiteral | ts . NumericLiteral | PropertyAssignment ,
104+ options ?: ArrayLiteralExpressionEditOptions
141105) : ts . TransformerFactory < ts . SourceFile > => {
142106 return < T extends ts . Node > ( context : ts . TransformationContext ) => {
143107 return ( rootNode : T ) => {
@@ -175,9 +139,14 @@ export const newMemberInArrayLiteralTransformerFactory = (
175139 } ) ;
176140 }
177141
142+ /**
143+ * TODO:
144+ * Consider extracting some of the logic to the factory that handles array literals as property initializers and reusing that here.
145+ * The anchor element should be preserved while it should also allow for overriding of the elements, if needed.
146+ */
178147 if ( anchor ) {
179148 let structure ! : ts . Expression [ ] ;
180- if ( prepend ) {
149+ if ( options ?. prepend ) {
181150 structure = node . elements
182151 . slice ( 0 , node . elements . indexOf ( anchor ) )
183152 . concat ( elements )
@@ -195,7 +164,7 @@ export const newMemberInArrayLiteralTransformerFactory = (
195164 ) ;
196165 }
197166
198- if ( prepend ) {
167+ if ( options ?. prepend ) {
199168 return context . factory . updateArrayLiteralExpression ( node , [
200169 ...elements ,
201170 ...node . elements ,
@@ -213,6 +182,27 @@ export const newMemberInArrayLiteralTransformerFactory = (
213182 } ;
214183} ;
215184
185+ /**
186+ * Creates a {@link ts.TransformerFactory} that sorts the elements in a {@link ts.ArrayLiteralExpression}.
187+ */
188+ export const sortInArrayLiteralTransformerFactory = (
189+ visitCondition : ( node : ts . ArrayLiteralExpression ) => boolean ,
190+ sortCondition : ( a : ts . Expression , b : ts . Expression ) => number
191+ ) => {
192+ return < T extends ts . Node > ( context : ts . TransformationContext ) => {
193+ return ( rootNode : T ) => {
194+ const visitor = ( node : ts . Node ) : ts . VisitResult < ts . Node > => {
195+ if ( ts . isArrayLiteralExpression ( node ) && visitCondition ( node ) ) {
196+ const elements = [ ...node . elements ] . sort ( sortCondition ) ;
197+ return context . factory . updateArrayLiteralExpression ( node , elements ) ;
198+ }
199+ return ts . visitEachChild ( node , visitor , context ) ;
200+ } ;
201+ return ts . visitNode ( rootNode , visitor , ts . isSourceFile ) ;
202+ } ;
203+ } ;
204+ } ;
205+
216206/**
217207 * Creates a {@link ts.TransformerFactory} that adds a new argument to a {@link ts.CallExpression}.
218208 */
@@ -488,29 +478,7 @@ function updatePropertyAssignmentWithIdentifier(
488478 ? newProperty . initializer
489479 : newProperty . objectAssignmentInitializer ;
490480
491- const updatedProperty = ts . isPropertyAssignment ( existingProperty )
492- ? context . factory . updatePropertyAssignment (
493- existingProperty ,
494- existingProperty . name ,
495- newPropInitializer
496- )
497- : context . factory . updateShorthandPropertyAssignment (
498- existingProperty ,
499- existingProperty . name ,
500- newPropInitializer
501- ) ;
502- const structure = Array . from ( node . properties ) ;
503- const targetIndex = structure . indexOf ( existingProperty ) ;
504- if ( targetIndex > - 1 ) {
505- // attempt to modify the property assignment and preserve the order
506- structure [ targetIndex ] = updatedProperty ;
507- return context . factory . updateObjectLiteralExpression ( node , structure ) ;
508- }
509- // append the property assignment at the end
510- return context . factory . updateObjectLiteralExpression ( node , [
511- ...node . properties . filter ( ( p ) => p !== existingProperty ) ,
512- updatedProperty ,
513- ] ) ;
481+ return updateProperty ( node , existingProperty , newPropInitializer , context ) ;
514482}
515483
516484/**
@@ -520,14 +488,16 @@ function updatePropertyAssignmentWithIdentifier(
520488 * @param context The transformation context.
521489 * @param node The object literal expression node.
522490 * @param multiline Whether the array literal should be multiline.
491+ * @param override Whether to override all elements if the property's initializer is an array.
523492 */
524493function updatePropertyAssignmentWithArrayLiteral (
525494 newProperty : ts . PropertyAssignment | ts . ShorthandPropertyAssignment ,
526495 existingProperty : ts . PropertyAssignment | ts . ShorthandPropertyAssignment ,
527496 context : ts . TransformationContext ,
528497 node : ts . ObjectLiteralExpression ,
529498 expressionCollector : TypeScriptExpressionCollector ,
530- multiline : boolean
499+ multiline : boolean ,
500+ override : boolean
531501) : ts . ObjectLiteralExpression {
532502 const existingPropInitializer = ts . isPropertyAssignment ( existingProperty )
533503 ? existingProperty . initializer
@@ -543,25 +513,44 @@ function updatePropertyAssignmentWithArrayLiteral(
543513 const newElements = ts . isArrayLiteralExpression ( newPropInitializer )
544514 ? [ ...newPropInitializer . elements ]
545515 : [ newPropInitializer ] ;
546- const uniqueElements = expressionCollector . collectUniqueExpressions ( [
547- ...elements ,
548- ...newElements ,
549- ] ) ;
516+ const uniqueElements = override
517+ ? expressionCollector . collectUniqueExpressions ( newElements )
518+ : expressionCollector . collectUniqueExpressions ( [
519+ ...elements ,
520+ ...newElements ,
521+ ] ) ;
550522
551523 const valueExpression = context . factory . createArrayLiteralExpression (
552524 uniqueElements ,
553525 multiline
554526 ) ;
527+
528+ return updateProperty ( node , existingProperty , valueExpression , context ) ;
529+ }
530+
531+ /**
532+ * Updates a {@link ts.PropertyAssignment} with a new {@link ts.Initializer}.
533+ * @param node The object literal expression node.
534+ * @param existingProperty The property to update.
535+ * @param newInitializer The new initializer to set.
536+ * @param context The transformation context.
537+ */
538+ function updateProperty (
539+ node : ts . ObjectLiteralExpression ,
540+ existingProperty : ts . PropertyAssignment | ts . ShorthandPropertyAssignment ,
541+ newInitializer : ts . Expression ,
542+ context : ts . TransformationContext
543+ ) : ts . ObjectLiteralExpression {
555544 const updatedProperty = ts . isPropertyAssignment ( existingProperty )
556545 ? context . factory . updatePropertyAssignment (
557546 existingProperty ,
558547 existingProperty . name ,
559- valueExpression
548+ newInitializer
560549 )
561550 : context . factory . updateShorthandPropertyAssignment (
562551 existingProperty ,
563552 existingProperty . name ,
564- valueExpression
553+ newInitializer
565554 ) ;
566555
567556 const structure = Array . from ( node . properties ) ;
0 commit comments