Skip to content

pygy/esm-reload

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ES module reload

This module lets you reload an ES module and its dependencies in Node.js. It does so by adding a module resolution hook.

Background

Per spec, ES modules are cached the first time they are imported, and subsequent import statements return the same object.

import {assert} from 'node:assert/strict'

const m1 = await import('./my-module.js')
const m2 = await import('./my-module.js')


assert.equal(m1, m2) // passes

This is desirable in most scenarios, but you can sometimes want to instantiate a module several times. For example, you may want to test a module that branches at load time depending on its environment. To some extent, that can be achieved by tacking a query string at the end of the import specifier:

import {assert} from 'node:assert/strict'

const m1 = await import('./my-module.js?dev')
process.env.NODE_ENV='production'
const m2 = await import('./my-module.js?prod')

assert.notEqual(mDev, mProd)  // passes

However, this doesn't work transitively. ./my-module.js?prod will import the dependencies that were cached when ./my-module.js?dev was loaded.

If you want to load a module multpile times from scratch with its dependencies you can use this module.

Usage:

The resolver hook gives a special meaning to ?instance=... and ?reload query strings.

If you want to retrieve a specific instance, use the former with an identifier of your choice.

import "esm-reload" // this registers the hook

const mDev = await import("./myModule.js?instance=dev")
process.env.NODE_ENV='production'
const mProd = await import("./myModule.js?instance=prod")
const mDev2 = await import("./myModule.js?instance=dev")

assert.equal(mDev, mDev2)              // passes
assert.notEqual(mDev, mProd)           // passes

If you just want a fresh instance you can use ?reload

const mReloaded = await import("./myModule.js?reload")
assert.notEqual(mDev, mReloaded)       // passes
assert.notEqual(mProd, mReloaded)      // passes

// ?reload is "magic"
const mReloaded2 = await import("./myModule.js?reload")
assert.notEqual(mReloaded, mReloaded2) // passes

In both cases, instances come with a fresh set of dependencies (except for the builtin node:xxx modules that don't support query strings at all).

With dependencies

Suppose these files:

// foo.js
export {x} from "./bar.js"

// bar.js
export const x = {}

We can then do

import "esm-reload"

const foo1 = await import("./foo.js?instance=1")
const bar1 = await import("./bar.js?instance=1")

const foo2 = await import("./foo.js?instance=2")
const bar2 = await import("./bar.js?instance=2")

assert.equal(foo1.x, bar1.x)
assert.equal(foo2.x, bar1.x)

assert.notEqual(bar1.x, bar2.x)

Credit:

The hook was originally written by Marcel Laverdet(@laverdet) then tweaked, tested and documented by yours truly.

License

ISC

About

Reload an ES module and its dependencies

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published