babel-plugin-lodash
Version:
Modular Lodash builds without the hassle.
164 lines (129 loc) • 5.36 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = lodash;
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _config = require('./config');
var _config2 = _interopRequireDefault(_config);
var _importModule = require('./importModule');
var _importModule2 = _interopRequireDefault(_importModule);
var _mapping = require('./mapping');
var _mapping2 = _interopRequireDefault(_mapping);
var _Store = require('./Store');
var _Store2 = _interopRequireDefault(_Store);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/** The error message used when chain sequences are detected. */
var CHAIN_ERROR = ['Lodash chain sequences are not supported by babel-plugin-lodash.', 'Consider substituting chain sequences with composition patterns.', 'See https://medium.com/making-internets/why-using-chain-is-a-mistake-9bc1f80d51ba'].join('\n');
/*----------------------------------------------------------------------------*/
function lodash(_ref) {
var types = _ref.types;
var identifiers = {
'PLACEHOLDER': types.identifier('placeholder'),
'UNDEFINED': types.identifier('undefined')
};
/**
* Used to track variables built during the AST pass. We instantiate these in
* the `Program` visitor in order to support running the plugin in watch mode
* or on multiple files.
*
* @type Store
*/
var store = new _Store2.default();
function getCallee(_ref2) {
var parentPath = _ref2.parentPath;
// Trace curried calls to their origin, e.g. `fp.partial(func)([fp, 2])(1)`.
while (!parentPath.isStatement()) {
if (parentPath.isCallExpression()) {
var result = parentPath.node.callee;
while (types.isCallExpression(result)) {
result = result.callee;
}
return result;
}
parentPath = parentPath.parentPath;
}
}
/*--------------------------------------------------------------------------*/
var visitor = {
Program: function Program(path, state) {
var _$assign = _lodash2.default.assign(_mapping2.default, (0, _config2.default)(state.opts));
var ids = _$assign.ids;
var file = path.hub.file;
if (_lodash2.default.isEmpty(ids)) {
throw new Error('Cannot find module');
}
// Clear tracked method imports.
_importModule2.default.cache.clear();
store.clear();
// Populate module paths per package.
_lodash2.default.each(ids, function (id) {
store.set(id);
_mapping2.default.modules.get(id).forEach(function (value, key) {
store.set(id + '/' + key);
});
});
// Replace old members with their method imports.
_lodash2.default.each(file.metadata.modules.imports, function (module) {
var pkgStore = store.get(module.source);
if (!pkgStore) {
return;
}
var isLodash = pkgStore.isLodash();
var specs = _lodash2.default.sortBy(module.specifiers, function (spec) {
return spec.imported == 'default';
});
_lodash2.default.each(specs, function (spec) {
var imported = spec.imported;
var local = spec.local;
var binding = file.scope.getBinding(local);
var isChain = isLodash && imported == 'chain';
_lodash2.default.each(binding.referencePaths, function (refPath) {
var parentPath = refPath.parentPath;
if (imported != 'default') {
if (isChain && refPath.parentPath.isCallExpression()) {
throw refPath.buildCodeFrameError(CHAIN_ERROR);
}
var identifier = (0, _importModule2.default)(pkgStore, imported, file);
refPath.replaceWith(types.clone(identifier));
} else if (parentPath.isMemberExpression()) {
var key = refPath.parent.property.name;
if (isLodash && key == 'chain' && parentPath.parentPath.isCallExpression()) {
throw refPath.buildCodeFrameError(CHAIN_ERROR);
}
var _identifier = (0, _importModule2.default)(pkgStore, key, file);
parentPath.replaceWith(types.clone(_identifier));
} else if (isLodash) {
var callee = getCallee(refPath);
if (callee && callee.name == local) {
throw refPath.buildCodeFrameError(CHAIN_ERROR);
}
refPath.replaceWith(callee ? types.memberExpression(callee, identifiers.PLACEHOLDER) : identifiers.UNDEFINED);
}
});
});
});
},
ImportDeclaration: function ImportDeclaration(path) {
if (store.get(path.node.source.value)) {
// Remove old import.
path.remove();
}
},
ExportNamedDeclaration: function ExportNamedDeclaration(path) {
var node = path.node;
var pkgPath = _lodash2.default.get(node, 'source.value');
var pkgStore = store.get(pkgPath);
if (!pkgStore) {
return;
}
node.source = null;
_lodash2.default.each(node.specifiers, function (spec) {
spec.local = (0, _importModule2.default)(pkgStore, spec.local.name, path.hub.file);
});
}
};
return { visitor: visitor };
};
module.exports = exports['default'];