@@ -42,7 +42,77 @@ export type ParsedCoreBridgeRequest = {
4242 */
4343export const JS_IDENTIFIER_RE = / ^ [ $ _ \p{ ID_Start} ] [ $ \u200c \u200d \p{ ID_Continue} ] * $ / u;
4444
45+ const REGEX_PRECEDING_KEYWORD_RE = / (?: ^ | [ ^ $ \w ] ) (?: r e t u r n | t y p e o f | i n s t a n c e o f | i n | o f | n e w | d e l e t e | v o i d | t h r o w | c a s e | d o | e l s e | y i e l d | a w a i t ) $ / ;
46+
47+ /**
48+ * Blank line and block comments (preserving newlines) so the regex export
49+ * scanners never see a commented-out `export` declaration. String, template
50+ * and regex literals are skipped so their contents are left intact.
51+ */
52+ export function maskJsComments ( code : string ) : string {
53+ if ( ! code || ( code . indexOf ( '//' ) === - 1 && code . indexOf ( '/*' ) === - 1 ) ) {
54+ return code ;
55+ }
56+ const chars = code . split ( '' ) ;
57+ const n = code . length ;
58+ const blank = ( from : number , to : number ) => {
59+ for ( let k = from ; k < to ; k ++ ) {
60+ if ( chars [ k ] !== '\n' ) chars [ k ] = ' ' ;
61+ }
62+ } ;
63+ // A `/` starts a regex literal (not division) when it follows an operator,
64+ // an opening bracket, or a keyword — the standard lexer heuristic.
65+ const slashStartsRegex = ( at : number ) : boolean => {
66+ let j = at - 1 ;
67+ while ( j >= 0 && / \s / . test ( code [ j ] ) ) j -- ;
68+ if ( j < 0 ) return true ;
69+ if ( '(,=:[!&|?{};+-*%<>~^' . includes ( code [ j ] ) ) return true ;
70+ return REGEX_PRECEDING_KEYWORD_RE . test ( code . slice ( Math . max ( 0 , j - 10 ) , j + 1 ) ) ;
71+ } ;
72+ let i = 0 ;
73+ while ( i < n ) {
74+ const c = code [ i ] ;
75+ const next = code [ i + 1 ] ;
76+ if ( c === '/' && next === '/' ) {
77+ const eol = code . indexOf ( '\n' , i ) ;
78+ const stop = eol === - 1 ? n : eol ;
79+ blank ( i , stop ) ;
80+ i = stop ;
81+ } else if ( c === '/' && next === '*' ) {
82+ const close = code . indexOf ( '*/' , i + 2 ) ;
83+ const stop = close === - 1 ? n : close + 2 ;
84+ blank ( i , stop ) ;
85+ i = stop ;
86+ } else if ( c === "'" || c === '"' || c === '`' ) {
87+ i ++ ;
88+ while ( i < n && code [ i ] !== c ) {
89+ i += code [ i ] === '\\' ? 2 : 1 ;
90+ }
91+ i ++ ;
92+ } else if ( c === '/' && slashStartsRegex ( i ) ) {
93+ i ++ ;
94+ let inClass = false ;
95+ while ( i < n && code [ i ] !== '\n' ) {
96+ const r = code [ i ] ;
97+ if ( r === '\\' ) {
98+ i += 2 ;
99+ continue ;
100+ }
101+ if ( r === '[' ) inClass = true ;
102+ else if ( r === ']' ) inClass = false ;
103+ else if ( r === '/' && ! inClass ) break ;
104+ i ++ ;
105+ }
106+ i ++ ;
107+ } else {
108+ i ++ ;
109+ }
110+ }
111+ return chars . join ( '' ) ;
112+ }
113+
45114export function extractDirectExportedNames ( code : string ) : string [ ] {
115+ code = maskJsComments ( code ) ;
46116 const names = new Set < string > ( ) ;
47117 const declRe = / \b e x p o r t \s + (?: a s y n c \s + ) ? (?: f u n c t i o n | c l a s s ) \s + ( [ $ _ \p{ ID_Start} ] [ $ \p{ ID_Continue} ] * ) / gu;
48118 let match : RegExpExecArray | null ;
0 commit comments