Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Middleware? #1

Closed
mindplay-dk opened this issue Jun 3, 2022 · 10 comments
Closed

Middleware? #1

mindplay-dk opened this issue Jun 3, 2022 · 10 comments

Comments

@mindplay-dk
Copy link

Looking at the examples for Express, Koa, Hapi and Fastify, all of these examples rely on manually applying lazy-loading and Vite's response transformation on a case-by-case basis.

What do you think about adding middleware for these frameworks to handle the integration with Vite and this plugin?

(I attempted to write a middleware for Express myself, but half a day in, I have nothing to show, and I gave up. I tried to use on-headers, but res.getHeader("Content-Type") always returns undefined - the res object being passed to it isn't even the object being passed to the route. I was going to use type-is to check if the response type is HTML, to decide whether to apply Vite's HTML transform, but... I have no idea why this doesn't work. Documentation, typings, Github issues, Stack Overflow, existing Express middlewares, nothing got me any closer to understanding how this is supposed to work... does it work at all? I wonder...)

@cyco130
Copy link
Owner

cyco130 commented Jun 3, 2022

Hi!

Lazy loading has nothing to do with the middleware vs server mode, it's just an optional optimization. Here's an example without lazy loading. All vavite features, including hot reloading, are functional and the performance impact is minimal. Check out this bit in the docs to decide whether you need it. Vite caches module transforms so the impact should be minimal and, in any case, smaller than restarting the process with nodemon or similar..

The fact is, you can use the Vite middleware: Just default import viteDevServer from "vavite/vite-dev-server" and do app.use(viteDevServer.middlewares). But there are three problems with this approach:

The first one is, you have to start your own server (with app.listen or equivalent). But this will fail on the first hot reload because the port will be busy because of the previous execution of the entry module. You will have to come up with a way to stop the previous server and start a new one. I don't have a good solution for this, that's why I wrote vavite. Leaving the server creation to Vite is the whole point of it.

Second problem is that connect-style middlewares are not compatible with all frameworks out of the box. Koa has some additional rules (no early return, must call next), Fastify requires an external plugin, and Hapi doesn't have the concept of middleware at all (they believe it's just bad design).

The third problem is minor: vavite starts its own server. You can work around it by just using another port. You could set server.middewareMode in your Vite config but vavite will throw an error in that case. I could fix that but I think the above problems render this kind of usage mostly useless.

@mindplay-dk
Copy link
Author

Okay, so I don't think I need the lazy-loading - I'm not concerned about time required to bootstrap Express middleware and routes etc... this will probably be in the order of milliseconds.

What about the transformIndexHtml calls though?

That's what I was trying to get rid of, primarily - I don't want to integrate these by hand into every controller, that's all.

A middleware should be able to modify every HTML response and make the transformIndexHtml call?

Checking the import.meta.env.DEV flag during Express setup, and conditionally adding the middleware in development mode only, that should be possible, right?

@cyco130
Copy link
Owner

cyco130 commented Jun 3, 2022

OK, I understand now.

Unfortunately Express's middleware system is very limited, it doesn't allow you to modify the response because it only runs before the next handler, not after. What you can do is to monkey-patch the res object to provide a res.sendHtml method that calls transformIndexHtml. And then use it everywhere instead of res.send.

Here's an implementation of the idea, including the typings (check the file types.d.ts).

Maybe Express gurus know a with a better way but this is the best I can come up with :)

@mindplay-dk
Copy link
Author

From what I've seen of other middleware, monkey patching is pretty much the way it's done. I'm not even sure it's fair to call this "middleware". Coming from PHP and PSR middleware, the whole thing is honestly fraught with complexity - pretty much everything that was easy and testable in PSR middleware seems to require some sort of horrible hack in Express.

Koa doesn't seem much better? Now there's a "context" object for everything that doesn't exactly fit in either request or response objects, but other than that, it seems to have all the same complexity and mess.

Honestly, from what I've seen, if you want proper middleware, you'd have to invent it. I'm just not sure I have the energy for it. 😕

@cyco130
Copy link
Owner

cyco130 commented Jun 4, 2022

I hear your pain :) Check back HatTip in a month or so. It's still work in progress but you can already do this:

async function transformIndexHtmlMiddleware(req: Request, ctx: Context) {
  const response = await ctx.next();
  if (response.headers.get("content-type") === "text/html") {
    let content = await response.text();
    content = await viteDevServer!.transformIndexHtml(req.url, content);
    return new Response(content, response);
  }
  return response;
}

(Closing the issue now).

@cyco130 cyco130 closed this as completed Jun 4, 2022
@mindplay-dk
Copy link
Author

This looks promising. Starred and subscribed. 🙂

@FossPrime
Copy link

FossPrime commented Jun 18, 2022

The first one is, you have to start your own server (with app.listen or equivalent). But this will fail on the first hot reload because the port will be busy because of the previous execution of the entry module. You will have to come up with a way to stop the previous server and start a new one. I don't have a good solution for this, that's why I wrote vavite

I was able to work around this in 4 lines by using Global this as an evergreen cache of the server, that allows be to kill it if I see it. Line 69: https://stackblitz.com/edit/vitejs-vite-gx8b9j?file=src%2Fapp.ts&terminal=dev

Given that, for me HMR of server modules is Vavites core feature... as My production servers take 50 seconds to boot from cleaning up, testing, cleaningup, putting up scaffolds and then auto-configuring DB and SMTP based on if I can connect to them after a timeout. Not having to do that 100 times a day would be a HUGE time saver. ... However,

I was in exactly @mindplay-dk 's position today, spedning all day trying to get a small Socket.io server working with vavite. Will try again today. It sounds like the key to serverside HMR is the Dynamic ESM imports and Vite... so I will try that now.

The main downside of using Vite for the server is, the client HAS to be served by vite, on another port. In production this will mean we have to deploy the app twice, once with the server port disabled, and once with Vite's port disabled... the DevOps trickery needed to do this is not optimal :/

@cyco130
Copy link
Owner

cyco130 commented Jun 18, 2022

I've just opened issue #3 for this, I'll have a look.

But I don't understand the other issue. You can serve the client on the same port with Vite, just check the SSR examples. And if you don't want to build your client with Vite for some reason, you can still serve them via Vite by pointing to them in your publicDir option. Open another issue if you need more guidance on this.

@cyco130
Copy link
Owner

cyco130 commented Jun 18, 2022

Also, if you don't want to reuse Vite's server and create your own, vite-node might be a more mature solution. Reusing the server is the whole point of vavite .

@FossPrime
Copy link

Also, if you don't want to reuse Vite's server and create your own, vite-node might be a more mature solution. Reusing the server is the whole point of vavite .

Trying to roll my own HMR hasn't been as good as Vavite... I've been trying to slam HMR into this project, will try again with vite-node and one last time with Vavite... Never give up, never surrender
https://stackblitz.com/edit/feathers-vite-chat?file=test%2Fhmr.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants