Skip to content

Commit 5f5413d

Browse files
authored
finalize webpack@4 implementation (#134)
* finalize webpack@4 implementation * test: reduce number of iterations There should no longer be any race condition possible, so it's pretty safe even if it could prevent the same issue not to be spotted * fix: webpack@3 plugin hook name
1 parent 052970e commit 5f5413d

6 files changed

Lines changed: 108 additions & 82 deletions

File tree

.travis.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ node_js:
33
- '9'
44
- '8'
55
- '6'
6-
- '4'
76

87
env:
98
- WEBPACK_VERSION=2 EXTRACT_PLUGIN_VERSION=2
109
- WEBPACK_VERSION=3 EXTRACT_PLUGIN_VERSION=3.0.2
11-
- WEBPACK_VERSION=4 EXTRACT_PLUGIN_VERSION=3.0.2
10+
- WEBPACK_VERSION=4 EXTRACT_PLUGIN_VERSION=next
1211

1312
install:
1413
- npm install

lib/plugin.js

Lines changed: 42 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ var path = require('path');
22
var fse = require('fs-extra');
33
var _ = require('lodash');
44

5-
var manifestMap = {};
5+
const emitCountMap = new Map();
66

77
function ManifestPlugin(opts) {
88
this.opts = _.assign({
@@ -35,6 +35,10 @@ ManifestPlugin.prototype.apply = function(compiler) {
3535
var seed = this.opts.seed || {};
3636
var moduleAssets = {};
3737

38+
var outputFolder = compiler.options.output.path;
39+
var outputFile = path.resolve(compiler.options.output.path, this.opts.fileName);
40+
var outputName = path.relative(outputFolder, outputFile);
41+
3842
var moduleAsset = function (module, file) {
3943
moduleAssets[file] = path.join(
4044
path.dirname(file),
@@ -43,6 +47,9 @@ ManifestPlugin.prototype.apply = function(compiler) {
4347
};
4448

4549
var emit = function(compilation, compileCallback) {
50+
const emitCount = emitCountMap.get(outputName) - 1
51+
emitCountMap.set(outputName, emitCount);
52+
4653
var publicPath = compilation.options.output.publicPath;
4754
var stats = compilation.getStats().toJson();
4855

@@ -106,7 +113,7 @@ ManifestPlugin.prototype.apply = function(compiler) {
106113
// Don't add hot updates to manifest
107114
var isUpdateChunk = file.path.indexOf('hot-update') >= 0;
108115
// Don't add manifest from another instance
109-
var isManifest = manifestMap[file.name];
116+
var isManifest = emitCountMap.get(file.name) !== undefined;
110117

111118
return !isUpdateChunk && !isManifest;
112119
});
@@ -158,74 +165,59 @@ ManifestPlugin.prototype.apply = function(compiler) {
158165
}, seed);
159166
}
160167

161-
var output = this.opts.serialize(manifest);
162-
163-
var outputFolder = compilation.options.output.path;
164-
var outputFile = path.resolve(compilation.options.output.path, this.opts.fileName);
165-
var outputName = path.relative(outputFolder, outputFile);
168+
const isLastEmit = emitCount === 0
169+
if (isLastEmit) {
170+
var output = this.opts.serialize(manifest);
166171

167-
compilation.assets[outputName] = {
168-
source: function() {
169-
return output;
170-
},
171-
size: function() {
172-
return output.length;
173-
}
174-
};
175-
176-
if (this.opts.writeToFileEmit) {
177-
fse.outputFileSync(outputFile, output);
178-
}
179-
180-
if (!manifestMap[outputName]) {
181-
manifestMap[outputName] = {
182-
running: false,
183-
queue: []
172+
compilation.assets[outputName] = {
173+
source: function() {
174+
return output;
175+
},
176+
size: function() {
177+
return output.length;
178+
}
184179
};
185-
}
186180

187-
function unqueueNext() {
188-
if (manifestMap[outputName].queue.length > 0) {
189-
manifestMap[outputName].running = true;
190-
manifestMap[outputName].queue.shift()();
191-
} else {
192-
manifestMap[outputName].running = false;
181+
if (this.opts.writeToFileEmit) {
182+
fse.outputFileSync(outputFile, output);
193183
}
194184
}
195185

186+
if (compiler.hooks) {
187+
compiler.hooks.webpackManifestPluginAfterEmit.call(manifest);
188+
} else {
189+
compilation.applyPluginsAsync('webpack-manifest-plugin-after-emit', manifest, compileCallback);
190+
}
191+
}.bind(this);
196192

197-
manifestMap[outputName].queue.push(function () {
198-
if (compiler.hooks) {
199-
compiler.hooks.afterEmit.tap('ManifestPlugin', function(compilation) {
200-
// TODO: when we deprecate webpack < 3, we can remove the queue logic
201-
unqueueNext()
202-
});
203-
} else {
204-
compiler.plugin('after-emit', function(compilation, cb) {
205-
unqueueNext();
206-
cb();
207-
});
208-
209-
compilation.applyPluginsAsync('webpack-manifest-plugin-after-emit', manifest, compileCallback);
210-
}
211-
})
193+
function beforeRun (compiler, callback) {
194+
let emitCount = emitCountMap.get(outputName) || 0;
195+
emitCountMap.set(outputName, emitCount + 1);
212196

213-
if(!manifestMap[outputName].running) {
214-
manifestMap[outputName].running = true;
215-
manifestMap[outputName].queue.shift()();
197+
if (callback) {
198+
callback();
216199
}
217-
}.bind(this);
200+
}
218201

219202
if (compiler.hooks) {
203+
const SyncWaterfallHook = require('tapable').SyncWaterfallHook;
204+
compiler.hooks.webpackManifestPluginAfterEmit = new SyncWaterfallHook(['manifest']);
205+
220206
compiler.hooks.compilation.tap('ManifestPlugin', function (compilation) {
221207
compilation.hooks.moduleAsset.tap('ManifestPlugin', moduleAsset);
222208
});
223209
compiler.hooks.emit.tap('ManifestPlugin', emit);
210+
211+
compiler.hooks.run.tap('ManifestPlugin', beforeRun);
212+
compiler.hooks.watchRun.tap('ManifestPlugin', beforeRun);
224213
} else {
225214
compiler.plugin('compilation', function (compilation) {
226215
compilation.plugin('module-asset', moduleAsset);
227216
});
228217
compiler.plugin('emit', emit);
218+
219+
compiler.plugin('before-run', beforeRun);
220+
compiler.plugin('watch-run', beforeRun);
229221
}
230222
};
231223

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"author": "Dane Thurber <dane.thurber@gmail.com>",
1010
"license": "MIT",
1111
"engines": {
12-
"node": ">=4"
12+
"node": ">=6.11.5"
1313
},
1414
"peerDependencies": {
1515
"webpack": "2 || 3 || 4"
@@ -18,7 +18,7 @@
1818
"codecov": "^2.2.0",
1919
"css-loader": "^0.9.1",
2020
"extract-text-webpack-plugin": "^3.0.2",
21-
"file-loader": "^0.9.0",
21+
"file-loader": "^1.1.11",
2222
"jasmine": "^2.2.1",
2323
"memory-fs": "^0.2.0",
2424
"nyc": "^10.3.2",
@@ -40,7 +40,8 @@
4040
"homepage": "https://github.com/danethurber/webpack-manifest-plugin",
4141
"dependencies": {
4242
"fs-extra": "^0.30.0",
43-
"lodash": ">=3.5 <5"
43+
"lodash": ">=3.5 <5",
44+
"tapable": "^1.0.0"
4445
},
4546
"nyc": {
4647
"reporter": [

spec/helpers/copy-plugin-mock.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ function FakeCopyWebpackPlugin() {
22
};
33

44
FakeCopyWebpackPlugin.prototype.apply = function (compiler) {
5-
compiler.plugin('emit', function (compilation, callback) {
5+
const emit = function (compilation, callback) {
66

77
var compiledMock = '// some compilation result\n';
88
compilation.assets['third.party.js'] = {
@@ -15,7 +15,13 @@ FakeCopyWebpackPlugin.prototype.apply = function (compiler) {
1515
};
1616

1717
callback();
18-
});
18+
};
19+
20+
if (compiler.hooks) {
21+
compiler.hooks.emit.tapAsync('FakeCopyWebpackPlugin', emit)
22+
} else {
23+
compiler.plugin('emit', emit);
24+
}
1925
};
2026

2127
module.exports = FakeCopyWebpackPlugin;

spec/plugin.integration.spec.js

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var ManifestPlugin = require('../index.js');
1010

1111
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5 * 60 * 1000;
1212

13-
var isCI = (yes, no) => process.env.CI === 'true' ? yes : no;
13+
const isWebpack4 = (yes, no) => webpack.version && webpack.version.slice(0, 1) === '4' ? yes : no;
1414

1515
function webpackConfig(webpackOpts, opts) {
1616
return _.merge({
@@ -25,7 +25,7 @@ function webpackCompile(config, compilerOps, cb) {
2525

2626
_.assign(compiler, compilerOps);
2727

28-
compiler.watch({
28+
return compiler.watch({
2929
aggregateTimeout: 300,
3030
poll: true
3131
}, function(err, stats){
@@ -104,13 +104,18 @@ describe('ManifestPlugin using real fs', function() {
104104
this.manifest = null;
105105
}
106106
TestPlugin.prototype.apply = function (compiler) {
107-
var self = this;
108-
compiler.plugin('compilation', function (compilation) {
109-
compilation.plugin('webpack-manifest-plugin-after-emit', function (manifest, callback) {
110-
self.manifest = manifest;
111-
callback();
107+
if (compiler.hooks) {
108+
compiler.hooks.webpackManifestPluginAfterEmit.tap('ManifestPlugin', (manifest) => {
109+
this.manifest = manifest;
110+
})
111+
} else {
112+
compiler.plugin('compilation', (compilation) => {
113+
compilation.plugin('webpack-manifest-plugin-after-emit', (manifest, callback) => {
114+
this.manifest = manifest;
115+
callback();
116+
});
112117
});
113-
});
118+
}
114119
};
115120

116121
var testPlugin = new TestPlugin();
@@ -145,7 +150,7 @@ describe('ManifestPlugin using real fs', function() {
145150
});
146151

147152
it('outputs a manifest of one file', function(done) {
148-
webpackCompile({
153+
const compiler = webpackCompile({
149154
context: __dirname,
150155
output: {
151156
filename: '[name].[hash].js',
@@ -169,6 +174,7 @@ describe('ManifestPlugin using real fs', function() {
169174

170175
if (hashes.length === 2) {
171176
expect(hashes[0]).not.toEqual(hashes[1]);
177+
compiler.close()
172178
return done();
173179
}
174180

@@ -178,7 +184,7 @@ describe('ManifestPlugin using real fs', function() {
178184
});
179185

180186
describe('multiple compilation', function() {
181-
var nbCompiler = isCI(4000, 1000);
187+
var nbCompiler = 10;
182188
var originalTimeout;
183189
beforeEach(function() {
184190
rimraf.sync(path.join(__dirname, 'output/multiple-compilation'));
@@ -199,15 +205,7 @@ describe('ManifestPlugin using real fs', function() {
199205
plugins: [
200206
new ManifestPlugin({
201207
seed
202-
}),
203-
function () {
204-
var compiler = this;
205-
206-
compiler.plugin('after-emit', function(compilation, cb) {
207-
JSON.parse(fse.readFileSync(path.join(__dirname, 'output/multiple-compilation/manifest.json')));
208-
cb();
209-
});
210-
}
208+
})
211209
]
212210
})), {}, function() {
213211
var manifest = JSON.parse(fse.readFileSync(path.join(__dirname, 'output/multiple-compilation/manifest.json')))

spec/plugin.spec.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ var plugin = require('../index.js');
1010
var OUTPUT_DIR = path.join(__dirname, './webpack-out');
1111
var manifestPath = path.join(OUTPUT_DIR, 'manifest.json');
1212

13+
const isWebpack4 = (yes, no) => webpack.version && webpack.version.slice(0, 1) === '4' ? yes : no;
14+
1315
function webpackConfig (webpackOpts, opts) {
1416
return _.merge({
1517
output: {
@@ -315,11 +317,21 @@ describe('ManifestPlugin', function() {
315317
webpackCompile({
316318
context: __dirname,
317319
entry: './fixtures/file.txt',
318-
module: {
320+
module: isWebpack4({
321+
rules: [{
322+
test: /\.(txt)/,
323+
use: [{
324+
loader: 'file-loader',
325+
options: {
326+
name: '[name].[ext]'
327+
}
328+
}]
329+
}]
330+
}, {
319331
loaders: [
320332
{ test: /\.(txt)/, loader: 'file-loader?name=file.[ext]' },
321333
]
322-
}
334+
})
323335
}, {}, function(manifest, stats) {
324336
expect(manifest).toBeDefined();
325337
expect(manifest).toEqual({
@@ -335,11 +347,21 @@ describe('ManifestPlugin', function() {
335347
webpackCompile({
336348
context: __dirname,
337349
entry: './fixtures/file.txt',
338-
module: {
350+
module: isWebpack4({
351+
rules: [{
352+
test: /\.(txt)/,
353+
use: [{
354+
loader: 'file-loader',
355+
options: {
356+
name: 'outputfile.[ext]'
357+
}
358+
}]
359+
}]
360+
}, {
339361
loaders: [
340362
{ test: /\.(txt)/, loader: 'file-loader?name=outputfile.[ext]' },
341363
]
342-
}
364+
})
343365
}, {}, function(manifest, stats) {
344366
expect(manifest).toBeDefined();
345367
expect(manifest).toEqual({
@@ -400,15 +422,23 @@ describe('ManifestPlugin', function() {
400422
output: {
401423
filename: '[name].js'
402424
},
403-
module: {
425+
module: isWebpack4({
426+
rules: [{
427+
test: /\.css$/,
428+
use: ExtractTextPlugin.extract({
429+
fallback: 'style-loader',
430+
use: 'css-loader'
431+
})
432+
}]
433+
}, {
404434
loaders: [{
405435
test: /\.css$/,
406436
loader: ExtractTextPlugin.extract({
407437
fallback: 'style-loader',
408438
use: 'css-loader'
409439
})
410440
}]
411-
},
441+
}),
412442
plugins: [
413443
new plugin(),
414444
new ExtractTextPlugin({

0 commit comments

Comments
 (0)