Skip to content

Commit f392b88

Browse files
committed
- merged with latest extract-text-webpack-plugin
- merged with webpack-contrib/extract-text-webpack-plugin#546 - readded async css file reload - cleanup
1 parent b61184b commit f392b88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+576
-215
lines changed

hotModuleReplacement.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
module.exports = function(publicPath, outputFilename) {
22
if (document) {
3-
var origin = document.location.protocol + '//' + document.location.hostname + (document.location.port ? ':' + document.location.port: '');
4-
var newHref = origin + publicPath + outputFilename
3+
var origin = document.location.protocol +
4+
'//' + document.location.hostname + (document.location.port ? ':' + document.location.port: '');
5+
var newHref = origin + publicPath + outputFilename;
56
var styleSheets = document.getElementsByTagName('link');
67

78
//update the stylesheet corresponding to `outputFilename`
@@ -20,4 +21,4 @@ module.exports = function(publicPath, outputFilename) {
2021
}
2122
}
2223
}
23-
}
24+
};

index.js

+121-112
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,49 @@
11
/*
2-
MIT License http://www.opensource.org/licenses/mit-license.php
3-
Author Tobias Koppers @sokra
4-
*/
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
55
var fs = require('fs');
66
var ConcatSource = require("webpack-sources").ConcatSource;
7-
var CachedSource = require("webpack-sources").CachedSource;
87
var async = require("async");
98
var ExtractedModule = require("./ExtractedModule");
109
var Chunk = require("webpack/lib/Chunk");
10+
var NormalModule = require("webpack/lib/NormalModule");
1111
var OrderUndefinedError = require("./OrderUndefinedError");
1212
var loaderUtils = require("loader-utils");
13-
var schemaTester = require('./schema/validator');
14-
var loaderSchema = require('./schema/loader-schema');
15-
var pluginSchema = require('./schema/plugin-schema.json');
13+
var validateOptions = require('schema-utils');
14+
var path = require('path');
1615

1716
var NS = fs.realpathSync(__dirname);
18-
var DEV = process.env.NODE_ENV === 'development';
17+
1918
var nextId = 0;
2019

2120
function ExtractTextPluginCompilation() {
2221
this.modulesByIdentifier = {};
2322
}
2423

25-
// ExtractTextPlugin.prototype.mergeNonInitialChunks = function(chunk, intoChunk, checkedChunks) {
26-
// if (chunk.chunks) {
27-
// // Fix error when hot module replacement used with CommonsChunkPlugin
28-
// chunk.chunks = chunk.chunks.filter(function(c) {
29-
// return typeof c !== 'undefined';
30-
// })
31-
// }
24+
function isInitialOrHasNoParents(chunk) {
25+
return chunk.isInitial() || chunk.parents.length === 0;
26+
}
3227

33-
// if(!intoChunk) {
34-
// checkedChunks = [];
35-
// chunk.chunks.forEach(function(c) {
36-
// if(c.isInitial()) return;
37-
// this.mergeNonInitialChunks(c, chunk, checkedChunks);
38-
// }, this);
39-
// } else if(checkedChunks.indexOf(chunk) < 0) {
40-
// checkedChunks.push(chunk);
41-
// chunk.modules.slice().forEach(function(module) {
42-
// intoChunk.addModule(module);
43-
// module.addChunk(intoChunk);
44-
// });
45-
// chunk.chunks.forEach(function(c) {
46-
// if(c.isInitial()) return;
47-
// this.mergeNonInitialChunks(c, intoChunk, checkedChunks);
48-
// }, this);
49-
// }
50-
// };
28+
ExtractTextPlugin.prototype.mergeNonInitialChunks = function(chunk, intoChunk, checkedChunks) {
29+
if(!intoChunk) {
30+
checkedChunks = [];
31+
chunk.chunks.forEach(function(c) {
32+
if(isInitialOrHasNoParents(c)) return;
33+
this.mergeNonInitialChunks(c, chunk, checkedChunks);
34+
}, this);
35+
} else if(checkedChunks.indexOf(chunk) < 0) {
36+
checkedChunks.push(chunk);
37+
chunk.modules.slice().forEach(function(module) {
38+
intoChunk.addModule(module);
39+
module.addChunk(intoChunk);
40+
});
41+
chunk.chunks.forEach(function(c) {
42+
if(isInitialOrHasNoParents(c)) return;
43+
this.mergeNonInitialChunks(c, intoChunk, checkedChunks);
44+
}, this);
45+
}
46+
};
5147

