babel-plugin-mockable-imports
Advanced tools
Comparing version 1.3.0 to 1.3.1
61
index.js
@@ -8,2 +8,5 @@ 'use strict'; | ||
/** | ||
* Default list of modules whose imports are excluded from processing. | ||
*/ | ||
const EXCLUDE_LIST = [ | ||
@@ -17,2 +20,9 @@ // Proxyquirify and proxyquire-universal are two popular mocking libraries | ||
/** | ||
* Default list of directories that are excluded from the transforms applied | ||
* by this plugin. | ||
* | ||
* The default list includes common names of test directories, because there | ||
* is no point in making imports in test modules mockable. | ||
*/ | ||
const EXCLUDED_DIRS = ['test', '__tests__']; | ||
@@ -77,2 +87,5 @@ | ||
/** | ||
* Return true if node is a `module.exports = <expression>` assignment. | ||
*/ | ||
function isCommonJSExportAssignment(path) { | ||
@@ -159,12 +172,7 @@ const assignExpr = path.node; | ||
enter(path, state) { | ||
// Map of local identifier for import => import info. | ||
state.importMeta = new Map(); | ||
// The last `import` or top-level CommonJS require node that was | ||
// seen in the code. | ||
state.lastImport = null; | ||
// Keep track of whether modifying this file has been aborted. | ||
// TODO - It should be possible to make use of `path.stop()` and avoid | ||
// needing to do this. This currently results in processing of the | ||
// file by other plugins stopping though it seems. Perhaps need to | ||
// create an innert path and use that? | ||
// Set of local identifiers which refer to an import. | ||
state.importIdentifiers = new Set(); | ||
// Flag to keep track of whether further processing of this file has | ||
// stopped. | ||
state.aborted = excludeModule(state); | ||
@@ -180,3 +188,3 @@ | ||
exit(path, state) { | ||
if (state.aborted || state.importMeta.size === 0) { | ||
if (state.aborted || state.importIdentifiers.size === 0) { | ||
return; | ||
@@ -244,2 +252,3 @@ } | ||
// Check for and register CommonJS imports in top-level variable assignments. | ||
AssignmentExpression(path, state) { | ||
@@ -287,7 +296,3 @@ if (state.aborted) { | ||
// not the assignment. | ||
state.importMeta.set(binding.identifier, { | ||
symbol: '<CJS>', | ||
source, | ||
value: binding.identifier, | ||
}); | ||
state.importIdentifiers.add(binding.identifier); | ||
@@ -301,2 +306,3 @@ // The actual import registration via `$imports.$add` however needs to | ||
// Check for and register CommonJS imports in top-level variable declarations. | ||
VariableDeclaration(path, state) { | ||
@@ -322,7 +328,3 @@ if (state.aborted) { | ||
imports.forEach(({alias, source, symbol, value}) => { | ||
state.importMeta.set(value, { | ||
symbol, | ||
source, | ||
value, | ||
}); | ||
state.importIdentifiers.add(value); | ||
path.insertAfter(createAddImportCall(alias, source, symbol, value)); | ||
@@ -332,2 +334,3 @@ }); | ||
// Register ES6 imports. | ||
ImportDeclaration(path, state) { | ||
@@ -337,4 +340,3 @@ if (state.aborted) { | ||
} | ||
// Process import and add metadata to `state.importMeta` map. | ||
state.lastImport = path; | ||
// Process import and add metadata to `state.importIdentifiers` map. | ||
path.node.specifiers.forEach(spec => { | ||
@@ -371,8 +373,3 @@ if (spec.local.name === '$imports') { | ||
state.importMeta.set(spec.local, { | ||
symbol: imported, | ||
source, | ||
value: spec.local, | ||
}); | ||
state.importIdentifiers.add(spec.local); | ||
path.insertAfter( | ||
@@ -384,2 +381,5 @@ createAddImportCall(spec.local.name, source, imported, spec.local), | ||
// Replace references to identifiers with `$imports.<identifier>` | ||
// expressions which resolve either to the original import or the active | ||
// mocks. | ||
ReferencedIdentifier(child, state) { | ||
@@ -390,5 +390,6 @@ if (state.aborted) { | ||
// Check if this a reference to an import. | ||
const name = child.node.name; | ||
const binding = child.scope.getBinding(name, /* noGlobal */ true); | ||
if (!binding || !state.importMeta.has(binding.identifier)) { | ||
if (!binding || !state.importIdentifiers.has(binding.identifier)) { | ||
return; | ||
@@ -395,0 +396,0 @@ } |
@@ -28,3 +28,12 @@ "use strict"; | ||
}(_wrapNativeSuper(Error)); | ||
/** | ||
* Object exposed by modules that have been processed by this plugin. | ||
* | ||
* The processed modules create an instance of `ImportMap` and register | ||
* mockable imports using `$add`. Test modules import the `ImportMap` from | ||
* the module under test and call `$mock` and `$restore` methods to mock | ||
* dependencies. | ||
*/ | ||
var ImportMap = | ||
@@ -31,0 +40,0 @@ /*#__PURE__*/ |
{ | ||
"name": "babel-plugin-mockable-imports", | ||
"version": "1.3.0", | ||
"version": "1.3.1", | ||
"description": "Babel plugin for mocking ES imports", | ||
@@ -18,3 +18,3 @@ "main": "index.js", | ||
"type": "git", | ||
"url": "git+https://github.com/robertknight/babel-plugin-mockble-imports.git" | ||
"url": "git+https://github.com/robertknight/babel-plugin-mockable-imports.git" | ||
}, | ||
@@ -21,0 +21,0 @@ "keywords": [ |
@@ -212,12 +212,31 @@ # babel-plugin-mockable-imports | ||
- The plugin adds an export named `$imports` to every module it processes. | ||
This may cause conflicts if you try to combine exports from multiple modules | ||
using `export * from <module>`. [See | ||
issue](https://github.com/robertknight/babel-plugin-mockable-imports/issues/2). | ||
It can also cause problems if you have code which tries to loop over the | ||
exports of a module and does not gracefully handle unexpected exports. | ||
- There is currently no support for dynamic imports, either using `import()` | ||
to obtain a promise for a module, or calling `require` anywhere other than | ||
at the top level of a module. | ||
### Mocking code that runs when a module is imported | ||
A downside of the approach used by this plugin is that you can't use it to change the result of code that is executed when the module is first imported. For example if a module has: | ||
```js | ||
import helper from './utils/helper'; | ||
export const aConstant = helper(someData); | ||
export function usesHelper() { | ||
return helper(someOtherData); | ||
} | ||
``` | ||
It is possible to mock `helper` in `usesHelper` but not the initialization of `aConstant`. There are solutions to this, but they will involve changes to the code being tested: | ||
1. Change the design of your code so that it exports a function which must be called, instead of executing side effects during the initial import. Making imports free of side effects can have other benefits, eg. for [tree-shaking](https://webpack.js.org/guides/tree-shaking/). | ||
2. Add an indirection so that the code you want to test calls/uses the mock on-demand rather than during the initial evaluation. | ||
### `$imports` export conflicts | ||
The plugin adds an export named `$imports` to every module it processes. This may cause conflicts if you try to combine exports from multiple modules using `export * from <module>`. [See issue](https://github.com/robertknight/babel-plugin-mockable-imports/issues/2). It can also cause problems if you have code which tries to loop over the exports of a module and does not gracefully handle unexpected exports. | ||
We may in future add an alternative method of exposing the `$imports` object so that tests can get at it. | ||
### Dynamic imports | ||
There is currently no support for dynamic imports, either using `import()` to obtain a promise for a module, or calling `require` anywhere other than at the top level of a module. | ||
## Troubleshooting | ||
@@ -224,0 +243,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33307
521
288