babel-plugin-rewire-exports v2.3.0
babel-plugin-rewire-exports
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.
Exports
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
rewireorrestoretop-level identifiers, the generated exports will be namedrewire$defaultandrestore$rewirerespectively
Example
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'
// 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
})
})Compatibility
- β€οΈ Works with modern browsers:
- π Safari 10.1+
- π Chrome 61+
- π¦ Firefox 60+
- π Edge 16+
- π Node:
@std/esm8.5.0+behind--experimental-modulesflag
- β¨ SystemJS
- π Webpack 2+
- π₯ Rollup
- π¦ Works with CommonJS, including running in Node and bundling with webpack/browserify:
- Use
envpreset - When specifying
pluginsdirectly make sure that"rewire-exports"goes before"@babel/plugin-transform-modules-commonjs" - Works with π Jest using babel-jest
- Use
How it works
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++; // TypeErrorThis allows for any exports to be overwritten from within the module - and imports will be automatically updated via their bindings.
Transformations
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 barorexport {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'orexport 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:- named exports:
export { _foo as foo } - default export:
export { _default as default }
You can use
unsafeConstoption to convertconsttoletin order to enable live binding.- named exports:
Functions (
export default function () {β¦}orexport 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 {β¦}orexport 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'orexport {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.
Installation
$ npm install babel-plugin-rewire-exportsUsage
Via .babelrc (Recommended)
.babelrc
// without options
{
"plugins": ["rewire-exports"]
}
// with options
{
"plugins": [
["rewire-exports", {
"unsafeConst": true
}]
]
}Via CLI
$ babel --plugins rewire-exports script.jsVia Node API
require("@babel/core").transform("code", {
plugins: ["rewire-exports"]
});Options
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.
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago