问题:
Does not work with babel-plugin-import
以上两个问题都是关于同时使用了istanbul与import的babel插件以后导致的问题。
编译后的前端代码运行后就会报上述的错误。
分析
首先通过上面的issue我们先清楚的了解到一个问题。
为什么
const { Option } = Select;
就存在有问题,但是通过
const Option = Select.Option;
就没有问题呢?
这里我们就需要先了解经过istanbul插桩后变成了怎么样内容了, 我们可以来看下。
const {
Option
} = (cov_1eldv8eouu().s[0]++, Select.Option);
const {
Option
} = (cov_1eldv8eouu().s[0]++, Select);
以上就是经过babel解析后的结果,那么这两个给到babel-plugin-import解析后有什么区别呢?
这里就需要我们先确认下 这两个地方首先他经过babel解析成ast的结果是什么样的了?
const {
Option
} = (cov_1eldv8eouu().s[0]++, Select.Option);
可以看到这里有个MemberExpression
MemberExpression(path, state) {
const { node } = path;
const file = (path && path.hub && path.hub.file) || (state && state.file);
const pluginState = this.getPluginState(state);
// multiple instance check.
if (!node.object || !node.object.name) return;
if (pluginState.libraryObjs[node.object.name]) {
// antd.Button -> _Button
path.replaceWith(this.importMethod(node.property.name, file, pluginState));
} else if (pluginState.specified[node.object.name] && path.scope.hasBinding(node.object.name)) {
const { scope } = path.scope.getBinding(node.object.name);
// global variable in file scope
if (scope.path.parent.type === 'File') {
node.object = this.importMethod(pluginState.specified[node.object.name], file, pluginState);
}
}
}
可以看到babel-plugin-import 是否这块相应的处理的。
但是针对
const {
Option
} = (cov_1eldv8eouu().s[0]++, Select);
babel解析的结果为
babel-plugin-import并没有单独针对Identifier的处理逻辑的。
针对这个问题的解决如下:
buildDeclaratorHandler(node, prop, path, state) {
const file = (path && path.hub && path.hub.file) || (state && state.file);
const { types } = this;
const pluginState = this.getPluginState(state);
const checkScope = targetNode =>
pluginState.specified[targetNode.name] && // eslint-disable-line
path.scope.hasBinding(targetNode.name) && // eslint-disable-line
path.scope.getBinding(targetNode.name).path.type === 'ImportSpecifier'; // eslint-disable-line
if (types.isIdentifier(node[prop]) && checkScope(node[prop])) {
node[prop] = this.importMethod(pluginState.specified[node[prop].name], file, pluginState); // eslint-disable-line
} else if (types.isSequenceExpression(node[prop])) {
node[prop].expressions.forEach((expressionNode, index) => {
if (types.isIdentifier(expressionNode) && checkScope(expressionNode)) {
node[prop].expressions[index] = this.importMethod(
pluginState.specified[expressionNode.name],
file,
pluginState,
); // eslint-disable-line
}
});
}
}
在 VariableDeclarator
中做处理。
这样子就能够解析到上述的问题了。