@@ -29,8 +29,8 @@ import {
2929 type CssStartingStyleAST ,
3030 type CssStylesheetAST ,
3131 type CssSupportsAST ,
32- type CssViewTransitionAST ,
3332 CssTypes ,
33+ type CssViewTransitionAST ,
3434} from '../type' ;
3535import {
3636 indexOfArrayWithBracketAndQuoteSupport ,
@@ -156,12 +156,32 @@ export const parse = (
156156 const rules : Array < CssRuleAST | CssAtRuleAST > = [ ] ;
157157 whitespace ( ) ;
158158 comments ( rules ) ;
159- while ( css . length && css . charAt ( 0 ) !== '}' ) {
159+ while ( css . length ) {
160+ if ( css . charAt ( 0 ) === '}' ) {
161+ if ( options ?. silent ) {
162+ // Skip stray closing braces at top level
163+ error ( "extra '}'" ) ;
164+ const fakeMatch = [ '}' ] as unknown as RegExpExecArray ;
165+ processMatch ( fakeMatch ) ;
166+ whitespace ( ) ;
167+ comments ( rules ) ;
168+ continue ;
169+ }
170+ break ;
171+ }
160172 node = atRule ( ) || rule ( ) ;
161173 if ( node ) {
162174 rules . push ( node ) ;
163175 comments ( rules ) ;
164176 } else {
177+ if ( options ?. silent ) {
178+ // Skip unrecognized character to recover
179+ const fakeMatch = [ css . charAt ( 0 ) ] as unknown as RegExpExecArray ;
180+ processMatch ( fakeMatch ) ;
181+ whitespace ( ) ;
182+ comments ( rules ) ;
183+ continue ;
184+ }
165185 break ;
166186 }
167187 }
@@ -311,6 +331,27 @@ export const parse = (
311331 comments ( decls ) ;
312332 decl = declaration ( ) ;
313333 }
334+ // In silent mode, try to recover from errors by skipping to next semicolon
335+ while ( options ?. silent && css . length && css . charAt ( 0 ) !== '}' ) {
336+ const semiPos = css . indexOf ( ';' ) ;
337+ const bracePos = css . indexOf ( '}' ) ;
338+ if ( semiPos !== - 1 && ( bracePos === - 1 || semiPos < bracePos ) ) {
339+ const fakeMatch = [
340+ css . substring ( 0 , semiPos + 1 ) ,
341+ ] as unknown as RegExpExecArray ;
342+ processMatch ( fakeMatch ) ;
343+ whitespace ( ) ;
344+ comments ( decls ) ;
345+ decl = declaration ( ) ;
346+ while ( decl ) {
347+ decls . push ( decl ) ;
348+ comments ( decls ) ;
349+ decl = declaration ( ) ;
350+ }
351+ } else {
352+ break ;
353+ }
354+ }
314355
315356 if ( ! close ( ) ) {
316357 return error ( "missing '}'" ) ;
@@ -382,7 +423,20 @@ export const parse = (
382423 continue ;
383424 }
384425
385- // nothing matched
426+ // nothing matched — skip to next semicolon or closing brace to recover
427+ if ( options ?. silent ) {
428+ const semiPos = css . indexOf ( ';' ) ;
429+ const bracePos = css . indexOf ( '}' ) ;
430+ if ( semiPos !== - 1 && ( bracePos === - 1 || semiPos < bracePos ) ) {
431+ const fakeMatch = [
432+ css . substring ( 0 , semiPos + 1 ) ,
433+ ] as unknown as RegExpExecArray ;
434+ processMatch ( fakeMatch ) ;
435+ whitespace ( ) ;
436+ comments ( items ) ;
437+ continue ;
438+ }
439+ }
386440 break ;
387441 }
388442
@@ -398,9 +452,7 @@ export const parse = (
398452 * both top-level rules and declarations when nested inside a rule.
399453 */
400454 function rulesOrDeclarations ( ) {
401- const items : Array <
402- CssAtRuleAST | CssDeclarationAST | CssCommentAST
403- > = [ ] ;
455+ const items : Array < CssAtRuleAST | CssDeclarationAST | CssCommentAST > = [ ] ;
404456 whitespace ( ) ;
405457 comments ( items ) ;
406458 while ( css . length && css . charAt ( 0 ) !== '}' ) {
@@ -432,7 +484,20 @@ export const parse = (
432484 continue ;
433485 }
434486
435- // nothing matched
487+ // nothing matched — skip to next semicolon or closing brace to recover
488+ if ( options ?. silent ) {
489+ const semiPos = css . indexOf ( ';' ) ;
490+ const bracePos = css . indexOf ( '}' ) ;
491+ if ( semiPos !== - 1 && ( bracePos === - 1 || semiPos < bracePos ) ) {
492+ const fakeMatch = [
493+ css . substring ( 0 , semiPos + 1 ) ,
494+ ] as unknown as RegExpExecArray ;
495+ processMatch ( fakeMatch ) ;
496+ whitespace ( ) ;
497+ comments ( items ) ;
498+ continue ;
499+ }
500+ }
436501 break ;
437502 }
438503 return items ;
@@ -703,7 +768,7 @@ export const parse = (
703768 'right-bottom' ,
704769 ] ;
705770 const pageMarginBoxRegex = new RegExp (
706- ' ^@(' + pageMarginBoxNames . join ( '|' ) + ' )(?![\\w-])\\s*' ,
771+ ` ^@(${ pageMarginBoxNames . join ( '|' ) } )(?![\\w-])\\s*` ,
707772 ) ;
708773
709774 function atPageMarginBox ( ) : CssPageMarginBoxAST | undefined {
@@ -1118,7 +1183,9 @@ export const parse = (
11181183 const preludeEnd = indexOfArrayWithBracketAndQuoteSupport ( css , [ '{' , ';' ] ) ;
11191184 if ( preludeEnd !== - 1 && preludeEnd > 0 ) {
11201185 prelude = trim ( css . substring ( 0 , preludeEnd ) ) ;
1121- const fakeMatch = [ css . substring ( 0 , preludeEnd ) ] as unknown as RegExpExecArray ;
1186+ const fakeMatch = [
1187+ css . substring ( 0 , preludeEnd ) ,
1188+ ] as unknown as RegExpExecArray ;
11221189 processMatch ( fakeMatch ) ;
11231190 }
11241191
0 commit comments