@@ -70,19 +70,24 @@ export default class CSSSplitWebpackPlugin {
7070 * @param {Boolean|String } imports Truish to generate an additional import
7171 * asset. When a boolean use the default name for the asset.
7272 * @param {String } filename Control the generated split file name.
73+ * @param {Boolean } defer Defer splitting until the `emit` phase. Normally
74+ * only needed if something else in your pipeline is mangling things at
75+ * the emit phase too.
7376 * @param {Boolean } preserve True to keep the original unsplit file.
7477 */
7578 constructor ( {
7679 size = 4000 ,
7780 imports = false ,
7881 filename = '[name]-[part].[ext]' ,
7982 preserve,
83+ defer = false ,
8084 } ) {
8185 this . options = {
8286 size,
8387 imports : normalizeImports ( imports , preserve ) ,
8488 filename : nameInterpolator ( filename ) ,
8589 preserve,
90+ defer,
8691 } ;
8792 }
8893
@@ -121,6 +126,48 @@ export default class CSSSplitWebpackPlugin {
121126 } ) ;
122127 }
123128
129+ chunksMapping ( compilation , chunks , done ) {
130+ const assets = compilation . assets ;
131+ const publicPath = strip ( compilation . options . output . publicPath || './' ) ;
132+ const promises = chunks . map ( ( chunk ) => {
133+ const input = chunk . files . filter ( isCSS ) ;
134+ const items = input . map ( ( name ) => this . file ( name , assets [ name ] ) ) ;
135+ return Promise . all ( items ) . then ( ( entries ) => {
136+ entries . forEach ( ( entry ) => {
137+ // Skip the splitting operation for files that result in no
138+ // split occuring.
139+ if ( entry . chunks . length === 1 ) {
140+ return ;
141+ }
142+ // Inject the new files into the chunk.
143+ entry . chunks . forEach ( ( file ) => {
144+ assets [ file . _name ] = file ;
145+ chunk . files . push ( file . _name ) ;
146+ } ) ;
147+ const content = entry . chunks . map ( ( file ) => {
148+ return `@import "${ publicPath } /${ file . _name } ";` ;
149+ } ) . join ( '\n' ) ;
150+ const imports = this . options . imports ( {
151+ ...entry ,
152+ content,
153+ } ) ;
154+ if ( ! this . options . preserve ) {
155+ chunk . files . splice ( chunk . files . indexOf ( entry . file ) , 1 ) ;
156+ delete assets [ entry . file ] ;
157+ }
158+ if ( imports ) {
159+ assets [ imports ] = new RawSource ( content ) ;
160+ chunk . files . push ( imports ) ;
161+ }
162+ } ) ;
163+ return Promise . resolve ( ) ;
164+ } ) ;
165+ } ) ;
166+ Promise . all ( promises ) . then ( ( ) => {
167+ done ( ) ;
168+ } , done ) ;
169+ }
170+
124171 /**
125172 * Run the plugin against a webpack compiler instance. Roughly it walks all
126173 * the chunks searching for CSS files and when it finds one that needs to be
@@ -132,50 +179,21 @@ export default class CSSSplitWebpackPlugin {
132179 * @returns {void }
133180 */
134181 apply ( compiler : Object ) {
135- // Only run on `this-compilation` to avoid injecting the plugin into
136- // sub-compilers as happens when using the `extract-text-webpack-plugin`.
137- compiler . plugin ( 'this-compilation' , ( compilation ) => {
138- const assets = compilation . assets ;
139- const publicPath = strip ( compilation . options . output . publicPath || './' ) ;
140- compilation . plugin ( 'optimize-chunk-assets' , ( chunks , done ) => {
141- const promises = chunks . map ( ( chunk ) => {
142- const input = chunk . files . filter ( isCSS ) ;
143- const items = input . map ( ( name ) => this . file ( name , assets [ name ] ) ) ;
144- return Promise . all ( items ) . then ( ( entries ) => {
145- entries . forEach ( ( entry ) => {
146- // Skip the splitting operation for files that result in no
147- // split occuring.
148- if ( entry . chunks . length === 1 ) {
149- return ;
150- }
151- // Inject the new files into the chunk.
152- entry . chunks . forEach ( ( file ) => {
153- assets [ file . _name ] = file ;
154- chunk . files . push ( file . _name ) ;
155- } ) ;
156- const content = entry . chunks . map ( ( file ) => {
157- return `@import "${ publicPath } /${ file . _name } ";` ;
158- } ) . join ( '\n' ) ;
159- const imports = this . options . imports ( {
160- ...entry ,
161- content,
162- } ) ;
163- if ( ! this . options . preserve ) {
164- chunk . files . splice ( chunk . files . indexOf ( entry . file ) , 1 ) ;
165- delete assets [ entry . file ] ;
166- }
167- if ( imports ) {
168- assets [ imports ] = new RawSource ( content ) ;
169- chunk . files . push ( imports ) ;
170- }
171- } ) ;
172- return Promise . resolve ( ) ;
173- } ) ;
182+ if ( this . options . defer ) {
183+ // Run on `emit` when user specifies the compiler phase
184+ // Due to the incorrect css split + optimization behavior
185+ // Expected: css split should happen after optimization
186+ compiler . plugin ( 'emit' , ( compilation , done ) => {
187+ return this . chunksMapping ( compilation , compilation . chunks , done ) ;
188+ } ) ;
189+ } else {
190+ // Only run on `this-compilation` to avoid injecting the plugin into
191+ // sub-compilers as happens when using the `extract-text-webpack-plugin`.
192+ compiler . plugin ( 'this-compilation' , ( compilation ) => {
193+ compilation . plugin ( 'optimize-chunk-assets' , ( chunks , done ) => {
194+ return this . chunksMapping ( compilation , chunks , done ) ;
174195 } ) ;
175- Promise . all ( promises ) . then ( ( ) => {
176- done ( ) ;
177- } , done ) ;
178196 } ) ;
179- } ) ;
197+ }
180198 }
181199}
0 commit comments