shellscape/koa-webpack

Can I get a simple, up-to-date and working example for hot-reloading?

Closed this issue ยท 11 comments

  • Node Version:
    v11.10.0
  • NPM Version:
    6.7.0
  • koa Version:
    2.7.0
  • koa-wepback Version:
    5.2.1

I think the README.md should have a clear example on how to make a simple app work with hot-reloading. I've been trying to make this repo work for a couple days now and I can't get any changes inside my server's code to refresh my page.

router.get('/', (ctx, next) => {
    ctx.body = "Doesn't change unless a force a refresh manually";
});

I've looked at multiple examples in this GitHub issues and they're all old and out-dated. Now the middleware uses Async/Await and it's really unclear where to use it (before or after initialising route).
It's also unclear what should exactly be in my webpack.config for hot-reloading and if I still have the need to do this in my index file.

if (module.hot) {
    {...}
}

This is not a request for support, it's an actual issue that there's so little practical documentation on how to make it work correctly, a simple example would go a long way.

Now the middleware uses Async/Await and it's really unclear where to use it

That's a koa fundamentals issue more than anything. I would recommend on reading up more on that front.

It's also unclear what should exactly be in my webpack.config for hot-reloading and if I still have the need to do this in my index file.

Well, from the README:

This module wraps and composes webpack-dev-middleware and webpack-hot-client into a single middleware module, allowing for quick and concise implementation.

That means you're asking for support (really, you are asking for support) in the wrong place. For HMR related questions, the webpack-hot-client repo is where you should head.

I really don't feel that any more documentation is needed in this repository. This is not a module for teaching webpack, nor is it a module to support the two middleware/modules that this middleware composes. It sounds like there's a fundamental misunderstanding of what this module does and what its place is, and that'd point back to understanding koa middleware better.

I'd welcome PRs to something like an /examples directory, but it's not something I'm going to be spending time on personally. You may find that other projects like webpack-plugin-serve or even the decrepit and aging webpack-dev-server fit your needs better, as they require less understanding of the inner workings and take care of most of the heavy lifting for you.

This is not a module for teaching webpack, nor is it a module to support the two middleware/modules that this middleware composes.

Very true, and your README accurately sums up what the module is ๐Ÿ‘ What isn't clear is why there's an await line in the middle here, especially since most of us coming to this module are likely not in an async function and are implementing Koa in the root of a file, where everything is synchronous.

An examples/ directory, with a single example of all this put together, something we could check out & sanity check against our own setups would be extremely useful, if anything to demonstrate how you expected to use this in our applications.

Eloquently put, though I still disagree.

especially since most of us coming to this module are likely not in an async function and are implementing Koa in the root of a file, where everything is synchronous.

If async/await were not core to Koa v2, you would have a valid point. However, since Koa v2 explicitly relies upon and is built around async/await, coming to this module and not expecting to have to use async/await is a bit naive, especially since the description mentions that the module is for Koa v2.

If async/awaitwere not core to Koa v2, you would have a valid point

Apologies ๐Ÿ™ That's not the point I'm making. I adore Koa due to it's core support for async/await, however this support is in the middleware functions themselves, i.e. once you enter the context scope with app.use(...). (AFAIK) Node itself doesn't particularly support a random await in the middle of synchronous code - it has to be inside an async function.

Quoting your README directly:

const Koa = require('koa');
const koaWebpack = require('koa-webpack');

const app = new Koa();
const options = { .. };
const middleware = await koaWebpack(options);

app.use(middleware);

Should it not be:

const Koa = require('koa');
const koaWebpack = require('koa-webpack');

async bootstrap() {
  const app = new Koa();
  const options = { .. };
  const middleware = await koaWebpack(options);

  app.use(middleware);
  return app;
}

bootstrap()
  .then(app => app.listen(8080))
  .catch(err => console.error(err));

In order to support this await keyword?

... And to bring this issue full-circle, a working example file included in this repo would have cleared this up!

If you're still unsure about this, check out the obligatory Koa hello-world example:

const Koa = require('koa');
const app = module.exports = new Koa();

app.use(async function(ctx) {
  ctx.body = 'Hello World';
});

if (!module.parent) app.listen(3000);

+1 for an example, found the example in this thread helpful for whatever it's worth.

I found the example in this thread very helpful as well. I had to do some fiddling to get it to work. Here is what worked for me:

  const Koa = require('koa');
  const koaWebpack = require('koa-webpack');

  async function bootstrap() {
    const app = new Koa();
    const options = { .. };
    const middleware = await koaWebpack(options);

    app.use(middleware);
    return app;
  }

  const app = bootstrap()
    .then(app => app.listen(8080))
    .catch(err => console.error(err));
const app = bootstrap()
  .then(app => app.listen(8080))
  .catch(err => console.error(err));

Bootstrapping a Koa app through a Promise on startup just seems mad to me, so here's how I bootstrapped this project before resorting to the natural webpack-dev-middleware & express ๐Ÿคทโ€โ™‚๏ธ

app.use((options => {
  let middleware = null;

  return async (ctx, next) => {
    if (!middleware) {
      middleware = await koaWebpack(options);
    }

    return middleware(ctx, next);
  };
})({
  // Options here
}));

Yea it basically comes down to Node not supporting top-level async/await ... do you understand why people are confused now @shellscape ? It's got nothing to do with how Koa works fundamentally.

I have yet to see any other koa library that requires you to asynchrounously set up your middleware, which in turn returns the actual asynchronous middlware function to plug into app.use().

In order to use your library it requires that we do some awkward bootstrapping as described in the previous few comments. This isn't the end of the world - but its not obvious and seems weird. Is there a reason the middleware can not be set up synchronously? I'm guessing it internally does file reading etc. using an asynchronous method.

Quite a few people commenting here, and it's good to see you all helping each other out.

Is there a reason the middleware can not be set up synchronously? I'm guessing it internally does file reading etc. using an asynchronous method.

Not quite. https://github.com/shellscape/koa-webpack/blob/master/lib/middleware.js#L14

While I will continue to keep the module up to date, I've moved on from webpack-dev-middleware as a solution for my projects - I've learned it's a pretty bad module, and a pretty bad way to go about it. Instead, I've moved onto webpack-plugin-ramdisk which is a far superior way to emit a build in memory, and comes with additional performance gains. (webpack-plugin-serve is going to be leveraging that as well). At some point we're going to abstract the client portion of webpack-plugin-serve and that'll make webpack-hot-client (the second half of koa-webpack) moot.

webpack-plugin-serve also ships with a waitForBuild option that makes the "wait for ready" portion of the code in this module moot. It also exposes the full middleware stack and the underlying koa app. If you're using koa + koa-webpack to hotwire a development server, you should really take a look at webpack-plugin-serve, because it's doing the same thing, comes with bonuses, and gives you just about full control.

This is probably the last comment I'll leave on this particular issue. To quote my reply right before I closed the issue:

I'd welcome PRs to something like an /examples directory, but it's not something I'm going to be spending time on personally.

It's up to you all to contribute examples that apply to the scenario in which you're using the module in the mean time.

Why do you hate providing examples? It's the easiest way for someone to learn/use your project.