1
1
/* eslint-disable class-methods-use-this */
2
2
3
3
import { validate } from 'schema-utils' ;
4
+ import { SyncWaterfallHook } from 'tapable' ;
4
5
5
6
import schema from './plugin-options.json' ;
6
7
import { MODULE_TYPE , compareModulesByIdentifier } from './utils' ;
@@ -28,6 +29,8 @@ const cssModuleCache = new WeakMap();
28
29
*/
29
30
const cssDependencyCache = new WeakMap ( ) ;
30
31
32
+ const compilerHookMap = new WeakMap ( ) ;
33
+
31
34
class MiniCssExtractPlugin {
32
35
static getCssModule ( webpack ) {
33
36
/**
@@ -300,6 +303,20 @@ class MiniCssExtractPlugin {
300
303
return CssDependency ;
301
304
}
302
305
306
+ static getCompilerHooks ( compiler ) {
307
+ /**
308
+ * Prevent creation of multiple compiler hook maps to allow other integrations to get the current mapping.
309
+ */
310
+ let hooks = compilerHookMap . get ( compiler ) ;
311
+ if ( ! hooks ) {
312
+ hooks = {
313
+ customize : new SyncWaterfallHook ( [ 'attributes' ] ) ,
314
+ } ;
315
+ compilerHookMap . set ( compiler , hooks ) ;
316
+ }
317
+ return hooks ;
318
+ }
319
+
303
320
constructor ( options = { } ) {
304
321
validate ( schema , options , {
305
322
name : 'Mini CSS Extract Plugin' ,
@@ -690,6 +707,42 @@ class MiniCssExtractPlugin {
690
707
}
691
708
) ;
692
709
710
+ const attributes = {
711
+ href : `${ mainTemplate . requireFn } .p + href` ,
712
+ rel : JSON . stringify ( 'stylesheet' ) ,
713
+ onload : 'onLinkComplete' ,
714
+ onerror : 'onLinkComplete' ,
715
+ } ;
716
+
717
+ // Some attributes cannot be assigned through setAttribute, so we maintain
718
+ // a list of attributes that can safely be assigned through dot notation
719
+ const safeAttrs = [ 'href' , 'rel' , 'type' , 'onload' , 'onerror' ] ;
720
+
721
+ // TODO: Test with webpack 4 as well
722
+ if ( this . runtimeOptions . linkType ) {
723
+ attributes . type = JSON . stringify ( this . runtimeOptions . linkType ) ;
724
+ }
725
+
726
+ if ( crossOriginLoading ) {
727
+ attributes . crossOrigin = `(linkTag.href.indexOf(window.location.origin + '/') !== 0)
728
+ ? ${ JSON . stringify ( crossOriginLoading ) }
729
+ : undefined` ;
730
+ }
731
+
732
+ // Append static attributes
733
+ if ( this . runtimeOptions . attributes ) {
734
+ Object . entries ( this . runtimeOptions . attributes ) . forEach (
735
+ ( [ key , value ] ) => {
736
+ attributes [ key ] = JSON . stringify ( value ) ;
737
+ }
738
+ ) ;
739
+ }
740
+
741
+ // Append dynamic attributes
742
+ MiniCssExtractPlugin . getCompilerHooks ( compiler ) . customize . call (
743
+ attributes
744
+ ) ;
745
+
693
746
return Template . asString ( [
694
747
source ,
695
748
'' ,
@@ -701,7 +754,7 @@ class MiniCssExtractPlugin {
701
754
'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {' ,
702
755
Template . indent ( [
703
756
`var href = ${ linkHrefPath } ;` ,
704
- `var fullhref = ${ mainTemplate . requireFn } .p + href;` ,
757
+ `var fullhref = ${ attributes . href } ;` ,
705
758
'var existingLinkTags = document.getElementsByTagName("link");' ,
706
759
'for(var i = 0; i < existingLinkTags.length; i++) {' ,
707
760
Template . indent ( [
@@ -719,25 +772,6 @@ class MiniCssExtractPlugin {
719
772
] ) ,
720
773
'}' ,
721
774
'var linkTag = document.createElement("link");' ,
722
- this . runtimeOptions . attributes
723
- ? Template . asString (
724
- Object . entries ( this . runtimeOptions . attributes ) . map (
725
- ( entry ) => {
726
- const [ key , value ] = entry ;
727
-
728
- return `linkTag.setAttribute(${ JSON . stringify (
729
- key
730
- ) } , ${ JSON . stringify ( value ) } );`;
731
- }
732
- )
733
- )
734
- : '' ,
735
- 'linkTag.rel = "stylesheet";' ,
736
- this . runtimeOptions . linkType
737
- ? `linkTag.type = ${ JSON . stringify (
738
- this . runtimeOptions . linkType
739
- ) } ;`
740
- : '' ,
741
775
'var onLinkComplete = function (event) {' ,
742
776
Template . indent ( [
743
777
'// avoid mem leaks.' ,
@@ -759,19 +793,17 @@ class MiniCssExtractPlugin {
759
793
'}' ,
760
794
] ) ,
761
795
'};' ,
762
- 'linkTag.onerror = linkTag.onload = onLinkComplete;' ,
763
- 'linkTag.href = fullhref;' ,
764
- crossOriginLoading
765
- ? Template . asString ( [
766
- `if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {` ,
767
- Template . indent (
768
- `linkTag.crossOrigin = ${ JSON . stringify (
769
- crossOriginLoading
770
- ) } ;`
771
- ) ,
772
- '}' ,
773
- ] )
774
- : '' ,
796
+ Template . asString (
797
+ Object . entries ( attributes ) . map ( ( [ key , value ] ) => {
798
+ if ( safeAttrs . includes ( key ) ) {
799
+ return `linkTag.${ key } = ${ value } ;` ;
800
+ }
801
+
802
+ return `linkTag.setAttribute(${ JSON . stringify (
803
+ key
804
+ ) } , ${ value } );`;
805
+ } )
806
+ ) ,
775
807
typeof this . runtimeOptions . insert !== 'undefined'
776
808
? typeof this . runtimeOptions . insert === 'function'
777
809
? `(${ this . runtimeOptions . insert . toString ( ) } )(linkTag)`
@@ -847,30 +879,46 @@ class MiniCssExtractPlugin {
847
879
return null ;
848
880
}
849
881
882
+ const attributes = {
883
+ href : `${ RuntimeGlobals . publicPath } + ${ RuntimeGlobals . require } .miniCssF(chunkId)` ,
884
+ rel : JSON . stringify ( 'stylesheet' ) ,
885
+ onload : 'onLinkComplete' ,
886
+ onerror : 'onLinkComplete' ,
887
+ } ;
888
+
889
+ // Some attributes cannot be assigned through setAttribute, so we maintain
890
+ // a list of attributes that can safely be assigned through dot notation
891
+ const safeAttrs = [ 'href' , 'rel' , 'type' , 'onload' , 'onerror' ] ;
892
+
893
+ if ( this . runtimeOptions . linkType ) {
894
+ attributes . type = JSON . stringify ( this . runtimeOptions . linkType ) ;
895
+ }
896
+
897
+ if ( crossOriginLoading ) {
898
+ attributes . crossOrigin = `(linkTag.href.indexOf(window.location.origin + '/') !== 0)
899
+ ? ${ JSON . stringify ( crossOriginLoading ) }
900
+ : undefined` ;
901
+ }
902
+
903
+ // Append static attributes
904
+ if ( this . runtimeOptions . attributes ) {
905
+ Object . entries ( this . runtimeOptions . attributes ) . forEach (
906
+ ( [ key , value ] ) => {
907
+ attributes [ key ] = JSON . stringify ( value ) ;
908
+ }
909
+ ) ;
910
+ }
911
+
912
+ // Append dynamic attributes
913
+ MiniCssExtractPlugin . getCompilerHooks ( compiler ) . customize . call (
914
+ attributes
915
+ ) ;
916
+
850
917
return Template . asString ( [
851
918
`var createStylesheet = ${ runtimeTemplate . basicFunction (
852
919
'chunkId, fullhref, resolve, reject' ,
853
920
[
854
921
'var linkTag = document.createElement("link");' ,
855
- this . runtimeOptions . attributes
856
- ? Template . asString (
857
- Object . entries ( this . runtimeOptions . attributes ) . map (
858
- ( entry ) => {
859
- const [ key , value ] = entry ;
860
-
861
- return `linkTag.setAttribute(${ JSON . stringify (
862
- key
863
- ) } , ${ JSON . stringify ( value ) } );`;
864
- }
865
- )
866
- )
867
- : '' ,
868
- 'linkTag.rel = "stylesheet";' ,
869
- this . runtimeOptions . linkType
870
- ? `linkTag.type = ${ JSON . stringify (
871
- this . runtimeOptions . linkType
872
- ) } ;`
873
- : '' ,
874
922
`var onLinkComplete = ${ runtimeTemplate . basicFunction (
875
923
'event' ,
876
924
[
@@ -892,19 +940,17 @@ class MiniCssExtractPlugin {
892
940
'}' ,
893
941
]
894
942
) } `,
895
- 'linkTag.onerror = linkTag.onload = onLinkComplete;' ,
896
- 'linkTag.href = fullhref;' ,
897
- crossOriginLoading
898
- ? Template . asString ( [
899
- `if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {` ,
900
- Template . indent (
901
- `linkTag.crossOrigin = ${ JSON . stringify (
902
- crossOriginLoading
903
- ) } ;`
904
- ) ,
905
- '}' ,
906
- ] )
907
- : '' ,
943
+ Template . asString (
944
+ Object . entries ( attributes ) . map ( ( [ key , value ] ) => {
945
+ if ( safeAttrs . includes ( key ) ) {
946
+ return `linkTag.${ key } = ${ value } ;` ;
947
+ }
948
+
949
+ return `linkTag.setAttribute(${ JSON . stringify (
950
+ key
951
+ ) } , ${ value } );`;
952
+ } )
953
+ ) ,
908
954
typeof this . runtimeOptions . insert !== 'undefined'
909
955
? typeof this . runtimeOptions . insert === 'function'
910
956
? `(${ this . runtimeOptions . insert . toString ( ) } )(linkTag)`
@@ -945,7 +991,7 @@ class MiniCssExtractPlugin {
945
991
'resolve, reject' ,
946
992
[
947
993
`var href = ${ RuntimeGlobals . require } .miniCssF(chunkId);` ,
948
- `var fullhref = ${ RuntimeGlobals . publicPath } + href;` ,
994
+ `var fullhref = ${ attributes . href } ;` ,
949
995
'if(findStylesheet(href, fullhref)) return resolve();' ,
950
996
'createStylesheet(chunkId, fullhref, resolve, reject);' ,
951
997
]
@@ -1016,7 +1062,7 @@ class MiniCssExtractPlugin {
1016
1062
'chunkId' ,
1017
1063
[
1018
1064
`var href = ${ RuntimeGlobals . require } .miniCssF(chunkId);` ,
1019
- `var fullhref = ${ RuntimeGlobals . publicPath } + href;` ,
1065
+ `var fullhref = ${ attributes . href } ;` ,
1020
1066
'var oldTag = findStylesheet(href, fullhref);' ,
1021
1067
'if(!oldTag) return;' ,
1022
1068
`promises.push(new Promise(${ runtimeTemplate . basicFunction (
0 commit comments