Skip to content

Commit b059e07

Browse files
authored
Merge pull request webpack#6176 from mikegreiling/fix-no-fail-on-child-compilation-error
Fix issue in which webpack exits with code 0 when errors exist in child compilations
2 parents 8b0a2ad + da6dba2 commit b059e07

File tree

12 files changed

+126
-6
lines changed

12 files changed

+126
-6
lines changed

lib/NoEmitOnErrorsPlugin.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
class NoEmitOnErrorsPlugin {
88
apply(compiler) {
99
compiler.plugin("should-emit", (compilation) => {
10-
if(compilation.errors.length > 0)
10+
if(compilation.getStats().hasErrors())
1111
return false;
1212
});
1313
compiler.plugin("compilation", (compilation) => {
1414
compilation.plugin("should-record", () => {
15-
if(compilation.errors.length > 0)
15+
if(compilation.getStats().hasErrors())
1616
return false;
1717
});
1818
});

lib/Stats.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@ class Stats {
5050
}
5151

5252
hasWarnings() {
53-
return this.compilation.warnings.length > 0;
53+
return this.compilation.warnings.length > 0 ||
54+
this.compilation.children.some(child => child.getStats().hasWarnings());
5455
}
5556

5657
hasErrors() {
57-
return this.compilation.errors.length > 0;
58+
return this.compilation.errors.length > 0 ||
59+
this.compilation.children.some(child => child.getStats().hasErrors());
5860
}
5961

6062
// remove a prefixed "!" that can be specified to reverse sort order

test/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ If you are trying to solve a bug which is reproducible when x and y properties a
3535

3636
In addition to an `index.js`, these configCases require a `webpack.config.js` is located inside of your test suite. This will run this specific config through `webpack` just as you were building individually. They will use the same loading/bundling technique of your `it()` tests, however you now have a more specific config use cases that you can write even before you start coding.
3737

38-
#### statsCases (`Stats.test.js`)
38+
#### statsCases (`StatsTestCases.test.js`)
3939
Stats cases are similar to configCases except specifically focusing on the `expected` output of your stats. Instead of writing to the console, however the output of stats will be written to disk.
4040

4141
By default, the "expected" outcome is a pain to write by hand so instead when statsCases are run the following happens:

test/Stats.unittest.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ describe("Stats", () => {
1010
describe("does have", () => {
1111
it("hasErrors", () => {
1212
const mockStats = new Stats({
13+
children: [],
1314
errors: ["firstError"],
1415
hash: "1234"
1516
});
1617
mockStats.hasErrors().should.be.ok();
1718
});
1819
it("hasWarnings", () => {
1920
const mockStats = new Stats({
21+
children: [],
2022
warnings: ["firstError"],
2123
hash: "1234"
2224
});
@@ -26,19 +28,49 @@ describe("Stats", () => {
2628
describe("does not have", () => {
2729
it("hasErrors", () => {
2830
const mockStats = new Stats({
31+
children: [],
2932
errors: [],
3033
hash: "1234"
3134
});
3235
mockStats.hasErrors().should.not.be.ok();
3336
});
3437
it("hasWarnings", () => {
3538
const mockStats = new Stats({
39+
children: [],
3640
warnings: [],
3741
hash: "1234"
3842
});
3943
mockStats.hasWarnings().should.not.be.ok();
4044
});
4145
});
46+
describe("children have", () => {
47+
it("hasErrors", () => {
48+
const mockStats = new Stats({
49+
children: [{
50+
getStats: () => new Stats({
51+
errors: ["firstError"],
52+
hash: "5678"
53+
}),
54+
}],
55+
errors: [],
56+
hash: "1234"
57+
});
58+
mockStats.hasErrors().should.be.ok();
59+
});
60+
it("hasWarnings", () => {
61+
const mockStats = new Stats({
62+
children: [{
63+
getStats: () => new Stats({
64+
warnings: ["firstError"],
65+
hash: "5678"
66+
}),
67+
}],
68+
warnings: [],
69+
hash: "1234"
70+
});
71+
mockStats.hasWarnings().should.be.ok();
72+
});
73+
});
4274
it("formatError handles string errors", () => {
4375
const mockStats = new Stats({
4476
errors: ["firstError"],

test/StatsTestCases.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe("StatsTestCases", () => {
5252
if(/error$/.test(testName)) {
5353
stats.hasErrors().should.be.equal(true);
5454
} else if(stats.hasErrors()) {
55-
done(new Error(stats.toJson().errors.join("\n\n")));
55+
return done(new Error(stats.toJson().errors.join("\n\n")));
5656
}
5757

5858
let toStringOptions = {

test/binCases/errors/child-compilation-error/index.js

Whitespace-only changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"use strict";
2+
3+
module.exports = function testAssertions(code, stdout, stderr) {
4+
code.should.be.eql(2);
5+
6+
stdout[0].should.containEql("Hash: ");
7+
stdout[1].should.containEql("Version: ");
8+
stdout[2].should.containEql("Time: ");
9+
stdout[5].should.containEql("./index.js");
10+
stdout[8].should.containEql("ERROR in child compilation");
11+
12+
stderr.should.be.empty();
13+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"use strict";
2+
3+
module.exports = {
4+
entry: "./index",
5+
output: {
6+
filename: "bundle.js"
7+
},
8+
plugins: [{
9+
apply(compiler) {
10+
compiler.plugin("make", (compilation, cb) => {
11+
const child = compilation.createChildCompiler("child", {});
12+
child.plugin("compilation", childCompilation => {
13+
childCompilation.errors.push(new Error("child compilation"));
14+
});
15+
child.runAsChild(cb);
16+
});
17+
}
18+
}]
19+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"use strict";
2+
3+
var SingleEntryPlugin = require("../../../lib/SingleEntryPlugin");
4+
5+
/**
6+
* Runs a child compilation which produces an error in order to test that NoEmitErrorsPlugin
7+
* recognizes errors within child compilations.
8+
*/
9+
module.exports = class TestChildCompilationFailurePlugin {
10+
11+
constructor(output) {
12+
this.output = output;
13+
}
14+
15+
apply(compiler) {
16+
compiler.plugin("make", (compilation, cb) => {
17+
const child = compilation.createChildCompiler("child", this.output);
18+
child.plugin("compilation", childCompilation => {
19+
childCompilation.errors.push(new Error("forced error"));
20+
});
21+
child.apply(new SingleEntryPlugin(compiler.options.context, compiler.options.entry, "child"));
22+
child.runAsChild(cb);
23+
});
24+
}
25+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Hash: fb0670ebd6884ae658dc
2+
Time: Xms
3+
Asset Size Chunks Chunk Names
4+
child.js 2.47 kB
5+
bundle.js 2.47 kB 0 main
6+
[0] (webpack)/test/statsCases/no-emit-on-errors-plugin-with-child-error/index.js 0 bytes {0} [built]
7+
Child child:
8+
Asset Size Chunks Chunk Names
9+
child.js 2.47 kB 0 child
10+
[0] (webpack)/test/statsCases/no-emit-on-errors-plugin-with-child-error/index.js 0 bytes {0} [built]
11+
12+
ERROR in forced error

0 commit comments

Comments
 (0)