Giter Club home page Giter Club logo

swap-hot's Introduction

swap-hot

Rationale

Large Node.js applications can take a (relatively) long time to start, due largely to requireing tens of thousands of files. The vast majority of these are from node_modules and won't change during a normal development session. It's annoying, and wasteful, to re-require all of these modules every time you want to see a change reflected in an application. This can also be bothersome when running tests -- often you change a single file and need to only run the associated test file, but starting the test suite incurs a several-second delay as the application starts and requires it's dependencies.

Prior Art

hotswap module is sort of clunky -- it has a weird module.change_code property you need to use, which seems dangerously easy to commit to source control. Further, it only works for exported properties -- if you export e.g. a class constructor, updates won't work.

Implementation

There are two primary pieces, the Watcher which observes the file system for changes, and the Swapper which does the actual hotswapping. In the vast majority of cases these can both run alongside the application code in the same process. However, under some systems -- Vagrant in particular -- file watching doesn't work very well. In this case, the Watcher will run on the host system and the Swapper will run alongside the application inside the VM. For simplicity, in either case the two pieces communicate over the network. The hotswapping logic is based on ES6 Proxies as well as a library I wrote that intercepts require() calls. Basically, whenever a module in the application is required, it's proxied through a Swappable, which can update its contents from source code transparently at any time.

Usage

Simple case -- hotswap any module when it changes

const swap = require("swap-hot");
const {close} = swap();
// call `close()` to stop hot swapping

Complex case -- running a test file with Mocha whenever the associated source file changes

const swap = require("swap-hot");
const {swapper} = swap();

swapper.on("update", function (filename) {
  const testFile = path.join(this._root, "..", "test", filename + ".js");
  if (testFile == null) {
    console.log("no test file found for", filename);
    return;
  }

  const ext = path.extname(testFile);
  const suffix = crypto.randomBytes(4).toString("hex");

  const testContents = fs.readFileSync(testFile, "utf8");
  // trying to re-run a file with mocha that has already been run before in this process
  // seems to "succeed" always with "0 tests run", so just make a temp file to circumvent
  // this unwanted behavior
  const tmpFile = path.join(path.dirname(testFile), `__hotswap-test-${suffix}${ext}`);
  fs.writeFileSync(tmpFile, testContents);

  const m = new Mocha();
  m.addFile(tmpFile);
  m.run(function (err) {
    if (err) {
      console.log("test runner errored!", err);
      throw err;
    }
    fs.unlinkSync(tmpFile);
  });
})

Limitations

Objects that live for the duration of the application won't be reloaded.

const clientInstance = new Client(); // <-- changes to Client class won't be reflected in clientInstance

const app = express();
app.use(myConfigurableMiddleware({key: "value"})) // <-- changes not reflected in behavior, because instance already created
app.use(myOtherMiddleware) // <-- changes will be reflected in behavior, since each request causes this function to be called
app.use(function (req, res, next) {
  req.client = new Client(); // <-- every request will get an up-to-date client
  next();  
});

It can be tricky to build the intuition for how this works. In general if the application uses a single "instance" of something you're changing (could be an actual class instance, or merely a function or object returned from a function call) it won't be reloaded because the instance the app is using has already been created. Hot reloaded does work when the app does something like create a new instance of something for each request, or call a function every time some event happens.

swap-hot's People

Contributors

bttmly avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.