Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
babel-plugin-explicit-exports-references
Advanced tools
Transforms all internal references to a module's exports such that each reference starts with `module.exports` instead of directly referencing an internal name. This enables easy mocking of specific (exported) functions in Jest with Babel/TypeScript, even
Transforms all internal references to a module's exports such that each
reference starts with module.exports
instead of directly referencing an
internal name. This enables easy mocking of specific (exported) functions in
Jest with Babel/TypeScript, even when the mocked functions call each other in
the same module.
npm install --save-dev babel-plugin-explicit-exports-references
And in your babel.config.js
:
module.exports = {
plugins: ['explicit-exports-references']
};
Note: it is recommended that this plugin only be enabled when NODE_ENV
is
test
. Using this plugin elsewhere, such as in production, can lead to
increased build size. For example:
module.exports = {
parserOpts: { ... },
plugins: [ ... ],
env: {
test: {
plugins: ['explicit-exports-references']
}
}
};
Finally, run Babel through your toolchain (Webpack, Jest, etc) or manually:
npx babel src --out-dir dist
To see detailed output on how this plugin transforms your code, enable debug mode:
DEBUG='babel-plugin-explicit-exports-references:*' npx jest your-test-file
The plugin begins by looking for default and named export declarations in a program.
For default exports, it looks for function declarations and class declarations
that have ids (i.e. variable names), like export default function main() {}
,
and updates any Identifiers referencing that id.
For named exports, it looks for function and class declarations too, but also
variable declarations like export const foo = 5;
and
export { x as default, y, x as z };
. Enums are explicitly ignored. Any
Identifiers that reference the declaration's id or specifier are updated.
When updating references, by default only Identifiers are transformed. Assignment Expressions can also be transformed, but doing so is currently unstable. All other reference types are ignored, including TypeScript types and Identifiers within their own declarations.
The following enables transforming Assignment Expressions along with Identifiers:
module.exports = {
plugins: [
['explicit-exports-references', { transformAssignExpr: true }]
]
};
Suppose we have the following myModule.ts
TypeScript file:
// file: myModule.ts
export function foo() {
// This function works fine in production but throws on our local test machine
throw new Error('failed to do expensive network stuff');
return;
}
export function bar() {
// ...
foo();
return 5;
}
export function baz() {
// ...
foo();
return 50;
}
Lets say we want to unit test myModule.ts
. Specifically, we want to test bar
and baz
. We don't want to unit test foo
because a) attempting to run it on
our local machine will always fail, which is why b) it is covered by our
integration tests instead. We simply want to ensure bar
and baz
work, and
that they both call foo
without running foo
.
If we expect a function to be called, and we want an alternative implementation run when it is called, the easy and obvious solution is to mock it.
So suppose we create the following myModule.test.ts
Jest test file, mocking
foo
with a noop:
// file: myModule.test.ts
import * as myModule from './myModule';
it('bar does what I want', () => {
const spy = jest.spyOn(myModule, 'foo').mockImplementation(() => undefined);
expect(myModule.bar()).toBe(5);
expect(spy).toBeCalled();
spy.mockRestore();
});
it('baz does what I want', () => {
const spy = jest.spyOn(myModule, 'foo').mockImplementation(() => undefined);
expect(myModule.baz()).toBe(50);
expect(spy).toBeCalled();
spy.mockRestore();
});
This file tests that bar
and baz
do what we want, and whenever they call
foo
the dummy version is called instead and no error is thrown. Or rather,
that seems like it should be the thing that happens. Unfortunately, if we run
this code, the above tests will fail because foo
throws
failed to do expensive network stuff
.
Is this a bug?
After encountering this problem over five years ago, someone posed the question to the Jest project: how do you mock a specific function in a module?, to which a contributor responded:
Supporting the above by mocking a function after requiring a module is impossible in JavaScript – there is (almost) no way to retrieve the binding that foo refers to and modify it.
However, if you change your code to this:
var foo = function foo() {}; var bar = function bar() { exports.foo(); }; exports.foo = foo; exports.bar = bar;
and then in your test file you do:
var module = require('../module'); module.foo = jest.fn(); module.bar();
it will work just as expected. This is what we do at Facebook where we don't use ES2015.
While ES2015 modules may have immutable bindings for what they export, the underlying compiled code that babel compiles to right now doesn't enforce any such constraints. I see no way currently to support exactly what you are asking...
Essentially, this plugin aims to automate the suggestion above, allowing you to
mock a specific module function using standard Jest spies by automatically
replacing references to exported identifiers with an explicit reference of the
form module.exports.${identifier}
. No assembly required.
With this plugin loaded into Babel, the tests in the motivating example above pass! 🎉
Prior solutions include:
exports.${identifier}
rewire
dark magicFurther reading and additional solutions:
Further documentation can be found under docs/
.
New issues and pull requests are always welcome and greatly appreciated! 🤩 Just as well, you can star 🌟 this project to let me know you found it useful! ✊🏿 Thank you!
See CONTRIBUTING.md and SUPPORT.md for more information.
FAQs
Transforms all internal references to a module's exports such that each reference starts with `module.exports` instead of directly referencing an internal name. This enables easy mocking of specific (exported) functions in Jest with Babel/TypeScript, even
The npm package babel-plugin-explicit-exports-references receives a total of 6,599 weekly downloads. As such, babel-plugin-explicit-exports-references popularity was classified as popular.
We found that babel-plugin-explicit-exports-references demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.