From a38305e0683fb2d3beeec7f82d0e04a54b998790 Mon Sep 17 00:00:00 2001 From: Vladislav Shkodin Date: Wed, 30 Jan 2019 14:34:11 +0200 Subject: [PATCH 1/2] feat: add waitForBuild option --- README.md | 6 +++ index.d.ts | 1 + lib/index.js | 22 ++++++++++ lib/server.js | 12 +++++- lib/validate.js | 3 +- test/fixtures/waitForBuild/app.js | 1 + .../waitForBuild/createWebpackConfig.js | 43 +++++++++++++++++++ test/waitForBuild.test.js | 32 ++++++++++++++ 8 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/waitForBuild/app.js create mode 100644 test/fixtures/waitForBuild/createWebpackConfig.js create mode 100644 test/waitForBuild.test.js diff --git a/README.md b/README.md index 267b23c5..0cdc83bd 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,12 @@ When the minimize button (yellow dot) is clicked, the overlay will shrink to a s status beacons

+### `waitForBuild` +Type: `boolean`
+Default: `false` + +If `true`, instructs the server to halt middleware processing until the current build is done. + ## Proxying Proxying with `webpack-plugin-serve` is supported via the [`middleware`](#middleware) option. But while this plugin module doesn't contain any fancy options processing for proxying, it does include access to the [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) module by default, and the rest should look familiar to users of `http-proxy-middleware`. diff --git a/index.d.ts b/index.d.ts index ac07ff4e..c955a11a 100644 --- a/index.d.ts +++ b/index.d.ts @@ -84,6 +84,7 @@ declare module 'webpack-plugin-serve' { progress?: boolean | 'minimal'; static?: string | Array; status?: boolean; + waitForBuild?: boolean; } export class WebpackPluginServe { diff --git a/lib/index.js b/lib/index.js index 3ca0d5d5..b7f75b64 100644 --- a/lib/index.js +++ b/lib/index.js @@ -98,6 +98,16 @@ class WebpackPluginServe extends EventEmitter { options.static = []; } + if (options.waitForBuild) { + this.compiled = false; + this.compiling = new Promise((resolve) => { + this.once('done', () => { + this.compiled = true; + resolve(); + }); + }); + } + this.app = new Koa(); this.log = getLogger(options.log || {}); this.options = options; @@ -154,6 +164,18 @@ class WebpackPluginServe extends EventEmitter { invalid.tap(key, (filePath) => this.emit('invalid', filePath, compiler)); watchClose.tap(key, () => this.emit('close', compiler)); + if (this.options.waitForBuild) { + this.on('invalid', () => { + this.compiled = false; + this.compiling = new Promise((resolve) => { + this.once('done', () => { + this.compiled = true; + resolve(); + }); + }); + }); + } + compiler.hooks.compilation.tap(key, (compilation) => { compilation.hooks.afterHash.tap(key, () => { // webpack still has a 4 year old bug whereby in watch mode, file timestamps aren't properly diff --git a/lib/server.js b/lib/server.js index b691c537..30af1784 100644 --- a/lib/server.js +++ b/lib/server.js @@ -53,12 +53,22 @@ const start = async function start() { } const { app } = this; - const { host, middleware, port } = this.options; + const { host, middleware, port, waitForBuild } = this.options; const builtins = getBuiltins(app, this.options); this.options.host = await host; this.options.port = await port; + if (waitForBuild) { + app.use(async (ctx, next) => { + if (!this.compiled) { + await this.compiling; + } + + await next(); + }); + } + // allow users to add and manipulate middleware in the config await middleware(app, builtins); diff --git a/lib/validate.js b/lib/validate.js index 406a2e2e..cb327c1a 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -52,7 +52,8 @@ module.exports = { secure: any().forbidden(), // prettier-ignore static: [string().allow(null), array().items(string())], - status: boolean() + status: boolean(), + waitForBuild: boolean() }; const schema = object().keys(keys); const results = validate(options, schema); diff --git a/test/fixtures/waitForBuild/app.js b/test/fixtures/waitForBuild/app.js new file mode 100644 index 00000000..76e8a568 --- /dev/null +++ b/test/fixtures/waitForBuild/app.js @@ -0,0 +1 @@ +module.exports = 'hello'; diff --git a/test/fixtures/waitForBuild/createWebpackConfig.js b/test/fixtures/waitForBuild/createWebpackConfig.js new file mode 100644 index 00000000..5c7586a1 --- /dev/null +++ b/test/fixtures/waitForBuild/createWebpackConfig.js @@ -0,0 +1,43 @@ +const { resolve } = require('path'); + +const { WebpackPluginServe: Serve } = require('../../../lib/'); + +function createConfig(port) { + const serve = new Serve({ + host: 'localhost', + port, + waitForBuild: true, + middleware: (app) => { + app.use(async (ctx, next) => { + if (ctx.url === '/test') { + try { + require(resolve(__dirname, './output/output.js')); + ctx.body = 'success'; + } catch (e) { + ctx.body = 'error'; + } + } + await next(); + }); + } + }); + + const config = { + context: __dirname, + entry: ['./app.js'], + mode: 'development', + output: { + filename: './output.js', + path: resolve(__dirname, './output'), + publicPath: 'output/', + libraryTarget: 'commonjs2' + }, + plugins: [serve], + target: 'node', + watch: true + }; + + return { serve, config }; +} + +module.exports = createConfig; diff --git a/test/waitForBuild.test.js b/test/waitForBuild.test.js new file mode 100644 index 00000000..aba2246e --- /dev/null +++ b/test/waitForBuild.test.js @@ -0,0 +1,32 @@ +const getPort = require('get-port'); +const del = require('del'); +const webpack = require('webpack'); +const test = require('ava'); +const fetch = require('node-fetch'); +const defer = require('p-defer'); + +const createWebpackConfig = require('./fixtures/waitForBuild/createWebpackConfig'); + +let watcher; +let port; + +test.before('Starting server', async () => { + const deferred = defer(); + port = await getPort(); + const { serve, config } = createWebpackConfig(port); + const compiler = webpack(config); + watcher = compiler.watch({}, () => {}); + serve.on('listening', deferred.resolve); + await deferred.promise; +}); + +test.after.always('Closing server', async () => { + watcher.close(); + await del('./test/fixtures/waitForBuild/output'); +}); + +test('should wait until bundle is compiled', async (t) => { + const response = await fetch(`http://localhost:${port}/test`); + const text = await response.text(); + t.is(text, 'success'); +}); From 1d10e21bed8c0b485f7702936d06d951e45ec78a Mon Sep 17 00:00:00 2001 From: shellscape Date: Sun, 3 Feb 2019 09:07:28 -0500 Subject: [PATCH 2/2] refactor: trimming code, reorg --- lib/index.js | 33 ++++++++----------- lib/server.js | 5 +-- .../{waitForBuild => wait-for-build}/app.js | 0 .../make-config.js} | 11 ++++--- ...orBuild.test.js => wait-for-build.test.js} | 4 +-- 5 files changed, 23 insertions(+), 30 deletions(-) rename test/fixtures/{waitForBuild => wait-for-build}/app.js (100%) rename test/fixtures/{waitForBuild/createWebpackConfig.js => wait-for-build/make-config.js} (74%) rename test/{waitForBuild.test.js => wait-for-build.test.js} (85%) diff --git a/lib/index.js b/lib/index.js index b7f75b64..f11815e4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -98,20 +98,11 @@ class WebpackPluginServe extends EventEmitter { options.static = []; } - if (options.waitForBuild) { - this.compiled = false; - this.compiling = new Promise((resolve) => { - this.once('done', () => { - this.compiled = true; - resolve(); - }); - }); - } - this.app = new Koa(); this.log = getLogger(options.log || {}); this.options = options; this.compilers = []; + this.state = {}; } apply(compiler) { @@ -165,13 +156,15 @@ class WebpackPluginServe extends EventEmitter { watchClose.tap(key, () => this.emit('close', compiler)); if (this.options.waitForBuild) { + // track the first build of the bundle + this.state.compiling = new Promise((resolve) => { + this.once('done', () => resolve()); + }); + + // track subsequent builds from watching this.on('invalid', () => { - this.compiled = false; - this.compiling = new Promise((resolve) => { - this.once('done', () => { - this.compiled = true; - resolve(); - }); + this.state.compiling = new Promise((resolve) => { + this.once('done', () => resolve()); }); }); } @@ -190,14 +183,14 @@ class WebpackPluginServe extends EventEmitter { }); watchRun.tapPromise(key, async () => { - if (!this.startPromise) { + if (!this.state.starting) { // ensure we're only trying to start the server once - this.startPromise = start.bind(this)(); - this.startPromise.then(() => newline()); + this.state.starting = start.bind(this)(); + this.state.starting.then(() => newline()); } // wait for the server to startup so we can get our client connection info from it - await this.startPromise; + await this.state.starting; const compilerData = { // only set the compiler name if we're dealing with more than one compiler. otherwise, the diff --git a/lib/server.js b/lib/server.js index 30af1784..64cf79b8 100644 --- a/lib/server.js +++ b/lib/server.js @@ -61,10 +61,7 @@ const start = async function start() { if (waitForBuild) { app.use(async (ctx, next) => { - if (!this.compiled) { - await this.compiling; - } - + await this.state.compiling; await next(); }); } diff --git a/test/fixtures/waitForBuild/app.js b/test/fixtures/wait-for-build/app.js similarity index 100% rename from test/fixtures/waitForBuild/app.js rename to test/fixtures/wait-for-build/app.js diff --git a/test/fixtures/waitForBuild/createWebpackConfig.js b/test/fixtures/wait-for-build/make-config.js similarity index 74% rename from test/fixtures/waitForBuild/createWebpackConfig.js rename to test/fixtures/wait-for-build/make-config.js index 5c7586a1..b061f823 100644 --- a/test/fixtures/waitForBuild/createWebpackConfig.js +++ b/test/fixtures/wait-for-build/make-config.js @@ -1,8 +1,10 @@ +/* eslint-disable no-param-reassign */ const { resolve } = require('path'); const { WebpackPluginServe: Serve } = require('../../../lib/'); -function createConfig(port) { +const make = (port) => { + const outputPath = resolve(__dirname, './output/output.js'); const serve = new Serve({ host: 'localhost', port, @@ -11,7 +13,8 @@ function createConfig(port) { app.use(async (ctx, next) => { if (ctx.url === '/test') { try { - require(resolve(__dirname, './output/output.js')); + // eslint-disable-next-line import/no-dynamic-require, global-require + require(outputPath); ctx.body = 'success'; } catch (e) { ctx.body = 'error'; @@ -38,6 +41,6 @@ function createConfig(port) { }; return { serve, config }; -} +}; -module.exports = createConfig; +module.exports = { make }; diff --git a/test/waitForBuild.test.js b/test/wait-for-build.test.js similarity index 85% rename from test/waitForBuild.test.js rename to test/wait-for-build.test.js index aba2246e..d59b3b83 100644 --- a/test/waitForBuild.test.js +++ b/test/wait-for-build.test.js @@ -5,7 +5,7 @@ const test = require('ava'); const fetch = require('node-fetch'); const defer = require('p-defer'); -const createWebpackConfig = require('./fixtures/waitForBuild/createWebpackConfig'); +const { make } = require('./fixtures/wait-for-build/make-config'); let watcher; let port; @@ -13,7 +13,7 @@ let port; test.before('Starting server', async () => { const deferred = defer(); port = await getPort(); - const { serve, config } = createWebpackConfig(port); + const { serve, config } = make(port); const compiler = webpack(config); watcher = compiler.watch({}, () => {}); serve.on('listening', deferred.resolve);