npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@vavite-giacomorebonato/reloader

v0.0.4

Published

Vite plugin for hot hot reloading server applications

Downloads

2

Readme

@vavite-giacomorebonato/reloader

@vavite-giacomorebonato/reloader is a plugin for developing and building Node.js server applications with Vite. It can be used with any Node.js server framework that allows you to provide your own http.Server instance, including but not limited to Express, Koa, Fastify, and Hapi.

Installation and usage

Install vite and @vavite-giacomorebonato/reloader as development dependencies (npm install --save-dev vite @vavite-giacomorebonato/reloader) and add @vavite-giacomorebonato/reloader to your Vite config:

import { defineConfig } from "vite";
import { reloader } from "@vavite-giacomorebonato/reloader";

export default defineConfig({
  plugins: [
    reloader({
      // Options, see below
    }),
  ],
});

Then install the server framework of your choice (e.g. Express) and create a server.ts file in the root of your project. Write your initialization code in such a way that when import.meta.env.PROD is true, it creates a normal server instance and when it's false, it uses the http.Server instance default imported from vavite/http-dev-server. How to do this depends on the framework, you can refer to the examples. Here's an example for Express:

import express from "express";
import httpDevServer from "@vavite-giacomorebonato/reloader/http-dev-server";

const app = express();

// Configure your server here
app.get("/", (req, res) => {
  res.send("Hello, world!");
});

if (import.meta.env.PROD) {
  // For production, start your server
  // as you would normally do.
  app.listen(3000, "localhost", () => {
    console.log("Server started on http://localhost:3000");
  });
} else {
  // For development, use httpDevServer.
  // An Express app instance is actually
  // a request listener function, this
  // is all we need to do:
  httpDevServer.on("request", app);
}

Now you can start the development server with npx vite and visit http://localhost:3000/ to see the result. Your server will be reloaded when you make any changes to the code.

You can build your server application for production with npx vite build --ssr and start it with node dist/server.

Lazy loading handlers

One of the most important advantages of Vite is its on-demand nature: Modules are only transpiled when they are actually used. By default, @vavite-giacomorebonato/reloader reloads your server entry every time one of its dependencies changes. Since the server entry is the the root of the dependency tree, this means any change in your server-side code will trigger a full reload. Although it works, it doesn't tap into the full potential of Vite.

A typical Node.js server application lifecycle consists of two phases. The first is the initialization phase where you create and configure your server instance and the second is the request listening phase where the application services incoming requests as they come in. Typically, the initialization code changes less often and request listeners change more often and more granularly.

@vavite-giacomorebonato/reloader can be used to lazy load request handlers to avoid re-executing the initialization code unnecessarily: If you set the configuration option reloadOn to "static-deps-change" (instead of the default "any-change"), @vavite-giacomorebonato/reloader will not reload the server entry when its dynamically imported dependencies change. For example, if you have an Express route listener like this:

import routeHandler from "./route-handler";

app.get("/my-route", routeHandler);

You can avoid re-executing your initialization code by refactoring it like this:

app.get("/my-route", async (req, res, next) => {
  // Omitting error handling for clarity
  const routeHandler = (await import("./route-handler")).default;
  routeHandler(req, res, next);
});

This way, changes to your route handlers will not force a server reload and your route handler will only be transpiled and loaded when a request to the path "/my-route" comes in, greatly improving development-time performance.

If this lazy loading pattern feels too wordy, you can refactor it into a function suitable for your server framework. One possible implementation for Express could be:

function lazy(
  importer: () => Promise<{ default: RequestHandler }>,
): RequestHandler {
  return async (req, res, next) => {
    try {
      const routeHandler = (await importer()).default;
      routeHandler(req, res, next);
    } catch (err) {
      next(err);
    }
  };
}

// When reloadOn option is set to "static-deps-change",
// changes to the route handlers will not trigger a reload.
app.get(
  "/foo",
  lazy(() => import("./routes/foo")),
);
app.get(
  "/bar",
  lazy(() => import("./routes/bar")),
);
app.get(
  "/baz",
  lazy(() => import("./routes/baz")),
);

Options

entry: string = "/server"

Server entry. The default value "/server" will resolve to server.js, server.ts etc. in your project root.

reloadOn: "any-change" | "static-deps-change" = "any-change"

When to reload the server. "any-change" reloads every time any of the dependencies of the server entry changes. "static-deps-change" only reloads when statically imported dependencies change, dynamically imported dependencies are not tracked.

serveClientAssetsInDev: boolean = false

Whether to serve client assets in development mode. Enable when developing full-stack applications.

TODO: Currently HTML files are not served regardless of this setting. An option to enable it is being considered.

Other considerations

Unlike solutions like nodemon which restarts the whole server process on file changes, @vavite-giacomorebonato/reloader only re-executes the server entry in the same process which may cause global state to leak from old to new server instances. Since this is a novel approach, we don't know whether it will cause adverse effects on the internal operations of server frameworks.