Babel plugin for stubbing (ES6, ES2015) module exports. It allows to rewire the 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:
- default export - plugin exports additional
rewire(stub)
function that allows to replace the original - named exports - for each export (e.g.
export var foo
) an additional functionrewire$foo(stub)
is exported restore()
function allows to restore the exports to their original values- if there are existing
rewire
orrestore
top-level identifiers, the generated exports will be namedrewire$default
andrestore$rewire
respectively
Named 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'
describe('adapter', function () {
beforeEach(function () {
rewire(this.spy = jasmine.createSpy('fetch'))
})
afterAll(restore)
it('should call fetch', function () {
fetchItems()
expect(this.spy).toHaveBeenCalledWith('/items')
})
})
- ❤️ Should work in modern browsers and Node once they start implementing ES6 modules natively - for example tested in latest Edge with experimental flags
- ✨ SystemJS
- 🌟 Webpack 2
- 💥 Rollup - requires v0.38+
- 📦 Seems to work with CommonJS and AMD (including bundling with webpack)
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:
- named exports (
export var foo
,export let bar
orexport {baz}
) are left intact, but their initial values are similarly copied to temp variables. - default export (
export default foo
) is converted to a named export to enable live binding:export {foo as default}
- named exports (
- Constants (
export const foo = 'bar'
) are ignored - Functions (
export default function () {…}
orexport function foo() {…}
) are split into a function declaration and exported variable by the same name. The variable is hoisted to the very top of the module to preserve existing behavior. - Classes (
export default class {…}
orexport class foo {…}
) are handled similar to functions except the variables are not hoisted (again to preserve the existing behavior). - Re-exports (
export * from './foo.js'
orexport {bar} from 'baz'
) are ignored
$ npm install babel-plugin-rewire-exports
.babelrc
{
"plugins": ["rewire-exports"]
}
$ babel --plugins rewire-exports script.js
require("babel-core").transform("code", {
plugins: ["rewire-exports"]
});