@@ -78,7 +78,8 @@ function collectTokens(ast: Root, keyframes: boolean) {
7878
7979export interface ParseCSSModuleOptions {
8080 fileName : string ;
81- safe : boolean ;
81+ /** Whether to include syntax errors from diagnostics */
82+ includeSyntaxError : boolean ;
8283 keyframes : boolean ;
8384}
8485
@@ -87,39 +88,46 @@ export interface ParseCSSModuleResult {
8788 diagnostics : DiagnosticWithLocation [ ] ;
8889}
8990
91+ /**
92+ * Parse CSS Module text.
93+ * If a syntax error is detected in the text, it is re-parsed using `postcss-safe-parser`, and `localTokens` are collected as much as possible.
94+ */
9095export function parseCSSModule (
9196 text : string ,
92- { fileName, safe , keyframes } : ParseCSSModuleOptions ,
97+ { fileName, includeSyntaxError , keyframes } : ParseCSSModuleOptions ,
9398) : ParseCSSModuleResult {
9499 let ast : Root ;
95- const diagnosticSourceFile = { fileName, text } ;
96- try {
97- const parser = safe ? safeParser : parse ;
98- ast = parser ( text , { from : fileName } ) ;
99- } catch ( e ) {
100- if ( e instanceof CssSyntaxError ) {
100+ const diagnosticFile = { fileName, text } ;
101+ const allDiagnostics : DiagnosticWithLocation [ ] = [ ] ;
102+ if ( includeSyntaxError ) {
103+ try {
104+ ast = parse ( text , { from : fileName } ) ;
105+ } catch ( e ) {
106+ if ( ! ( e instanceof CssSyntaxError ) ) throw e ;
107+ // If syntax error, try to parse with safe parser. While this incurs a cost
108+ // due to parsing the file twice, it rarely becomes an issue since files
109+ // with syntax errors are usually few in number.
110+ ast = safeParser ( text , { from : fileName } ) ;
101111 const { line, column, endColumn } = e . input ! ;
102- return {
103- cssModule : { fileName, text, localTokens : [ ] , tokenImporters : [ ] } ,
104- diagnostics : [
105- {
106- file : diagnosticSourceFile ,
107- start : { line, column } ,
108- length : endColumn !== undefined ? endColumn - column : 1 ,
109- text : e . reason ,
110- category : 'error' ,
111- } ,
112- ] ,
113- } ;
112+ allDiagnostics . push ( {
113+ file : diagnosticFile ,
114+ start : { line, column } ,
115+ length : endColumn !== undefined ? endColumn - column : 1 ,
116+ text : e . reason ,
117+ category : 'error' ,
118+ } ) ;
114119 }
115- throw e ;
120+ } else {
121+ ast = safeParser ( text , { from : fileName } ) ;
116122 }
123+
117124 const { localTokens, tokenImporters, diagnostics } = collectTokens ( ast , keyframes ) ;
118125 const cssModule = {
119126 fileName,
120127 text,
121128 localTokens,
122129 tokenImporters,
123130 } ;
124- return { cssModule, diagnostics : diagnostics . map ( ( diagnostic ) => ( { ...diagnostic , file : diagnosticSourceFile } ) ) } ;
131+ allDiagnostics . push ( ...diagnostics . map ( ( diagnostic ) => ( { ...diagnostic , file : diagnosticFile } ) ) ) ;
132+ return { cssModule, diagnostics : allDiagnostics } ;
125133}
0 commit comments