UNPKG

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
"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