@@ -51,15 +51,8 @@ impl<'a> VuePartialLoader<'a> {
5151 let content = & self . source_text [ * pointer..* pointer + offset] ;
5252
5353 // parse `lang`
54- let lang = content. split_once ( "lang" ) . map_or ( Some ( "mjs" ) , |( _, s) | {
55- const QUOTES : [ char ; 2 ] = [ '"' , '\'' ] ;
56- s. trim_start ( )
57- . trim_start_matches ( '=' )
58- . trim_start ( )
59- . trim_start_matches ( QUOTES )
60- . split_once ( QUOTES )
61- . map ( |( s, _) | s)
62- } ) ?;
54+ let lang = Self :: extract_lang_attribute ( content) ;
55+
6356 let Ok ( mut source_type) = SourceType :: from_extension ( lang) else { return None } ;
6457 if !lang. contains ( 'x' ) {
6558 source_type = source_type. with_standard ( true ) ;
@@ -79,6 +72,43 @@ impl<'a> VuePartialLoader<'a> {
7972 #[ expect( clippy:: cast_possible_truncation) ]
8073 Some ( JavaScriptSource :: partial ( source_text, source_type, js_start as u32 ) )
8174 }
75+
76+ fn extract_lang_attribute ( content : & str ) -> & str {
77+ let content = content. trim ( ) ;
78+
79+ let Some ( lang_index) = content. find ( "lang" ) else { return "mjs" } ;
80+
81+ // Move past "lang"
82+ let mut rest = content[ lang_index + 4 ..] . trim_start ( ) ;
83+
84+ if !rest. starts_with ( '=' ) {
85+ return "mjs" ;
86+ }
87+
88+ // Move past "="
89+ rest = rest[ 1 ..] . trim_start ( ) ;
90+
91+ let first_char = rest. chars ( ) . next ( ) ;
92+
93+ match first_char {
94+ Some ( '"' | '\'' ) => {
95+ let quote = first_char. unwrap ( ) ;
96+ rest = & rest[ 1 ..] ;
97+ match rest. find ( quote) {
98+ Some ( end) => & rest[ ..end] ,
99+ None => "mjs" , // Unterminated quote
100+ }
101+ }
102+ Some ( _) => {
103+ // Unquoted value: take until first whitespace or attribute separator
104+ match rest. find ( |c : char | c. is_whitespace ( ) || c == '>' ) {
105+ Some ( end) => & rest[ ..end] ,
106+ None => rest, // whole rest is the lang value
107+ }
108+ }
109+ None => "mjs" , // nothing after =
110+ }
111+ }
82112}
83113
84114#[ cfg( test) ]
@@ -257,11 +287,11 @@ mod test {
257287 ( "<script>debugger</script>" , Some ( SourceType :: mjs ( ) ) ) ,
258288 ( "<script lang = 'tsx' >debugger</script>" , Some ( SourceType :: tsx ( ) ) ) ,
259289 ( r#"<script lang = "cjs" >debugger</script>"# , Some ( SourceType :: cjs ( ) ) ) ,
290+ ( "<script lang=tsx>debugger</script>" , Some ( SourceType :: tsx ( ) ) ) ,
260291 ( "<script lang = 'xxx'>debugger</script>" , None ) ,
261292 ( r#"<script lang = "xxx">debugger</script>"# , None ) ,
262293 ( "<script lang='xxx'>debugger</script>" , None ) ,
263294 ( r#"<script lang="xxx">debugger</script>"# , None ) ,
264- ( "<script lang=tsx>debugger</script>" , None ) , // this is valid but too compliated to parse
265295 ] ;
266296
267297 for ( source_text, source_type) in cases {
0 commit comments