1
- import { Expression , Statement , Node , NodePath } from './types'
1
+ import {
2
+ Expression ,
3
+ Statement ,
4
+ Node ,
5
+ NodePath ,
6
+ ImportDeclaration ,
7
+ } from './types'
2
8
import { Backend } from './backend/Backend'
3
9
import find , { Match , convertWithCaptures , createMatch } from './find'
4
10
import replace from './replace'
@@ -14,6 +20,11 @@ import {
14
20
} from './compileMatcher/Placeholder'
15
21
import { SimpleReplacementInterface } from './util/SimpleReplacementCollector'
16
22
import forEachNode from './util/forEachNode'
23
+ import addImports from './util/addImports'
24
+ import findImports from './util/findImports'
25
+ import removeImports from './util/removeImports'
26
+ import createReplacementConverter from './convertReplacement'
27
+ import compileReplacement , { CompiledReplacement } from './compileReplacement'
17
28
18
29
export type TransformOptions = {
19
30
/** The absolute path to the current file. */
@@ -254,11 +265,12 @@ export default class Astx extends ExtendableProxy implements Iterable<Astx> {
254
265
}
255
266
256
267
withCaptures (
257
- ...captures : (
258
- | Match
259
- | Astx
260
- | { [ name : `$${string } ` | `$$${string } ` | `$$$${string } `] : Astx }
261
- ) [ ]
268
+ ...captures :
269
+ | (
270
+ | Match
271
+ | Astx
272
+ | { [ name : `$${string } ` | `$$${string } ` | `$$$${string } `] : Astx }
273
+ ) [ ]
262
274
) : Astx {
263
275
const withCaptures : Match [ ] = [ ...this . _withCaptures ]
264
276
for ( const item of captures ) {
@@ -331,12 +343,12 @@ export default class Astx extends ExtendableProxy implements Iterable<Astx> {
331
343
)
332
344
}
333
345
334
- private _execPattern < Options > (
346
+ private _execPattern < Options , Return > (
335
347
name : string ,
336
348
exec : (
337
349
pattern : NodePath < Node , any > | readonly NodePath < Node , any > [ ] ,
338
350
options ?: Options
339
- ) => Astx ,
351
+ ) => Return ,
340
352
arg0 :
341
353
| string
342
354
| Node
@@ -346,7 +358,7 @@ export default class Astx extends ExtendableProxy implements Iterable<Astx> {
346
358
| string [ ]
347
359
| TemplateStringsArray ,
348
360
...rest : any [ ]
349
- ) : Astx | ( ( options ?: Options ) => Astx ) {
361
+ ) : Return | ( ( options ?: Options ) => Return ) {
350
362
const { backend } = this
351
363
const { parsePattern } = backend
352
364
const { NodePath } = backend . t
@@ -382,9 +394,9 @@ export default class Astx extends ExtendableProxy implements Iterable<Astx> {
382
394
}
383
395
}
384
396
385
- private _execPatternOrPredicate < Options > (
397
+ private _execPatternOrPredicate < Options , Return > (
386
398
name : string ,
387
- exec : ( match : CompiledMatcher [ 'match' ] , options ?: Options ) => Astx ,
399
+ exec : ( match : CompiledMatcher [ 'match' ] , options ?: Options ) => Return ,
388
400
arg0 :
389
401
| string
390
402
| Node
@@ -395,7 +407,7 @@ export default class Astx extends ExtendableProxy implements Iterable<Astx> {
395
407
| TemplateStringsArray
396
408
| FindPredicate ,
397
409
...rest : any [ ]
398
- ) : Astx | ( ( options ?: Options ) => Astx ) {
410
+ ) : Return | ( ( options ?: Options ) => Return ) {
399
411
const { backend } = this
400
412
if ( arg0 instanceof Function ) {
401
413
const predicate = arg0
@@ -647,4 +659,221 @@ export default class Astx extends ExtendableProxy implements Iterable<Astx> {
647
659
remove ( ) : void {
648
660
this . replace ( [ ] )
649
661
}
662
+
663
+ addImports (
664
+ strings : string [ ] | TemplateStringsArray ,
665
+ ...quasis : any [ ]
666
+ ) : ( ) => Astx
667
+ addImports (
668
+ pattern : string | Node | Node [ ] | NodePath < any > | NodePath < any > [ ]
669
+ ) : Astx
670
+ addImports (
671
+ arg0 :
672
+ | string
673
+ | Node
674
+ | Node [ ]
675
+ | NodePath < any >
676
+ | NodePath < any > [ ]
677
+ | string [ ]
678
+ | TemplateStringsArray ,
679
+ ...rest : any [ ]
680
+ ) : Astx | ( ( ) => Astx ) {
681
+ return this . _execPattern (
682
+ 'addImports' ,
683
+ ( pattern : NodePath < Node , any > | readonly NodePath < Node , any > [ ] ) : Astx =>
684
+ addImports ( this , Array . isArray ( pattern ) ? pattern : [ pattern ] ) ,
685
+ arg0 ,
686
+ ...rest
687
+ )
688
+ }
689
+
690
+ findImports (
691
+ strings : string [ ] | TemplateStringsArray ,
692
+ ...quasis : any [ ]
693
+ ) : ( ) => Astx
694
+ findImports (
695
+ pattern : string | Node | Node [ ] | NodePath < any > | NodePath < any > [ ]
696
+ ) : Astx
697
+ findImports (
698
+ arg0 :
699
+ | string
700
+ | Node
701
+ | Node [ ]
702
+ | NodePath < any >
703
+ | NodePath < any > [ ]
704
+ | string [ ]
705
+ | TemplateStringsArray ,
706
+ ...rest : any [ ]
707
+ ) : Astx | ( ( ) => Astx ) {
708
+ return this . _execPattern (
709
+ 'findImports' ,
710
+ ( pattern : NodePath < Node , any > | readonly NodePath < Node , any > [ ] ) : Astx =>
711
+ findImports ( this , Array . isArray ( pattern ) ? pattern : [ pattern ] ) ,
712
+ arg0 ,
713
+ ...rest
714
+ )
715
+ }
716
+
717
+ removeImports (
718
+ strings : string [ ] | TemplateStringsArray ,
719
+ ...quasis : any [ ]
720
+ ) : ( ) => boolean
721
+ removeImports (
722
+ pattern : string | Node | Node [ ] | NodePath < any > | NodePath < any > [ ]
723
+ ) : boolean
724
+ removeImports (
725
+ arg0 :
726
+ | string
727
+ | Node
728
+ | Node [ ]
729
+ | NodePath < any >
730
+ | NodePath < any > [ ]
731
+ | string [ ]
732
+ | TemplateStringsArray ,
733
+ ...rest : any [ ]
734
+ ) : boolean | ( ( ) => boolean ) {
735
+ return this . _execPattern (
736
+ 'removeImports' ,
737
+ (
738
+ pattern : NodePath < Node , any > | readonly NodePath < Node , any > [ ]
739
+ ) : boolean =>
740
+ removeImports ( this , Array . isArray ( pattern ) ? pattern : [ pattern ] ) ,
741
+ arg0 ,
742
+ ...rest
743
+ )
744
+ }
745
+
746
+ replaceImport (
747
+ strings : string [ ] | TemplateStringsArray ,
748
+ ...quasis : any [ ]
749
+ ) : ( ) => ImportReplacer
750
+ replaceImport (
751
+ pattern : string | Node | Node [ ] | NodePath < any > | NodePath < any > [ ]
752
+ ) : ImportReplacer
753
+ replaceImport (
754
+ arg0 :
755
+ | string
756
+ | Node
757
+ | Node [ ]
758
+ | NodePath < any >
759
+ | NodePath < any > [ ]
760
+ | string [ ]
761
+ | TemplateStringsArray ,
762
+ ...rest : any [ ]
763
+ ) : ImportReplacer | ( ( ) => ImportReplacer ) {
764
+ return this . _execPattern (
765
+ 'replaceImport' ,
766
+ (
767
+ _pattern : NodePath < Node , any > | readonly NodePath < Node , any > [ ]
768
+ ) : ImportReplacer => {
769
+ const pattern = Array . isArray ( _pattern ) ? _pattern : [ _pattern ]
770
+ if (
771
+ pattern . length !== 1 ||
772
+ pattern [ 0 ] . node . type !== 'ImportDeclaration'
773
+ ) {
774
+ throw new Error ( `pattern must contain exactly one import declaration` )
775
+ }
776
+ const decl : ImportDeclaration = pattern [ 0 ] ?. node as any
777
+ if ( decl . specifiers && decl . specifiers . length > 1 ) {
778
+ throw new Error (
779
+ `pattern may not contain more than one import specifier`
780
+ )
781
+ }
782
+ const found = findImports ( this , pattern )
783
+ return new ImportReplacer ( this , found , pattern )
784
+ } ,
785
+ arg0 ,
786
+ ...rest
787
+ )
788
+ }
789
+ }
790
+
791
+ class ImportReplacer {
792
+ constructor (
793
+ public astx : Astx ,
794
+ public match : Astx ,
795
+ public findPattern : readonly NodePath < Node , any > [ ]
796
+ ) { }
797
+
798
+ with (
799
+ strings : string [ ] | TemplateStringsArray ,
800
+ ...quasis : any [ ]
801
+ ) : ( ) => boolean
802
+ with (
803
+ pattern :
804
+ | string
805
+ | Node
806
+ | Node [ ]
807
+ | NodePath < any >
808
+ | NodePath < any > [ ]
809
+ | GetReplacement
810
+ ) : boolean
811
+ with (
812
+ arg0 :
813
+ | string
814
+ | Node
815
+ | Node [ ]
816
+ | NodePath < any >
817
+ | NodePath < any > [ ]
818
+ | GetReplacement
819
+ | string [ ]
820
+ | TemplateStringsArray ,
821
+ ...rest : any [ ]
822
+ ) : boolean | ( ( ) => boolean ) {
823
+ const { backend } = this . astx
824
+ const { parsePatternToNodes } = backend
825
+
826
+ const doReplace = ( rawReplacement : any ) : boolean => {
827
+ if ( ! this . match . matched ) return false
828
+ const match = this . match . match
829
+ const path =
830
+ match . path . parentPath ?. node ?. type === 'ExpressionStatement'
831
+ ? match . path . parentPath
832
+ : match . path
833
+ const converter = createReplacementConverter ( path )
834
+ const generated = (
835
+ rawReplacement instanceof Object &&
836
+ typeof ( rawReplacement as any ) . generate === 'function'
837
+ ? ( rawReplacement as CompiledReplacement )
838
+ : compileReplacement (
839
+ Array . isArray ( rawReplacement )
840
+ ? rawReplacement . map ( ( n ) => new backend . t . NodePath ( n ) )
841
+ : new backend . t . NodePath ( rawReplacement ) ,
842
+ { backend }
843
+ )
844
+ ) . generate ( match )
845
+
846
+ const converted = converter (
847
+ Array . isArray ( generated ) ? generated [ 0 ] : generated
848
+ )
849
+
850
+ removeImports ( this . astx , this . findPattern )
851
+ this . astx . addImports ( converted )
852
+ return true
853
+ }
854
+
855
+ try {
856
+ if ( typeof arg0 === 'function' ) {
857
+ // Always replace in reverse so that if there are matches inside of
858
+ // matches, the inner matches get replaced first (since they come
859
+ // later in the code)
860
+ return doReplace ( ( arg0 as any ) ( this . match , parsePatternToNodes ) )
861
+ } else if ( typeof arg0 === 'string' ) {
862
+ return doReplace ( parsePatternToNodes ( arg0 ) )
863
+ } else if ( isNode ( arg0 ) || isNodeArray ( arg0 ) ) {
864
+ return doReplace ( arg0 )
865
+ } else {
866
+ const rawReplacement = parsePatternToNodes ( arg0 as any , ...rest )
867
+ return ( ) => doReplace ( rawReplacement )
868
+ }
869
+ } catch ( error ) {
870
+ if ( error instanceof Error ) {
871
+ CodeFrameError . rethrow ( error , {
872
+ filename : 'replace pattern' ,
873
+ source : typeof arg0 === 'string' ? arg0 : undefined ,
874
+ } )
875
+ }
876
+ throw error
877
+ }
878
+ }
650
879
}
0 commit comments