Skip to content


Folders and files

Last commit message
Last commit date

Latest commit


Repository files navigation


NPM Version NPM Downloads Build Status

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:

  • 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 function rewire$foo(stub) is exported
  • restore() function allows to restore the exports to their original values
  • if there are existing rewire or restore top-level identifiers, the generated exports will be named rewire$default and restore$rewire respectively


Named export:

//------ text.js ------
export let message = 'Hello world!'

//------ logger.js ------
import {message} from './text.js'

export default function () {

//------ 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'
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'))
  it('should call fetch', function () {

// Mocha/Chai and Sinon example
describe('adapter', function () {
  var spy

  beforeEach(function () {
    rewire(spy = sinon.spy())
  it('should call fetch', function () {


How it works

In ES6, imports are live read-only views on exported values:

//------ lib.js ------
export let counter = 3;
export function incCounter() {

//------ main1.js ------
import { counter, incCounter } from './lib';

// The imported value `counter` is live
console.log(counter); // 3
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 or export {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}
  • 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:

    • named exports: export { _foo as foo }
    • default export: 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


Via .babelrc (Recommended)


// without options
  "plugins": ["rewire-exports"]

// with options
  "plugins": [
    ["rewire-exports", {
      "unsafeConst": true


$ babel --plugins rewire-exports script.js

Via Node API

require("@babel/core").transform("code", {
  plugins: ["rewire-exports"]



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.