@@ -123,10 +123,9 @@ class LocConverter {
123
123
124
124
const CSS_MODE_TOP_LEVEL = 0 ;
125
125
const CSS_MODE_IN_BLOCK = 1 ;
126
- const CSS_MODE_AT_IMPORT_EXPECT_URL = 2 ;
127
- const CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA = 3 ;
128
- const CSS_MODE_AT_IMPORT_INVALID = 4 ;
129
- const CSS_MODE_AT_NAMESPACE_INVALID = 5 ;
126
+ const CSS_MODE_IN_AT_IMPORT = 2 ;
127
+ const CSS_MODE_AT_IMPORT_INVALID = 3 ;
128
+ const CSS_MODE_AT_NAMESPACE_INVALID = 4 ;
130
129
131
130
class CssParser extends Parser {
132
131
constructor ( { allowModeSwitch = true , defaultMode = "global" } = { } ) {
@@ -185,7 +184,7 @@ class CssParser extends Parser {
185
184
let lastIdentifier = undefined ;
186
185
/** @type [string, number, number][] */
187
186
let balanced = [ ] ;
188
- /** @type {undefined | { start: number, end: number, url ?: string, media ?: string, supports?: string, layer ?: string } } */
187
+ /** @type {undefined | { start: number, url?: string, urlStart?: number, urlEnd ?: number, layer ?: string, layerStart?: number, layerEnd?: number, supports?: string, supportsStart?: number, supportsEnd?: number, inSupports?:boolean, media ?: string } } */
189
188
let importData = undefined ;
190
189
/** @type {boolean } */
191
190
let inAnimationProperty = false ;
@@ -407,15 +406,32 @@ class CssParser extends Parser {
407
406
} ,
408
407
url : ( input , start , end , contentStart , contentEnd ) => {
409
408
let value = normalizeUrl ( input . slice ( contentStart , contentEnd ) , false ) ;
409
+
410
410
switch ( scope ) {
411
- case CSS_MODE_AT_IMPORT_EXPECT_URL : {
411
+ case CSS_MODE_IN_AT_IMPORT : {
412
+ // Do not parse URLs in `supports(...)`
413
+ if ( importData . inSupports ) {
414
+ break ;
415
+ }
416
+
417
+ if ( importData . url ) {
418
+ this . _emitWarning (
419
+ state ,
420
+ `Duplicate of 'url(...)' in '${ input . slice (
421
+ importData . start ,
422
+ end
423
+ ) } '`,
424
+ locConverter ,
425
+ start ,
426
+ end
427
+ ) ;
428
+
429
+ break ;
430
+ }
431
+
412
432
importData . url = value ;
413
- importData . end = end ;
414
- scope = CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA ;
415
- break ;
416
- }
417
- // Do not parse URLs in `supports(...)`
418
- case CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA : {
433
+ importData . urlStart = start ;
434
+ importData . urlEnd = end ;
419
435
break ;
420
436
}
421
437
// Do not parse URLs in import between rules
@@ -442,23 +458,44 @@ class CssParser extends Parser {
442
458
} ,
443
459
string : ( input , start , end ) => {
444
460
switch ( scope ) {
445
- case CSS_MODE_AT_IMPORT_EXPECT_URL : {
461
+ case CSS_MODE_IN_AT_IMPORT : {
462
+ const insideURLFunction =
463
+ balanced [ balanced . length - 1 ] &&
464
+ balanced [ balanced . length - 1 ] [ 0 ] === "url" ;
465
+
466
+ // Do not parse URLs in `supports(...)` and other strings if we already have a URL
467
+ if (
468
+ importData . inSupports ||
469
+ ( ! insideURLFunction && importData . url )
470
+ ) {
471
+ break ;
472
+ }
473
+
474
+ if ( insideURLFunction && importData . url ) {
475
+ this . _emitWarning (
476
+ state ,
477
+ `Duplicate of 'url(...)' in '${ input . slice (
478
+ importData . start ,
479
+ end
480
+ ) } '`,
481
+ locConverter ,
482
+ start ,
483
+ end
484
+ ) ;
485
+
486
+ break ;
487
+ }
488
+
446
489
importData . url = normalizeUrl (
447
490
input . slice ( start + 1 , end - 1 ) ,
448
491
true
449
492
) ;
450
- importData . end = end ;
451
- const insideURLFunction =
452
- balanced [ balanced . length - 1 ] &&
453
- balanced [ balanced . length - 1 ] [ 0 ] === "url" ;
454
493
455
494
if ( ! insideURLFunction ) {
456
- scope = CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA ;
495
+ importData . urlStart = start ;
496
+ importData . urlEnd = end ;
457
497
}
458
- break ;
459
- }
460
- // Do not parse URLs in `supports(...)`
461
- case CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA : {
498
+
462
499
break ;
463
500
}
464
501
case CSS_MODE_IN_BLOCK : {
@@ -499,7 +536,7 @@ class CssParser extends Parser {
499
536
scope = CSS_MODE_AT_NAMESPACE_INVALID ;
500
537
this . _emitWarning (
501
538
state ,
502
- "@namespace is not supported in bundled CSS" ,
539
+ "' @namespace' is not supported in bundled CSS" ,
503
540
locConverter ,
504
541
start ,
505
542
end
@@ -510,16 +547,16 @@ class CssParser extends Parser {
510
547
scope = CSS_MODE_AT_IMPORT_INVALID ;
511
548
this . _emitWarning (
512
549
state ,
513
- "Any @import rules must precede all other rules" ,
550
+ "Any ' @import' rules must precede all other rules" ,
514
551
locConverter ,
515
552
start ,
516
553
end
517
554
) ;
518
555
return end ;
519
556
}
520
557
521
- scope = CSS_MODE_AT_IMPORT_EXPECT_URL ;
522
- importData = { start, end } ;
558
+ scope = CSS_MODE_IN_AT_IMPORT ;
559
+ importData = { start } ;
523
560
} else if (
524
561
this . allowModeSwitch &&
525
562
OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE . test ( name )
@@ -600,54 +637,99 @@ class CssParser extends Parser {
600
637
} ,
601
638
semicolon : ( input , start , end ) => {
602
639
switch ( scope ) {
603
- case CSS_MODE_AT_IMPORT_EXPECT_URL : {
604
- this . _emitWarning (
605
- state ,
606
- `Expected URL for @import at ${ start } ` ,
607
- locConverter ,
608
- start ,
609
- end
610
- ) ;
611
- return end ;
612
- }
613
- case CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA : {
614
- if ( ! importData . url === undefined ) {
640
+ case CSS_MODE_IN_AT_IMPORT : {
641
+ const { start } = importData ;
642
+
643
+ if ( importData . url === undefined ) {
615
644
this . _emitWarning (
616
645
state ,
617
- `Expected URL for @import at ${ importData . start } ` ,
646
+ `Expected URL in ' ${ input . slice ( start , end ) } ' ` ,
618
647
locConverter ,
619
- importData . start ,
620
- importData . end
648
+ start ,
649
+ end
621
650
) ;
651
+ importData = undefined ;
652
+ scope = CSS_MODE_TOP_LEVEL ;
653
+ return end ;
654
+ }
655
+ if (
656
+ importData . urlStart > importData . layerStart ||
657
+ importData . urlStart > importData . supportsStart
658
+ ) {
659
+ this . _emitWarning (
660
+ state ,
661
+ `An URL in '${ input . slice (
662
+ start ,
663
+ end
664
+ ) } ' should be before 'layer(...)' or 'supports(...)'`,
665
+ locConverter ,
666
+ start ,
667
+ end
668
+ ) ;
669
+ importData = undefined ;
670
+ scope = CSS_MODE_TOP_LEVEL ;
622
671
return end ;
623
672
}
673
+ if ( importData . layerStart > importData . supportsStart ) {
674
+ this . _emitWarning (
675
+ state ,
676
+ `The 'layer(...)' in '${ input . slice (
677
+ start ,
678
+ end
679
+ ) } ' should be before 'supports(...)'`,
680
+ locConverter ,
681
+ start ,
682
+ end
683
+ ) ;
684
+ importData = undefined ;
685
+ scope = CSS_MODE_TOP_LEVEL ;
686
+ return end ;
687
+ }
688
+
624
689
const semicolonPos = end ;
625
690
end = walkCssTokens . eatWhiteLine ( input , end + 1 ) ;
626
- const { line : sl , column : sc } = locConverter . get ( importData . start ) ;
691
+ const { line : sl , column : sc } = locConverter . get ( start ) ;
627
692
const { line : el , column : ec } = locConverter . get ( end ) ;
628
- const pos = walkCssTokens . eatWhitespaceAndComments (
629
- input ,
630
- importData . end
631
- ) ;
693
+ const lastEnd =
694
+ importData . supportsEnd ||
695
+ importData . layerEnd ||
696
+ importData . urlEnd ||
697
+ start ;
698
+ const pos = walkCssTokens . eatWhitespaceAndComments ( input , lastEnd ) ;
632
699
// Prevent to consider comments as a part of media query
633
700
if ( pos !== semicolonPos - 1 ) {
634
- importData . media = input
635
- . slice ( importData . end , semicolonPos - 1 )
636
- . trim ( ) ;
701
+ importData . media = input . slice ( lastEnd , semicolonPos - 1 ) . trim ( ) ;
637
702
}
638
- const dep = new CssImportDependency (
639
- importData . url . trim ( ) ,
640
- [ importData . start , end ] ,
641
- importData . layer ,
642
- importData . supports ,
643
- importData . media && importData . media . length > 0
644
- ? importData . media
645
- : undefined
646
- ) ;
647
- dep . setLoc ( sl , sc , el , ec ) ;
648
- module . addDependency ( dep ) ;
703
+
704
+ const url = importData . url . trim ( ) ;
705
+
706
+ if ( url . length === 0 ) {
707
+ const dep = new ConstDependency ( "" , [ start , end ] ) ;
708
+ module . addPresentationalDependency ( dep ) ;
709
+ dep . setLoc ( sl , sc , el , ec ) ;
710
+ } else {
711
+ const dep = new CssImportDependency (
712
+ url ,
713
+ [ start , end ] ,
714
+ importData . layer ,
715
+ importData . supports ,
716
+ importData . media && importData . media . length > 0
717
+ ? importData . media
718
+ : undefined
719
+ ) ;
720
+ dep . setLoc ( sl , sc , el , ec ) ;
721
+ module . addDependency ( dep ) ;
722
+ }
723
+
649
724
importData = undefined ;
650
725
scope = CSS_MODE_TOP_LEVEL ;
726
+
727
+ break ;
728
+ }
729
+ case CSS_MODE_AT_IMPORT_INVALID :
730
+ case CSS_MODE_AT_NAMESPACE_INVALID : {
731
+ scope = CSS_MODE_TOP_LEVEL ;
732
+
651
733
break ;
652
734
}
653
735
case CSS_MODE_IN_BLOCK : {
@@ -720,10 +802,11 @@ class CssParser extends Parser {
720
802
}
721
803
break ;
722
804
}
723
- case CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA : {
805
+ case CSS_MODE_IN_AT_IMPORT : {
724
806
if ( input . slice ( start , end ) . toLowerCase ( ) === "layer" ) {
725
807
importData . layer = "" ;
726
- importData . end = end ;
808
+ importData . layerStart = start ;
809
+ importData . layerEnd = end ;
727
810
}
728
811
break ;
729
812
}
@@ -758,6 +841,13 @@ class CssParser extends Parser {
758
841
759
842
balanced . push ( [ name , start , end ] ) ;
760
843
844
+ if (
845
+ scope === CSS_MODE_IN_AT_IMPORT &&
846
+ name . toLowerCase ( ) === "supports"
847
+ ) {
848
+ importData . inSupports = true ;
849
+ }
850
+
761
851
if ( isLocalMode ( ) ) {
762
852
name = name . toLowerCase ( ) ;
763
853
@@ -812,20 +902,23 @@ class CssParser extends Parser {
812
902
}
813
903
814
904
switch ( scope ) {
815
- case CSS_MODE_AT_IMPORT_EXPECT_URL : {
816
- if ( last && last [ 0 ] === "url" ) {
817
- importData . end = end ;
818
- scope = CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA ;
819
- }
820
- break ;
821
- }
822
- case CSS_MODE_AT_IMPORT_EXPECT_LAYER_OR_SUPPORTS_OR_MEDIA : {
823
- if ( last && last [ 0 ] . toLowerCase ( ) === "layer" ) {
905
+ case CSS_MODE_IN_AT_IMPORT : {
906
+ if ( last && last [ 0 ] === "url" && ! importData . inSupports ) {
907
+ importData . urlStart = last [ 1 ] ;
908
+ importData . urlEnd = end ;
909
+ } else if (
910
+ last &&
911
+ last [ 0 ] . toLowerCase ( ) === "layer" &&
912
+ ! importData . inSupports
913
+ ) {
824
914
importData . layer = input . slice ( last [ 2 ] , end - 1 ) . trim ( ) ;
825
- importData . end = end ;
915
+ importData . layerStart = last [ 1 ] ;
916
+ importData . layerEnd = end ;
826
917
} else if ( last && last [ 0 ] . toLowerCase ( ) === "supports" ) {
827
918
importData . supports = input . slice ( last [ 2 ] , end - 1 ) . trim ( ) ;
828
- importData . end = end ;
919
+ importData . supportsStart = last [ 1 ] ;
920
+ importData . supportsEnd = end ;
921
+ importData . inSupports = false ;
829
922
}
830
923
break ;
831
924
}
0 commit comments