@@ -18,7 +18,7 @@ const REGEX_HTML_COMMENT_CLOSE = /-->/;
1818 * @param {import('../index.js').Parser } parser
1919 * @param {number } start
2020 * @param {Array<import('#compiler').Attribute | import('#compiler').SpreadAttribute | import('#compiler').Directive> } attributes
21- * @returns {import('#compiler').Style }
21+ * @returns {import('#compiler').Css.StyleSheet }
2222 */
2323export default function read_style ( parser , start , attributes ) {
2424 const content_start = parser . index ;
@@ -28,7 +28,7 @@ export default function read_style(parser, start, attributes) {
2828 parser . read ( / ^ < \/ s t y l e \s * > / ) ;
2929
3030 return {
31- type : 'Style ' ,
31+ type : 'StyleSheet ' ,
3232 start,
3333 end : parser . index ,
3434 attributes,
@@ -37,8 +37,7 @@ export default function read_style(parser, start, attributes) {
3737 start : content_start ,
3838 end : content_end ,
3939 styles : parser . template . slice ( content_start , content_end )
40- } ,
41- parent : null
40+ }
4241 } ;
4342}
4443
@@ -187,42 +186,66 @@ function read_selector_list(parser, inside_pseudo_class = false) {
187186function read_selector ( parser , inside_pseudo_class = false ) {
188187 const list_start = parser . index ;
189188
190- /** @type {Array< import('#compiler').Css.SimpleSelector | import('#compiler').Css.Combinator> } */
189+ /** @type {import('#compiler').Css.RelativeSelector[] } */
191190 const children = [ ] ;
192191
192+ /**
193+ * @param {import('#compiler').Css.Combinator | null } combinator
194+ * @param {number } start
195+ * @returns {import('#compiler').Css.RelativeSelector }
196+ */
197+ function create_selector ( combinator , start ) {
198+ return {
199+ type : 'RelativeSelector' ,
200+ combinator,
201+ selectors : [ ] ,
202+ start,
203+ end : - 1 ,
204+ metadata : {
205+ is_global : false ,
206+ is_host : false ,
207+ is_root : false ,
208+ scoped : false
209+ }
210+ } ;
211+ }
212+
213+ /** @type {import('#compiler').Css.RelativeSelector } */
214+ let relative_selector = create_selector ( null , parser . index ) ;
215+
193216 while ( parser . index < parser . template . length ) {
194- const start = parser . index ;
217+ let start = parser . index ;
195218
196219 if ( parser . eat ( '*' ) ) {
197220 let name = '*' ;
198- if ( parser . match ( '|' ) ) {
221+
222+ if ( parser . eat ( '|' ) ) {
199223 // * is the namespace (which we ignore)
200- parser . index ++ ;
201224 name = read_identifier ( parser ) ;
202225 }
203226
204- children . push ( {
227+ relative_selector . selectors . push ( {
205228 type : 'TypeSelector' ,
206229 name,
207230 start,
208231 end : parser . index
209232 } ) ;
210233 } else if ( parser . eat ( '#' ) ) {
211- children . push ( {
234+ relative_selector . selectors . push ( {
212235 type : 'IdSelector' ,
213236 name : read_identifier ( parser ) ,
214237 start,
215238 end : parser . index
216239 } ) ;
217240 } else if ( parser . eat ( '.' ) ) {
218- children . push ( {
241+ relative_selector . selectors . push ( {
219242 type : 'ClassSelector' ,
220243 name : read_identifier ( parser ) ,
221244 start,
222245 end : parser . index
223246 } ) ;
224247 } else if ( parser . eat ( '::' ) ) {
225- children . push ( {
248+ relative_selector . selectors . push ( {
226249 type : 'PseudoElementSelector' ,
227250 name : read_identifier ( parser ) ,
228251 start,
@@ -247,7 +270,7 @@ function read_selector(parser, inside_pseudo_class = false) {
247270 error ( parser . index , 'invalid-css-global-selector' ) ;
248271 }
249272
250- children . push ( {
273+ relative_selector . selectors . push ( {
251274 type : 'PseudoClassSelector' ,
252275 name,
253276 args,
@@ -276,7 +299,7 @@ function read_selector(parser, inside_pseudo_class = false) {
276299 parser . allow_whitespace ( ) ;
277300 parser . eat ( ']' , true ) ;
278301
279- children . push ( {
302+ relative_selector . selectors . push ( {
280303 type : 'AttributeSelector' ,
281304 start,
282305 end : parser . index ,
@@ -288,37 +311,28 @@ function read_selector(parser, inside_pseudo_class = false) {
288311 } else if ( inside_pseudo_class && parser . match_regex ( REGEX_NTH_OF ) ) {
289312 // nth of matcher must come before combinator matcher to prevent collision else the '+' in '+2n-1' would be parsed as a combinator
290313
291- children . push ( {
314+ relative_selector . selectors . push ( {
292315 type : 'Nth' ,
293316 value : /**@type {string } */ ( parser . read ( REGEX_NTH_OF ) ) ,
294317 start,
295318 end : parser . index
296319 } ) ;
297- } else if ( parser . match_regex ( REGEX_COMBINATOR_WHITESPACE ) ) {
298- parser . allow_whitespace ( ) ;
299- const start = parser . index ;
300- children . push ( {
301- type : 'Combinator' ,
302- name : /** @type {string } */ ( parser . read ( REGEX_COMBINATOR ) ) ,
303- start,
304- end : parser . index
305- } ) ;
306- parser . allow_whitespace ( ) ;
307320 } else if ( parser . match_regex ( REGEX_PERCENTAGE ) ) {
308- children . push ( {
321+ relative_selector . selectors . push ( {
309322 type : 'Percentage' ,
310323 value : /** @type {string } */ ( parser . read ( REGEX_PERCENTAGE ) ) ,
311324 start,
312325 end : parser . index
313326 } ) ;
314- } else {
327+ } else if ( ! parser . match_regex ( REGEX_COMBINATOR ) ) {
315328 let name = read_identifier ( parser ) ;
316- if ( parser . match ( '|' ) ) {
329+
330+ if ( parser . eat ( '|' ) ) {
317331 // we ignore the namespace when trying to find matching element classes
318- parser . index ++ ;
319332 name = read_identifier ( parser ) ;
320333 }
321- children . push ( {
334+
335+ relative_selector . selectors . push ( {
322336 type : 'TypeSelector' ,
323337 name,
324338 start,
@@ -330,29 +344,85 @@ function read_selector(parser, inside_pseudo_class = false) {
330344 parser . allow_whitespace ( ) ;
331345
332346 if ( parser . match ( ',' ) || ( inside_pseudo_class ? parser . match ( ')' ) : parser . match ( '{' ) ) ) {
347+ // rewind, so we know whether to continue building the selector list
333348 parser . index = index ;
334349
350+ relative_selector . end = index ;
351+ children . push ( relative_selector ) ;
352+
335353 return {
336- type : 'Selector ' ,
354+ type : 'ComplexSelector ' ,
337355 start : list_start ,
338356 end : index ,
339- children
357+ children,
358+ metadata : {
359+ used : false
360+ }
340361 } ;
341362 }
342363
343- if ( parser . index !== index && ! parser . match_regex ( REGEX_COMBINATOR ) ) {
344- children . push ( {
345- type : 'Combinator' ,
346- name : ' ' ,
347- start : index ,
348- end : parser . index
349- } ) ;
364+ parser . index = index ;
365+ const combinator = read_combinator ( parser ) ;
366+
367+ if ( combinator ) {
368+ if ( relative_selector . selectors . length === 0 ) {
369+ if ( ! inside_pseudo_class ) {
370+ error ( start , 'invalid-css-selector' ) ;
371+ }
372+ } else {
373+ relative_selector . end = index ;
374+ children . push ( relative_selector ) ;
375+ }
376+
377+ // ...and start a new one
378+ relative_selector = create_selector ( combinator , combinator . start ) ;
379+
380+ parser . allow_whitespace ( ) ;
381+
382+ if ( parser . match ( ',' ) || ( inside_pseudo_class ? parser . match ( ')' ) : parser . match ( '{' ) ) ) {
383+ error ( parser . index , 'invalid-css-selector' ) ;
384+ }
350385 }
351386 }
352387
353388 error ( parser . template . length , 'unexpected-eof' ) ;
354389}
355390
391+ /**
392+ * @param {import('../index.js').Parser } parser
393+ * @returns {import('#compiler').Css.Combinator | null }
394+ */
395+ function read_combinator ( parser ) {
396+ const start = parser . index ;
397+ parser . allow_whitespace ( ) ;
398+
399+ const index = parser . index ;
400+ const name = parser . read ( REGEX_COMBINATOR ) ;
401+
402+ if ( name ) {
403+ const end = parser . index ;
404+ parser . allow_whitespace ( ) ;
405+
406+ return {
407+ type : 'Combinator' ,
408+ name,
409+ start : index ,
410+ end
411+ } ;
412+ }
413+
414+ if ( parser . index !== start ) {
415+ return {
416+ type : 'Combinator' ,
417+ name : ' ' ,
418+ start,
419+ end : parser . index
420+ } ;
421+ }
422+
423+ return null ;
424+ }
425+
356426/**
357427 * @param {import('../index.js').Parser } parser
358428 * @returns {import('#compiler').Css.Block }
0 commit comments