babel-plugin-transform-imports-api
Version:
Convert import default package API to modular reference to reduce package size and transforms member style imports.
222 lines • 11 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const transformImport_1 = require("./transformImport");
// 标识没有没有默认导出的包
const noDefault = '__noDefault';
const plugin = function (babel) {
const t = babel.types;
// 需要一个数据格式管理 package 信息,这些变量需要在每个 program 里重置
/**
*
packagesApis: new Map([
['packageName1', new Set(['api'])],
['packageName2', new Set(['api'])],
]),
*/
let packagesApis = new Map();
let invokedApis = new Map();
const packageInvokedApis = new Map();
const packageNeedDefault = new Map();
// packageName 和 defaultName 的映射
const packageNames = new Map();
// defaultName packageName 的映射
const packageDefaultNames = new Map();
const packageIsApiImported = new Map();
return {
name: 'babel-plugin-transform-imports-api',
visitor: {
ImportDeclaration(ast) {
const packageName = ast.node.source.value;
const apis = packagesApis.get(packageName);
if (!apis)
return;
ast.node.specifiers.forEach(node => {
if (t.isImportDefaultSpecifier(node)) {
packageNames.set(packageName, node.local.name);
packageDefaultNames.set(node.local.name, packageName);
}
else if (t.isImportSpecifier(node)) {
// @ts-ignore
const propertyName = node.imported.name;
if (apis.has(propertyName)) { // 记录api名字
ast.scope.rename(node.local.name);
if (!packageInvokedApis.get(packageName)) {
packageInvokedApis.set(packageName, new Map());
}
invokedApis = packageInvokedApis.get(packageName) || new Map();
invokedApis.set(propertyName, node.local.name);
}
else {
// 如果是未实现的 api 改成Taro.xxx
packageNeedDefault.set(packageName, true);
if (!packageNames.get(packageName)) {
// 假如没有默认导出变量,noDefault 占位,标识导出默认变量没有命名
packageNames.set(packageName, noDefault);
}
}
}
});
},
Identifier(ast) {
const packageName = packageDefaultNames.get(ast.node.name);
if (!packageName)
return;
// import Taro from '@tarojs/taro'
if (t.isImportDefaultSpecifier(ast.parent))
return;
// obj.Taro MemberExpression
if (t.isMemberExpression(ast.parent) || t.isJSXMemberExpression(ast.parent)) {
// @ts-ignore
if (ast.parent.object.name !== ast.node.name) {
return;
}
}
// 如果 import default 变量被引用,则需要默认导出变量
packageNeedDefault.set(packageName, true);
},
MemberExpression(ast) {
// @ts-ignore
const packageName = packageDefaultNames.get(ast.node.object.name);
if (!packageName)
return;
const apis = packagesApis.get(packageName);
if (!apis)
return;
let invokedApis = packageInvokedApis.get(packageName);
if (!invokedApis) {
invokedApis = new Map();
packageInvokedApis.set(packageName, invokedApis);
}
/* 处理Taro.xxx */
const property = ast.node.property;
let propertyName = null;
let propName = 'name';
// 兼容一下 Taro['xxx']
if (t.isStringLiteral(property)) {
propName = 'value';
}
propertyName = property[propName];
if (!propertyName)
return;
// 同一api使用多次, 读取变量名
if (apis.has(propertyName)) {
const parentNode = ast.parent;
const isAssignment = t.isAssignmentExpression(parentNode) && parentNode.left === ast.node;
if (!isAssignment) {
let identifier;
if (invokedApis.has(propertyName)) {
identifier = t.identifier(invokedApis.get(propertyName));
}
else {
const newPropertyName = ast.scope.generateUid(propertyName);
invokedApis.set(propertyName, newPropertyName);
/* 未绑定作用域 */
identifier = t.identifier(newPropertyName);
}
ast.replaceWith(identifier);
}
}
else {
packageNeedDefault.set(packageName, true);
}
},
Program: {
enter(ast, state) {
if (!state.opts)
return;
packagesApis = state.opts.packagesApis;
packageInvokedApis.clear();
packageNeedDefault.clear();
packageNames.clear();
packageDefaultNames.clear();
packageIsApiImported.clear();
},
exit(ast, state) {
if (!state.opts)
return;
ast.traverse({
ImportDeclaration(ast) {
const packageName = ast.node.source.value;
if (!packageNames.get(packageName))
return;
const apis = packagesApis.get(packageName);
if (!apis)
return;
// 未实现 api 没有默认导出的
ast.node.specifiers.forEach(node => {
if (t.isImportSpecifier(node)) {
if (packageNames.get(packageName) === noDefault) {
// 假如到最后都没有默认导出,则生成一个唯一的 iden
const defaultIden = ast.scope.generateUid(packageName);
packageNames.set(packageName, defaultIden);
}
// @ts-ignore
const propertyName = node.imported.name;
// 如果是实现的 api,不去更改 Taro.api 这种引用
if (apis.has(propertyName)) {
return;
}
const iden = t.identifier(packageNames.get(packageName) || '');
packageNeedDefault.set(packageName, true);
const localName = node.local.name;
const binding = ast.scope.getBinding(localName);
binding && binding.referencePaths.forEach(reference => {
reference.replaceWith(t.memberExpression(iden, t.identifier(propertyName)));
});
}
});
// 调用了配置的 api,重复引入包会被删除
if (packageIsApiImported.get(packageName)) {
return ast.remove();
}
packageIsApiImported.set(packageName, true);
const invokedApis = packageInvokedApis.get(packageName) || new Map();
const namedImports = Array.from(invokedApis.entries()).map(([imported, local]) => t.importSpecifier(t.identifier(local), t.identifier(imported)));
const defaultImportName = packageNames.get(packageName) || '';
if (packageNeedDefault.get(packageName)) {
const defaultImport = t.importDefaultSpecifier(t.identifier(defaultImportName));
ast.node.specifiers = [
defaultImport,
...namedImports
];
packageNeedDefault.set(packageName, false);
}
else if (namedImports.length) {
ast.node.specifiers = namedImports;
}
else {
// 导出的声明变量如果没有被上下引用则删除该导出声明
ast.remove();
}
},
});
// usePackgesImport 是否对 import member 进行处理
// 这个包 fork 的是 https://bitbucket.org/amctheatres/babel-transform-imports/src/master/index.js 仓库
// 以下是修改后 opt 配置
/**
usePackgesImport: false, // 开关是否使用 packagesImport
packagesImport: {
'my-library': {
transform: (importName, matches) => `my-library/etc/${importName.toUpperCase()}`,
preventFullImport: true,
},
'date-fns': {
transform: importName => `date-fns/${camelCase(importName)}`,
preventFullImport: true,
},
}
*/
if (state.opts.usePackgesImport) {
ast.traverse({
ImportDeclaration(ast) {
transformImport_1.default(ast, state, babel.types);
},
});
}
}
}
}
};
};
exports.default = plugin;
//# sourceMappingURL=index.js.map