Wait Until Webpack Dev Server Is Ready

The other answer here by Brett DeWoody is partially correct. It does properly wait until the server is started. However this does not wait for the bundle to be created. So if you ask webpack for a page at this point, it will return something akin to "still compiling".


To wait for the bundle to be fully compiled (and that the server is listening) before trying to access a page, you'll need something like this:

function makeServer(config) {
  return new Promise((resolve, reject) => {
    const compiler = webpack(config);

    let compiled = false;
    let listening = false;

    compiler.hooks.done.tap('IDoNotUnderstandWhatThisStringIsForButItCannotBeEmpty', () => {
      // console.log('compiled');

      if (listening) resolve(server);
      else compiled = true;
    });

    const server = new WebpackDevServer(compiler, config.devServer);

    server.listen(port, '0.0.0.0', err => {
      if (err) return reject(err);

      // console.log('listening');

      if (compiled) resolve(server);
      else listening = true;
    });
  });
}

Then, just like Brett's answer, await the result:

var server = await makeServer(config);

Or without async/await:

makeServer(config).then(server => {
  // Do something with server
});

One way to handle this is to wrap the server setup in a function that returns a Promise. The Promise is resolved when the server connects, or is rejected if there's an error.

Here's a simplified example:

function startServer() {
  return new Promise((resolve, reject) => {
    new WebpackDevServer(webpack(myConfig), {
      // Do stuff
    }).listen(3000, '0.0.0.0', function(err) {
      resolve();
    }).on('error', (error) => {
      reject(error);
    });
  });
}

and then provide a callback for when the server is started:

var server = startServer();

server.then(function() {
  // Run tests
});

Here's another solution (inspired by @CameronTacklind's answer). The setImmediate makes the log output show up below the initial compiler log output.

const PORT = process.env.PORT || 3000

const compiler = webpack(config)
new WebpackDevServer(compiler).listen(PORT, '0.0.0.0', err => {
    if (err) {
        console.log(err)
    }
})

compiler.hooks.done.tap('done', () => {
    setImmediate(() => {
        console.log()
        console.log(`Running at http://localhost:${PORT}`)
    })
})

UPDATE

Here's an alternative version that doesn't require a custom server script—you can just add this to your webpack config options. It also preserves the built-in feature of automatically using a different port if the default one isn't available...of course, if you'd rather specify your port, you don't have to use that part.

const chalk = require('chalk')
...

module.exports = {
    ....
    devServer: {
        onListening: server => {
            const { port } = server.listeningApp.address()
            server.compiler.hooks.done.tap('done', () => {
                setImmediate(() => {
                    console.log()
                    console.log(
                        chalk.cyan.bold(`Running at http://localhost:${port}`)
                    )
                })
            })
        },
    }
}