From f672f331920dd7545125213bab81059e758d0707 Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Thu, 26 Oct 2017 00:51:38 +0200 Subject: [PATCH] Make webpack benchmark synchronous --- jest.config.js | 10 ++++++++-- src/mocks/clear-immediate.js | 9 +++++++++ src/mocks/event-loop.js | 27 +++++++++++++++++++++++++++ src/mocks/process.js | 24 ++++++++++++++++++++++++ src/mocks/set-immediate.js | 14 ++++++++++++++ src/webpack-benchmark.js | 34 +++++++++++++++++++++++----------- src/webpack-benchmark.test.js | 8 ++------ webpack.config.js | 35 +++++++++++++++++++++++++++++++++-- 8 files changed, 140 insertions(+), 21 deletions(-) create mode 100644 src/mocks/clear-immediate.js create mode 100644 src/mocks/event-loop.js create mode 100644 src/mocks/process.js create mode 100644 src/mocks/set-immediate.js diff --git a/jest.config.js b/jest.config.js index 48d59bad..a520ccec 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,13 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + const path = require("path"); module.exports = { - watchPathIgnorePatterns: [ + rootDir: path.resolve(__dirname, "src"), + transformIgnorePatterns: [ + "/node_modules/", path.resolve(__dirname, "build") ] -} +}; diff --git a/src/mocks/clear-immediate.js b/src/mocks/clear-immediate.js new file mode 100644 index 00000000..2884160a --- /dev/null +++ b/src/mocks/clear-immediate.js @@ -0,0 +1,9 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +const eventLoop = require("./event-loop"); + +module.exports = task => { + eventLoop.cancel(task); +}; diff --git a/src/mocks/event-loop.js b/src/mocks/event-loop.js new file mode 100644 index 00000000..fdfc7fa0 --- /dev/null +++ b/src/mocks/event-loop.js @@ -0,0 +1,27 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a very simple event loop implementation. It does not cover the exact behavior +// in node, especially not the differences between process.nextTick and setImmediate. +// It is, however, sufficient for the benchmark. +const tasks = []; + +module.exports = { + schedule(task) { + tasks.push(task); + }, + cancel(task) { + const i = tasks.indexOf(task); + + if (i > -1) { + tasks.splice(i, 1); + } + }, + run() { + let task; + while ((task = tasks.shift())) { + task(); + } + } +}; diff --git a/src/mocks/process.js b/src/mocks/process.js new file mode 100644 index 00000000..482b37a3 --- /dev/null +++ b/src/mocks/process.js @@ -0,0 +1,24 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +const eventLoop = require("./event-loop"); + +exports.nextTick = function(fn) { + const args = Array.prototype.slice.call(arguments, 1); + eventLoop.schedule(() => { + fn.apply(null, args); + }); +}; + +exports.platform = exports.arch = exports.execPath = exports.title = "browser"; +exports.pid = 1; +exports.browser = true; +exports.env = {}; +exports.argv = []; +exports.cwd = () => "/"; +exports.binding = name => { + throw new Error("No such module. (Possibly not yet loaded)"); +}; +exports.exit = exports.kill = exports.umask = exports.dlopen = exports.uptime = exports.memoryUsage = exports.uvCounters = () => {}; +exports.features = {}; diff --git a/src/mocks/set-immediate.js b/src/mocks/set-immediate.js new file mode 100644 index 00000000..c05ff775 --- /dev/null +++ b/src/mocks/set-immediate.js @@ -0,0 +1,14 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +const eventLoop = require("./event-loop"); + +module.exports = function(fn) { + const args = Array.prototype.slice.call(arguments, 1); + const task = () => { + fn.apply(null, args); + }; + eventLoop.schedule(task); + return task; +}; diff --git a/src/webpack-benchmark.js b/src/webpack-benchmark.js index 65440d24..0cb8b9f1 100644 --- a/src/webpack-benchmark.js +++ b/src/webpack-benchmark.js @@ -3,6 +3,7 @@ // found in the LICENSE file. const webpack = require("webpack"); +const eventLoop = require("./mocks/event-loop"); const payloads = [ { @@ -22,16 +23,27 @@ const payloads = [ module.exports = { name: "webpack", - defer: true, - fn(deferred) { - return Promise.all( - payloads.map( - config => - new Promise((resolve, reject) => { - const compiler = webpack(config); - compiler.run((err, stats) => void (err ? reject(err) : resolve())); - }) - ) - ).then(() => deferred.resolve()); + fn() { + payloads.forEach(config => { + let finished = false; + + eventLoop.schedule(() => { + const compiler = webpack(config); + compiler.run((err, stats) => { + if (err) { + throw err; + } + if (stats.hasErrors()) { + throw stats.compilation.errors[0]; + } + finished = true; + }); + }); + eventLoop.run(); + + if (finished !== true) { + throw new Error("Webpack did not finish synchronously"); + } + }); } }; diff --git a/src/webpack-benchmark.test.js b/src/webpack-benchmark.test.js index 8163446e..653edeaf 100644 --- a/src/webpack-benchmark.test.js +++ b/src/webpack-benchmark.test.js @@ -19,8 +19,7 @@ beforeAll( new Promise((resolve, reject) => { const baseConfig = webpackConfig[0]; const config = Object.assign({}, baseConfig, { - entry: require.resolve("./webpack-benchmark.js"), - bail: true + entry: require.resolve("./webpack-benchmark.js") }); config.output = Object.assign({}, baseConfig.output, { libraryTarget: "commonjs2", @@ -39,7 +38,4 @@ beforeAll( }) ); -it("webpack runs to completion", () => - new Promise(resolve => { - webpackBenchmark.fn({ resolve }); - })); +it("webpack runs to completion", () => void webpackBenchmark.fn()); diff --git a/webpack.config.js b/webpack.config.js index 0935bcbd..808d51bb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -22,10 +22,26 @@ module.exports = [ "graceful-fs": require.resolve("./src/vfs"), module: require.resolve("./src/mocks/dummy"), chokidar: require.resolve("./src/mocks/chokidar"), - "uglify-js": require.resolve("./src/mocks/dummy") + "uglify-js": require.resolve("./src/mocks/dummy"), + // These modules are used by virtualfs to fake async fs calls + "core-js/library/fn/set-immediate": require.resolve( + "./src/mocks/set-immediate" + ), + "core-js/library/fn/clear-immediate": require.resolve( + "./src/mocks/clear-immediate" + ) } }, + node: { + setImmediate: false, // this disables also clearImmediate + process: false + }, plugins: [ + new webpack.ProvidePlugin({ + setImmediate: require.resolve("./src/mocks/set-immediate"), + clearImmediate: require.resolve("./src/mocks/clear-immediate"), + process: require.resolve("./src/mocks/process") + }), new webpack.BannerPlugin({ banner: "// Required for JavaScript engine shells.\n" + @@ -52,10 +68,25 @@ module.exports = [ "graceful-fs": require.resolve("./src/vfs"), module: require.resolve("./src/mocks/dummy"), chokidar: require.resolve("./src/mocks/chokidar"), - "uglify-js": require.resolve("./src/mocks/dummy") + "uglify-js": require.resolve("./src/mocks/dummy"), + "core-js/library/fn/set-immediate": require.resolve( + "./src/mocks/set-immediate" + ), + "core-js/library/fn/clear-immediate": require.resolve( + "./src/mocks/clear-immediate" + ) } }, + node: { + setImmediate: false, + process: false + }, plugins: [ + new webpack.ProvidePlugin({ + setImmediate: require.resolve("./src/mocks/set-immediate"), + clearImmediate: require.resolve("./src/mocks/clear-immediate"), + process: require.resolve("./src/mocks/process") + }), new CopyWebpackPlugin([{ from: "style.css" }, { from: "Logo.png" }]), new webpack.BannerPlugin({ banner: