Babel plugin for stubbing (ES6, ES2015) module exports. It allows to rewire the exported values in all the importing modules. Unlike babel-plugin-rewire it doesn't modify the module internals (e.g. imports and top-level variables and functions). See How it works section for implementation details.
Plugin transforms module exports in such a way that they can be stubbed (or "rewired") via the following API:
rewire(stub)
function that allows to replace the originalexport var foo
) an additional function rewire$foo(stub)
is exportedrestore()
function allows to restore the exports to their original valuesrewire
or restore
top-level identifiers, the generated exports will be named
rewire$default
and restore$rewire
respectivelyNamed export:
//------ text.js ------
export let message = 'Hello world!'
//------ logger.js ------
import {message} from './text.js'
export default function () {
console.log(message)
}
//------ main.js ------
import {rewire$message, restore} from './text.js'
import logger from './logger.js'
logger() // 'Hello world!'
rewire$message('I am now rewired')
logger() // 'I am now rewired'
restore()
logger() // 'Hello world!'
Default export:
//------ fetch.js ------
export default function (url) {
// perform some expensive remote call
}
//------ adapter.js ------
import fetch from './fetch.js'
export function fetchItems() {
return fetch('/items')
}
//------ test.js ------
import {rewire, restore} from './fetch.js'
import {fetchItems} from './adapter.js'
// Jasmine example
describe('adapter', function () {
beforeEach(function () {
rewire(this.spy = jasmine.createSpy('fetch'))
})
afterAll(restore)
it('should call fetch', function () {
fetchItems()
expect(this.spy).toHaveBeenCalledWith('/items')
})
})
// Mocha/Chai and Sinon example
describe('adapter', function () {
var spy
beforeEach(function () {
rewire(spy = sinon.spy())
})
after(restore)
it('should call fetch', function () {
fetchItems()
expect(spy.withArgs('/items').calledOnce).to.be.true
})
})
@std/esm
8.5.0+
behind --experimental-modules
flagenv
presetplugins
directly make sure that "rewire-exports"
goes
before "@babel/plugin-transform-modules-commonjs"
In ES6, imports are live read-only views on exported values:
//------ lib.js ------
export let counter = 3;
export function incCounter() {
counter++;
}
//------ main1.js ------
import { counter, incCounter } from './lib';
// The imported value `counter` is live
console.log(counter); // 3
incCounter();
console.log(counter); // 4
// The imported value canβt be changed
counter++; // TypeError
This allows for any exports to be overwritten from within the module - and imports will be automatically updated via their bindings.
Here's how various kinds of export declarations are transformed:
Literals (export default 'foo'
) - the original value is copied to a variable to be stubbed and restored later:
export {_default as default}
Variables:
export var foo
, export let bar
or export {baz}
) are left intact,
but their initial values are similarly copied to temp variables.export default foo
) is converted to a named export to enable live binding:
export {foo as default}
Constants (export const foo = 'bar'
or export default foo
) are treated similar to variables,
but their values are not modified within the module (since they are read-only) - only exported values are rewired:
export { _foo as foo }
export { _default as default }
You can use unsafeConst
option to convert const
to let
in order to enable live binding.
Functions (export default function () {β¦}
or export function foo() {β¦}
)
are converted into a function expression and exported variable by the same name.
The variable initialization is hoisted to the top of the scope to preserve existing behavior.
Classes (export default class foo {β¦}
or export class foo {β¦}
) are handled similarly to functions
except the variables are not hoisted (again to preserve the existing behavior).
Re-exports (export * from './foo.js'
or export {bar} from 'baz'
) are ignored.
They can be rewired in the original modules.
Immutable values such as undefined
, globals and imports are copied similar to literals.
$ npm install babel-plugin-rewire-exports
.babelrc
(Recommended).babelrc
// without options
{
"plugins": ["rewire-exports"]
}
// with options
{
"plugins": [
["rewire-exports", {
"unsafeConst": true
}]
]
}
$ babel --plugins rewire-exports script.js
require("@babel/core").transform("code", {
plugins: ["rewire-exports"]
});
unsafeConst
boolean
, defaults to false
.
Constants cannot be rewired if you're targeting ES2015+,
because the plugin relies on variables being assign-able in order to work.
However setting unsafeConst: true
will convert export const foo = 'bar'
to export let foo = 'bar'
.
This will allow to treat constant exports as regular variables.
This is potentially unsafe if your code relies on constants being read-only.