Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Open more actions menu

Repository files navigation

babel-plugin-rewire-exports

NPM Version NPM Downloads

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 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

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 12.17.0+
  • ✨ SystemJS
  • 🌟 Webpack 2+
  • 💥 Rollup
  • 📦 Works with CommonJS, including running in Node and bundling with webpack/browserify:

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++; // TypeError

This 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 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.

Installation

$ npm install babel-plugin-rewire-exports

Usage

Via .babelrc (Recommended)

.babelrc

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

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

Via CLI

$ babel --plugins rewire-exports script.js

Via Node API

require("@babel/core").transformSync("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.

Releases

Packages

Used by

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.