1
1
import * as CompilerDOM from '@vue/compiler-dom' ;
2
2
import { camelize , capitalize } from '@vue/shared' ;
3
- import type * as ts from 'typescript' ;
4
- import { getNodeText } from '../../parsers/scriptSetupRanges' ;
5
3
import type { Code , VueCodeInformation } from '../../types' ;
6
4
import { hyphenateTag } from '../../utils/shared' ;
7
5
import { createVBindShorthandInlayHintInfo } from '../inlayHints' ;
8
- import { collectVars , createTsAst , endOfLine , newLine , variableNameRegex , wrapWith } from '../utils' ;
6
+ import { collectVars , createTsAst , endOfLine , newLine , normalizeAttributeValue , variableNameRegex , wrapWith } from '../utils' ;
9
7
import { generateCamelized } from '../utils/camelized' ;
10
8
import type { TemplateCodegenContext } from './context' ;
11
9
import { generateElementChildren } from './elementChildren' ;
@@ -16,6 +14,7 @@ import type { TemplateCodegenOptions } from './index';
16
14
import { generateInterpolation } from './interpolation' ;
17
15
import { generateObjectProperty } from './objectProperty' ;
18
16
import { generatePropertyAccess } from './propertyAccess' ;
17
+ import { collectStyleScopedClassReferences } from './styleScopedClasses' ;
19
18
import { generateTemplateChild } from './templateChild' ;
20
19
21
20
const colonReg = / : / g;
@@ -420,7 +419,7 @@ function* generateVScope(
420
419
421
420
yield * generateElementDirectives ( options , ctx , node ) ;
422
421
const [ refName , offset ] = yield * generateReferencesForElements ( options , ctx , node ) ; // <el ref="foo" />
423
- yield * generateReferencesForScopedCssClasses ( options , ctx , node ) ;
422
+ collectStyleScopedClassReferences ( options , ctx , node ) ;
424
423
425
424
if ( inScope ) {
426
425
yield `}${ newLine } ` ;
@@ -618,193 +617,10 @@ function* generateReferencesForElements(
618
617
return [ ] ;
619
618
}
620
619
621
- function * generateReferencesForScopedCssClasses (
622
- options : TemplateCodegenOptions ,
623
- ctx : TemplateCodegenContext ,
624
- node : CompilerDOM . ElementNode
625
- ) : Generator < Code > {
626
- for ( const prop of node . props ) {
627
- if (
628
- prop . type === CompilerDOM . NodeTypes . ATTRIBUTE
629
- && prop . name === 'class'
630
- && prop . value
631
- ) {
632
- if ( options . template . lang === 'pug' ) {
633
- const getClassOffset = Reflect . get ( prop . value . loc . start , 'getClassOffset' ) as ( offset : number ) => number ;
634
- const content = prop . value . loc . source . slice ( 1 , - 1 ) ;
635
-
636
- let startOffset = 1 ;
637
- for ( const className of content . split ( ' ' ) ) {
638
- if ( className ) {
639
- ctx . scopedClasses . push ( {
640
- source : 'template' ,
641
- className,
642
- offset : getClassOffset ( startOffset ) ,
643
- } ) ;
644
- }
645
- startOffset += className . length + 1 ;
646
- }
647
- }
648
- else {
649
- let isWrapped = false ;
650
- const [ content , startOffset ] = normalizeAttributeValue ( prop . value ) ;
651
- if ( content ) {
652
- const classes = collectClasses ( content , startOffset + ( isWrapped ? 1 : 0 ) ) ;
653
- ctx . scopedClasses . push ( ...classes ) ;
654
- }
655
- else {
656
- ctx . emptyClassOffsets . push ( startOffset ) ;
657
- }
658
- }
659
- }
660
- else if (
661
- prop . type === CompilerDOM . NodeTypes . DIRECTIVE
662
- && prop . arg ?. type === CompilerDOM . NodeTypes . SIMPLE_EXPRESSION
663
- && prop . exp ?. type === CompilerDOM . NodeTypes . SIMPLE_EXPRESSION
664
- && prop . arg . content === 'class'
665
- ) {
666
- const content = '`${' + prop . exp . content + '}`' ;
667
- const startOffset = prop . exp . loc . start . offset - 3 ;
668
-
669
- const { ts } = options ;
670
- const ast = ts . createSourceFile ( '' , content , 99 satisfies ts . ScriptTarget . Latest ) ;
671
- const literals : ts . StringLiteralLike [ ] = [ ] ;
672
-
673
- ts . forEachChild ( ast , node => {
674
- if (
675
- ! ts . isExpressionStatement ( node ) ||
676
- ! isTemplateExpression ( node . expression )
677
- ) {
678
- return ;
679
- }
680
-
681
- const expression = node . expression . templateSpans [ 0 ] . expression ;
682
-
683
- if ( ts . isStringLiteralLike ( expression ) ) {
684
- literals . push ( expression ) ;
685
- }
686
-
687
- if ( ts . isArrayLiteralExpression ( expression ) ) {
688
- walkArrayLiteral ( expression ) ;
689
- }
690
-
691
- if ( ts . isObjectLiteralExpression ( expression ) ) {
692
- walkObjectLiteral ( expression ) ;
693
- }
694
- } ) ;
695
-
696
- for ( const literal of literals ) {
697
- if ( literal . text ) {
698
- const classes = collectClasses (
699
- literal . text ,
700
- literal . end - literal . text . length - 1 + startOffset
701
- ) ;
702
- ctx . scopedClasses . push ( ...classes ) ;
703
- }
704
- else {
705
- ctx . emptyClassOffsets . push ( literal . end - 1 + startOffset ) ;
706
- }
707
- }
708
-
709
- function walkArrayLiteral ( node : ts . ArrayLiteralExpression ) {
710
- const { elements } = node ;
711
- for ( const element of elements ) {
712
- if ( ts . isStringLiteralLike ( element ) ) {
713
- literals . push ( element ) ;
714
- }
715
- else if ( ts . isObjectLiteralExpression ( element ) ) {
716
- walkObjectLiteral ( element ) ;
717
- }
718
- }
719
- }
720
-
721
- function walkObjectLiteral ( node : ts . ObjectLiteralExpression ) {
722
- const { properties } = node ;
723
- for ( const property of properties ) {
724
- if ( ts . isPropertyAssignment ( property ) ) {
725
- const { name } = property ;
726
- if ( ts . isIdentifier ( name ) ) {
727
- walkIdentifier ( name ) ;
728
- }
729
- else if ( ts . isStringLiteral ( name ) ) {
730
- literals . push ( name ) ;
731
- }
732
- else if ( ts . isComputedPropertyName ( name ) ) {
733
- const { expression } = name ;
734
- if ( ts . isStringLiteralLike ( expression ) ) {
735
- literals . push ( expression ) ;
736
- }
737
- }
738
- }
739
- else if ( ts . isShorthandPropertyAssignment ( property ) ) {
740
- walkIdentifier ( property . name ) ;
741
- }
742
- }
743
- }
744
-
745
- function walkIdentifier ( node : ts . Identifier ) {
746
- const text = getNodeText ( ts , node , ast ) ;
747
- ctx . scopedClasses . push ( {
748
- source : 'template' ,
749
- className : text ,
750
- offset : node . end - text . length + startOffset
751
- } ) ;
752
- }
753
- }
754
- }
755
- }
756
-
757
620
function camelizeComponentName ( newName : string ) {
758
621
return camelize ( '-' + newName ) ;
759
622
}
760
623
761
624
function getTagRenameApply ( oldName : string ) {
762
625
return oldName === hyphenateTag ( oldName ) ? hyphenateTag : undefined ;
763
626
}
764
-
765
- function normalizeAttributeValue ( node : CompilerDOM . TextNode ) : [ string , number ] {
766
- let offset = node . loc . start . offset ;
767
- let content = node . loc . source ;
768
- if (
769
- ( content . startsWith ( `'` ) && content . endsWith ( `'` ) )
770
- || ( content . startsWith ( `"` ) && content . endsWith ( `"` ) )
771
- ) {
772
- offset ++ ;
773
- content = content . slice ( 1 , - 1 ) ;
774
- }
775
- return [ content , offset ] ;
776
- }
777
-
778
- function collectClasses ( content : string , startOffset = 0 ) {
779
- const classes : {
780
- source : string ;
781
- className : string ;
782
- offset : number ;
783
- } [ ] = [ ] ;
784
-
785
- let currentClassName = '' ;
786
- let offset = 0 ;
787
- for ( const char of ( content + ' ' ) ) {
788
- if ( char . trim ( ) === '' ) {
789
- if ( currentClassName !== '' ) {
790
- classes . push ( {
791
- source : 'template' ,
792
- className : currentClassName ,
793
- offset : offset + startOffset
794
- } ) ;
795
- offset += currentClassName . length ;
796
- currentClassName = '' ;
797
- }
798
- offset += char . length ;
799
- }
800
- else {
801
- currentClassName += char ;
802
- }
803
- }
804
- return classes ;
805
- }
806
-
807
- // isTemplateExpression is missing in tsc
808
- function isTemplateExpression ( node : ts . Node ) : node is ts . TemplateExpression {
809
- return node . kind === 228 satisfies ts . SyntaxKind . TemplateExpression ;
810
- }
0 commit comments