@@ -34,6 +34,11 @@ function isImplicitThis(node: angular.AST, text: string): boolean {
3434 return start >= end || / ^ \s + $ / . test ( text . slice ( start , end ) ) ;
3535}
3636
37+ type NodeTransformOptions = {
38+ isInParentParens ?: boolean ;
39+ parent ?: angular . AST ;
40+ } ;
41+
3742class Transformer extends Source {
3843 #node;
3944 #text;
@@ -105,12 +110,19 @@ class Transformer extends Source {
105110 ) ;
106111 }
107112
108- #transform< T extends NGNode > ( node : angular . AST , isInParentParens = false ) {
109- return this . #transformNode( node , isInParentParens ) as T &
110- LocationInformation ;
113+ #transform< T extends NGNode > (
114+ node : angular . AST ,
115+ options ?: NodeTransformOptions ,
116+ ) {
117+ return this . #transformNode( node , options ) as T & LocationInformation ;
111118 }
112119
113- #transformNode( node : angular . AST , isInParentParens = false ) : NGNode {
120+ #transformNode( node : angular . AST , options ?: NodeTransformOptions ) : NGNode {
121+ const { isInParentParens } = {
122+ isInParentParens : false ,
123+ ...options ,
124+ } ;
125+
114126 if ( node instanceof angular . Interpolation ) {
115127 const { expressions } = node ;
116128
@@ -374,7 +386,11 @@ class Transformer extends Source {
374386 const { receiver, args } = node ;
375387 const tArgs =
376388 args . length === 1
377- ? [ this . #transform< babel . Expression > ( args [ 0 ] , true ) ]
389+ ? [
390+ this . #transform< babel . Expression > ( args [ 0 ] , {
391+ isInParentParens : true ,
392+ } ) ,
393+ ]
378394 : ( args as angular . AST [ ] ) . map < babel . Expression > ( ( node ) =>
379395 this . #transform( node ) ,
380396 ) ;
@@ -392,7 +408,7 @@ class Transformer extends Source {
392408 optional :
393409 nodeType === 'OptionalCallExpression' ? isOptionalType : undefined ,
394410 start : getOuterStart ( tReceiver ) ,
395- end : node . sourceSpan . end , // )
411+ end : node . sourceSpan . end , // `)`
396412 } ,
397413 { hasParentParens : isInParentParens } ,
398414 ) ;
@@ -405,7 +421,7 @@ class Transformer extends Source {
405421 type : 'TSNonNullExpression' ,
406422 expression : expression ,
407423 start : getOuterStart ( expression ) ,
408- end : node . sourceSpan . end , // !
424+ end : node . sourceSpan . end , // `!`
409425 } ,
410426 { hasParentParens : isInParentParens } ,
411427 ) ;
@@ -523,35 +539,88 @@ class Transformer extends Source {
523539 ) ;
524540 }
525541
542+ if ( node instanceof angular . TemplateLiteral ) {
543+ const { elements, expressions } = node ;
544+
545+ return this . #create< babel . TemplateLiteral > ( {
546+ type : 'TemplateLiteral' ,
547+ quasis : elements . map ( ( element ) =>
548+ this . #transform( element , { parent : node } ) ,
549+ ) ,
550+ expressions : expressions . map ( ( expression ) =>
551+ this . #transform( expression ) ,
552+ ) ,
553+ ...node . sourceSpan ,
554+ } ) ;
555+ }
556+
557+ if ( node instanceof angular . TemplateLiteralElement ) {
558+ const templateLiteral = options ! . parent ! as angular . TemplateLiteral ;
559+ const elementIndex = templateLiteral . elements . indexOf ( node ) ;
560+ const isFirst = elementIndex === 0 ;
561+ const isLast = elementIndex === templateLiteral . elements . length - 1 ;
562+
563+ // The `TemplateLiteralElement` don't have correct location information
564+ const start = isFirst
565+ ? templateLiteral . sourceSpan . start + 1
566+ : node . sourceSpan . start ;
567+ let end ;
568+ if ( isLast ) {
569+ end = templateLiteral . sourceSpan . end - 1 ;
570+ } else {
571+ const nextExpression = templateLiteral . expressions [ elementIndex ] ;
572+ // TODO: Support search multiple characters in `getCharacterLastIndex()`
573+ // FIXME: Search `${` instead
574+ end = this . getCharacterLastIndex ( '$' , nextExpression . sourceSpan . start ) ;
575+ }
576+ const raw = this . text . slice ( start , end ) ;
577+
578+ return this . #create< babel . TemplateElement > (
579+ {
580+ type : 'TemplateElement' ,
581+ value : {
582+ cooked : node . text ,
583+ raw,
584+ } ,
585+ start : start ,
586+ end : end ,
587+ tail : isLast ,
588+ } ,
589+ { stripSpaces : false } ,
590+ ) ;
591+ }
592+
526593 // istanbul ignore next
527- throw Object . assign ( new Error ( ' Unexpected node' ) , { node } ) ;
594+ throw new Error ( ` Unexpected node type ' ${ node . constructor . name } '` ) ;
528595 }
529596}
530597
531598// See `convertAst` in `@angular/compiler`
532- // ASTWithSource (Not handled)
533- // PropertyRead
534- // PropertyWrite
535- // KeyedWrite
536- // Call
537- // LiteralPrimitive
538- // Unary
539- // Binary
540- // ThisReceiver (Not handled)
541- // KeyedRead
542- // Chain
543- // LiteralMap
544- // LiteralArray
545- // Conditional
546- // NonNullAssert
547- // BindingPipe
548- // SafeKeyedRead
549- // SafePropertyRead
550- // SafeCall
551- // EmptyExpr
552- // PrefixNot
553- // TypeofExpression
554- function transform ( node : angular . AST , text : string ) : NGNode {
599+ type SupportedNodes =
600+ | angular . ASTWithSource // Not handled
601+ | angular . PropertyRead
602+ | angular . PropertyWrite
603+ | angular . KeyedWrite
604+ | angular . Call
605+ | angular . LiteralPrimitive
606+ | angular . Unary
607+ | angular . Binary
608+ | angular . ThisReceiver // Not handled
609+ | angular . KeyedRead
610+ | angular . Chain
611+ | angular . LiteralMap
612+ | angular . LiteralArray
613+ | angular . Conditional
614+ | angular . NonNullAssert
615+ | angular . BindingPipe
616+ | angular . SafeKeyedRead
617+ | angular . SafePropertyRead
618+ | angular . SafeCall
619+ | angular . EmptyExpr
620+ | angular . PrefixNot
621+ | angular . TypeofExpression
622+ | angular . TemplateLiteral ; // Including `TemplateLiteralElement`
623+ function transform ( node : SupportedNodes , text : string ) : NGNode {
555624 return new Transformer ( node , text ) . node ;
556625}
557626
0 commit comments