@@ -1082,6 +1082,64 @@ pub fn extractFunction(s: *Scanner, decl_start: usize, is_exported: bool, is_asy
10821082// Variable extraction
10831083// ========================================================================
10841084
1085+ const JsxTagInfo = struct { delta : isize , end : usize };
1086+
1087+ fn jsxTagAt (source : []const u8 , index : usize ) ? JsxTagInfo {
1088+ if (index + 1 >= source .len or source [index ] != ch .CH_LANGLE ) return null ;
1089+ const next = source [index + 1 ];
1090+ if (next == ch .CH_RANGLE ) return .{ .delta = 1 , .end = index + 1 };
1091+ if (next == ch .CH_SLASH ) {
1092+ const close_end = ch .indexOfChar (source , ch .CH_RANGLE , index + 2 ) orelse return null ;
1093+ return .{ .delta = -1 , .end = close_end };
1094+ }
1095+ if (! ch .isIdentStart (next )) return null ;
1096+
1097+ var name_end = index + 2 ;
1098+ while (name_end < source .len ) : (name_end += 1 ) {
1099+ const code = source [name_end ];
1100+ if (! ch .isIdentChar (code ) and code != ch .CH_DOT and code != ':' and code != '-' ) break ;
1101+ }
1102+ const tag_name = source [index + 1 .. name_end ];
1103+ if (name_end >= source .len ) return null ;
1104+ const delimiter = source [name_end ];
1105+ if (! ch .isWhitespace (delimiter ) and delimiter != ch .CH_RANGLE and delimiter != ch .CH_SLASH ) return null ;
1106+
1107+ var brace_depth : usize = 0 ;
1108+ var quote : u8 = 0 ;
1109+ var tag_end = name_end ;
1110+ while (tag_end < source .len ) : (tag_end += 1 ) {
1111+ const code = source [tag_end ];
1112+ if (quote != 0 ) {
1113+ if (code == ch .CH_BACKSLASH ) tag_end += 1 else if (code == quote ) quote = 0 ;
1114+ continue ;
1115+ }
1116+ if (code == ch .CH_SQUOTE or code == ch .CH_DQUOTE or code == ch .CH_BACKTICK ) {
1117+ quote = code ;
1118+ } else if (code == ch .CH_LBRACE ) {
1119+ brace_depth += 1 ;
1120+ } else if (code == ch .CH_RBRACE and brace_depth > 0 ) {
1121+ brace_depth -= 1 ;
1122+ } else if (code == ch .CH_RANGLE and brace_depth == 0 ) {
1123+ break ;
1124+ }
1125+ }
1126+ if (tag_end >= source .len ) return null ;
1127+
1128+ var before_end = tag_end - 1 ;
1129+ while (before_end > index and ch .isWhitespace (source [before_end ])) before_end -= 1 ;
1130+ if (source [before_end ] == ch .CH_SLASH ) return .{ .delta = 0 , .end = tag_end };
1131+
1132+ var search_from = tag_end + 1 ;
1133+ while (ch .indexOf (source , "</" , search_from )) | close_start | {
1134+ const close_name_start = close_start + 2 ;
1135+ const close_name_end = close_name_start + tag_name .len ;
1136+ if (close_name_end <= source .len and std .mem .eql (u8 , source [close_name_start .. close_name_end ], tag_name ) and
1137+ close_name_end < source .len and (source [close_name_end ] == ch .CH_RANGLE or ch .isWhitespace (source [close_name_end ]))) return .{ .delta = 1 , .end = tag_end };
1138+ search_from = close_start + 2 ;
1139+ }
1140+ return null ;
1141+ }
1142+
10851143/// Extract variable declaration(s)
10861144pub fn extractVariable (s : * Scanner , decl_start : usize , kind : []const u8 , is_exported : bool ) []const Declaration {
10871145 s .pos += kind .len ; // skip const/let/var
@@ -1141,6 +1199,8 @@ pub fn extractVariable(s: *Scanner, decl_start: usize, kind: []const u8, is_expo
11411199 s .skipWhitespaceAndComments ();
11421200 const init_start = s .pos ;
11431201 var depth : isize = 0 ;
1202+ var jsx_depth : isize = 0 ;
1203+ var jsx_tag_end : ? usize = null ;
11441204 while (s .pos < s .len ) {
11451205 // SIMD fast-skip: bulk-skip bytes that can't be structural
11461206 if (depth > 0 ) {
@@ -1193,14 +1253,27 @@ pub fn extractVariable(s: *Scanner, decl_start: usize, kind: []const u8, is_expo
11931253 if (s .pos >= s .len ) break ;
11941254 if (s .skipNonCode ()) continue ;
11951255 const ic = s .source [s .pos ];
1196- if (ic == ch .CH_LPAREN or ic == ch .CH_LBRACE or ic == ch .CH_LBRACKET or ic == ch .CH_LANGLE ) {
1256+ if (ic == ch .CH_LANGLE ) {
1257+ if (jsxTagAt (s .source , s .pos )) | tag | {
1258+ jsx_depth = @max (0 , jsx_depth + tag .delta );
1259+ jsx_tag_end = tag .end ;
1260+ } else {
1261+ depth += 1 ;
1262+ }
1263+ } else if (ic == ch .CH_LPAREN or ic == ch .CH_LBRACE or ic == ch .CH_LBRACKET ) {
11971264 depth += 1 ;
1198- } else if (ic == ch .CH_RPAREN or ic == ch .CH_RBRACE or ic == ch .CH_RBRACKET or ( ic == ch . CH_RANGLE and ! s . isArrowGT ()) ) {
1265+ } else if (ic == ch .CH_RPAREN or ic == ch .CH_RBRACE or ic == ch .CH_RBRACKET ) {
11991266 depth -= 1 ;
1200- } else if (depth == 0 and (ic == ch .CH_SEMI or ic == ch .CH_COMMA )) {
1267+ } else if (ic == ch .CH_RANGLE ) {
1268+ if (jsx_tag_end != null and jsx_tag_end .? == s .pos ) {
1269+ jsx_tag_end = null ;
1270+ } else if (jsx_depth == 0 and ! s .isArrowGT () and depth > 0 ) {
1271+ depth -= 1 ;
1272+ }
1273+ } else if (depth == 0 and jsx_depth == 0 and (ic == ch .CH_SEMI or ic == ch .CH_COMMA )) {
12011274 break ;
12021275 }
1203- if (depth == 0 and s .checkASITopLevel ()) break ;
1276+ if (depth == 0 and jsx_depth == 0 and s .checkASITopLevel ()) break ;
12041277 s .pos += 1 ;
12051278 }
12061279 initializer_text = if (skip_isolated_initializer ) "" else s .sliceTrimmed (init_start , s .pos );
0 commit comments