5248
ExtractTextPluginCompilation.prototype.addModule = function(identifier, originalModule, source, additionalInformation, sourceMap, prevModules) {
5349
var m;
@@ -115,26 +111,25 @@ function getOrder(a, b) {
115111
}
116112

117113
function ExtractTextPlugin(options) {
118-
options = options || {}
119-
120114
if(arguments.length > 1) {
121115
throw new Error("Breaking change: ExtractTextPlugin now only takes a single argument. Either an options " +
122-
"object *or* the name of the result file.\n" +
123-
"Example: if your old code looked like this:\n" +
124-
" new ExtractTextPlugin('css/[name].css', { disable: false, allChunks: true })\n\n" +
125-
"You would change it to:\n" +
126-
" new ExtractTextPlugin({ filename: 'css/[name].css', disable: false, allChunks: true })\n\n" +
127-
"The available options are:\n" +
128-
" filename: string\n" +
129-
" allChunks: boolean\n" +
130-
" disable: boolean\n");
116+
"object *or* the name of the result file.\n" +
117+
"Example: if your old code looked like this:\n" +
118+
" new ExtractTextPlugin('css/[name].css', { disable: false, allChunks: true })\n\n" +
119+
"You would change it to:\n" +
120+
" new ExtractTextPlugin({ filename: 'css/[name].css', disable: false, allChunks: true })\n\n" +
121+
"The available options are:\n" +
122+
" filename: string\n" +
123+
" allChunks: boolean\n" +
124+
" disable: boolean\n" +
125+
" ignoreOrder: boolean\n");
131126
}
132127
if(isString(options)) {
133128
options = { filename: options };
134129
} else {
135-
schemaTester(pluginSchema, options);
130+
validateOptions(path.resolve(__dirname, './schema/plugin.json'), options, 'Extract Text Plugin');
136131
}
137-
this.filename = options.filename || (DEV ? '[name].css' : '[name].[contenthash].css');
132+
this.filename = options.filename;
138133
this.id = options.id != null ? options.id : ++nextId;
139134
this.options = {};
140135
mergeOptions(this.options, options);
@@ -192,15 +187,15 @@ ExtractTextPlugin.prototype.loader = function(options) {
192187
ExtractTextPlugin.prototype.extract = function(options) {
193188
if(arguments.length > 1) {
194189
throw new Error("Breaking change: extract now only takes a single argument. Either an options " +
195-
"object *or* the loader(s).\n" +
196-
"Example: if your old code looked like this:\n" +
197-
" ExtractTextPlugin.extract('style-loader', 'css-loader')\n\n" +
198-
"You would change it to:\n" +
199-
" ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' })\n\n" +
200-
"The available options are:\n" +
201-
" use: string | object | loader[]\n" +
202-
" fallback: string | object | loader[]\n" +
203-
" publicPath: string\n");
190+
"object *or* the loader(s).\n" +
191+
"Example: if your old code looked like this:\n" +
192+
" ExtractTextPlugin.extract('style-loader', 'css-loader')\n\n" +
193+
"You would change it to:\n" +
194+
" ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' })\n\n" +
195+
"The available options are:\n" +
196+
" use: string | object | loader[]\n" +
197+
" fallback: string | object | loader[]\n" +
198+
" publicPath: string\n");
204199
}
205200
if(options.fallbackLoader) {
206201
console.warn('fallbackLoader option has been deprecated - replace with "fallback"');
@@ -211,7 +206,7 @@ ExtractTextPlugin.prototype.extract = function(options) {
211206
if(Array.isArray(options) || isString(options) || typeof options.options === "object" || typeof options.query === 'object') {
212207
options = { loader: options };
213208
} else {
214-
schemaTester(loaderSchema, options);
209+
validateOptions(path.resolve(__dirname, './schema/loader.json'), options, 'Extract Text Plugin (Loader)');
215210
}
216211
var loader = options.use ||  options.loader;
217212
var before = options.fallback || options.fallbackLoader || [];
@@ -231,14 +226,15 @@ ExtractTextPlugin.prototype.extract = function(options) {
231226
return [this.loader(options)]
232227
.concat(before, loader)
233228
.map(getLoaderObject);
234-
}
229+
};
235230

236231
ExtractTextPlugin.extract = ExtractTextPlugin.prototype.extract.bind(ExtractTextPlugin);
237232

238233
ExtractTextPlugin.prototype.apply = function(compiler) {
239234
var options = this.options;
240235
compiler.plugin("this-compilation", function(compilation) {
241236
var extractCompilation = new ExtractTextPluginCompilation();
237+
var toRemoveModules = {};
242238
compilation.plugin("normal-module-loader", function(loaderContext, module) {
243239
loaderContext[NS] = function(content, opt) {
244240
if(options.disable)
@@ -274,29 +270,49 @@ ExtractTextPlugin.prototype.apply = function(compiler) {
274270
});
275271
async.forEach(chunks, function(chunk, callback) {
276272
var extractedChunk = extractedChunks[chunks.indexOf(chunk)];
277-
278-
// SETTING THIS TO TRUE INSURES ALL CHUNKS ARE HANDLED:
279-
var shouldExtract = true; //!!(options.allChunks || chunk.isInitial());
280-
273+
var shouldExtract = !!(options.allChunks || isInitialOrHasNoParents(chunk));
281274
async.forEach(chunk.modules.slice(), function(module, callback) {
282275
var meta = module[NS];
283276
if(meta && (!meta.options.id || meta.options.id === id)) {
284277
var wasExtracted = Array.isArray(meta.content);
285278
if(shouldExtract !== wasExtracted) {
286-
module[NS + "/extract"] = shouldExtract; // eslint-disable-line no-path-concat
287-
compilation.rebuildModule(module, function(err) {
279+
var newModule = new NormalModule(
280+
module.request,
281+
module.userRequest,
282+
module.rawRequest,
283+
module.loaders,
284+
module.resource,
285+
module.parser
286+
);
287+
newModule[NS + "/extract"] = shouldExtract; // eslint-disable-line no-path-concat
288+
// build a new module and save result to extracted compilations
289+
compilation.buildModule(newModule, false, newModule, null, function(err) {
288290
if(err) {
289291
compilation.errors.push(err);
290292
return callback();
291293
}
292-
meta = module[NS];
294+
meta = newModule[NS];
295+
// Error out if content is not an array and is not null
293296
if(!Array.isArray(meta.content) && meta.content != null) {
294-
err = new Error(module.identifier() + " doesn't export content");
297+
err = new Error(newModule.identifier() + " doesn't export content");
295298
compilation.errors.push(err);
296299
return callback();
297300
}
298-
if(meta.content)
299-
extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk);
301+
if(meta.content) {
302+
var ident = module.identifier();
303+
extractCompilation.addResultToChunk(ident, meta.content, newModule, extractedChunk);
304+
// remove generated result from chunk
305+
if(toRemoveModules[ident]) {
306+
toRemoveModules[ident].chunks.push(chunk)
307+
} else {
308+
toRemoveModules[ident] = {
309+
module: newModule,
310+
moduleToRemove: module,
311+
chunks: [chunk]
312+
};
313+
}
314+
315+
}
300316
callback();
301317
});
302318
} else {
@@ -311,23 +327,47 @@ ExtractTextPlugin.prototype.apply = function(compiler) {
311327
});
312328
}, function(err) {
313329
if(err) return callback(err);
314-
// REMOVING THIS CODE IS ALL THAT'S NEEDED TO CREATE CSS FILES PER CHUNK:
315-
// extractedChunks.forEach(function(extractedChunk) {
316-
// if(extractedChunk.isInitial())
317-
// this.mergeNonInitialChunks(extractedChunk);
318-
// }, this);
319-
// extractedChunks.forEach(function(extractedChunk) {
320-
// if(!extractedChunk.isInitial()) {
321-
// extractedChunk.modules.slice().forEach(function(module) {
322-
// extractedChunk.removeModule(module);
323-
// });
324-
// }
325-
// });
330+
extractedChunks.forEach(function(extractedChunk) {
331+
if(isInitialOrHasNoParents(extractedChunk))
332+
this.mergeNonInitialChunks(extractedChunk);
333+
}, this);
334+
extractedChunks.forEach(function(extractedChunk) {
335+
if(!isInitialOrHasNoParents(extractedChunk)) {
336+
extractedChunk.modules.slice().forEach(function(module) {
337+
extractedChunk.removeModule(module);
338+
});
339+
}
340+
});
326341
compilation.applyPlugins("optimize-extracted-chunks", extractedChunks);
327342
callback();
328343
}.bind(this));
329344
}.bind(this));
330-
345+
compilation.plugin("optimize-module-ids", function(modules){
346+
modules.forEach(function (module) {
347+
var data = toRemoveModules[module.identifier()];
348+
if (data) {
349+
var id = module.id;
350+
var newModule = new NormalModule(
351+
module.request,
352+
module.userRequest,
353+
module.rawRequest,
354+
module.loaders,
355+
module.resource,
356+
module.parser
357+
);
358+
newModule.id = id;
359+
newModule._source = data.module._source;
360+
data.chunks.forEach(function (chunk) {
361+
chunk.removeModule(data.moduleToRemove);
362+
var deps = data.moduleToRemove.dependencies.slice();
363+
deps.forEach(d => {
364+
chunk.removeModule(d.module);
365+
});
366+
chunk.addModule(newModule);
367+
});
368+
}
369+
});
370+
});
331371
compilation.plugin("additional-assets", function(callback) {
332372
extractedChunks.forEach(function(extractedChunk) {
333373
if(extractedChunk.modules.length) {
@@ -361,38 +401,7 @@ ExtractTextPlugin.prototype.apply = function(compiler) {
361401
});
362402
}
363403
}, this);
364-
365-
// duplicate js chunks into secondary files that don't have css injection,
366-
// giving the additional js files the extension: `.no_css.js`
367-
Object.keys(compilation.assets).forEach(function(name) {
368-
var asset = compilation.assets[name];
369-
370-
if (/\.js$/.test(name) && asset._source) {
371-
var regex = /\/\*__START_CSS__\*\/[\s\S]*?\/\*__END_CSS__\*\//g
372-
var source = asset.source();
373-
374-
if (!source.match(regex)) {
375-
return;
376-
}
377-
var newName = name.replace(/\.js/, '.no_css.js');
378-
var newAsset = new CachedSource(asset._source);
379-
// remove js that adds css to DOM via style-loader, so that React Loadable
380-
// can serve smaller files (without css) in initial request.
381-
newAsset._cachedSource = source.replace(regex, '');
382-
383-
compilation.assets[newName] = newAsset;
384-
385-
// add no_css file to files associated with chunk so that they are minified,
386-
// and receive source maps, and can be found by React Loadable
387-
extractedChunks.forEach(function(extractedChunk) {
388-
var chunk = extractedChunk.originalChunk;
389-
if (chunk.files.indexOf(name) > -1) {
390-
chunk.files.push(newName);
391-
}
392-
})
393-
}
394-
})
395-
callback()
404+
callback();
396405
}.bind(this));
397406
}.bind(this));
398407
};

0 commit comments

Comments
 (0)