Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: commit my repo #640

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "babel-plugin-import",
"version": "1.13.8",
"name": "babel-plugin-custom-import",
"version": "1.0.1",
"description": "Component modular import plugin for babel.",
"repository": {
"type": "git",
Expand Down Expand Up @@ -36,8 +36,7 @@
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@umijs/test": "^3.2.28",
"babel-core": "^7.0.0-0",
"@umijs/test": "3.2.28",
"babel-preset-umi": "^1.0.0",
"coveralls": "^3.0.6",
"eslint": "^7.1.0",
Expand Down
180 changes: 143 additions & 37 deletions src/Plugin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { join } from 'path';
import { addSideEffect, addDefault, addNamed } from '@babel/helper-module-imports';

/**
* 驼峰 -> 分隔符
* @param {*} _str 驼峰
* @param {*} symbol 分隔符
* @returns
*/
function transCamel(_str, symbol) {
// e.g. QRCode
// First match: QR
Expand All @@ -25,59 +31,144 @@ function normalizeCustomName(originCustomName) {
}

export default class Plugin {
constructor(
libraryName,
libraryDirectory,
style,
styleLibraryDirectory,
customStyleName,
camel2DashComponentName,
camel2UnderlineComponentName,
fileName,
customName,
transformToDefaultImport,
types,
index = 0,
) {
constructor(param) {
const {
libraryName,
libraryDirectory,
style,
styleLibraryDirectory,
customStyleName,
camel2DashComponentName,
camel2UnderlineComponentName,
fileName,
customName,
transformToDefaultImport,
alias,
transferNameOn,
types,
index = 0,
} = param;
if (!types) {
throw new Error('types不存在', { types });
}
/**
* 目标包名,如antd
*/
this.libraryName = libraryName;
/**
* 包里到处模块的文件夹,默认是lib
*/
this.libraryDirectory = typeof libraryDirectory === 'undefined' ? 'lib' : libraryDirectory;
/**
* 驼峰是否转化为'-'分隔符,默认是true
*/
this.camel2DashComponentName =
typeof camel2DashComponentName === 'undefined' ? true : camel2DashComponentName;
/**
* 驼峰是否转化为'_'分隔符
*/
this.camel2UnderlineComponentName = camel2UnderlineComponentName;
/**
* 是否导出当前组件的样式文件,不用管
*/
this.style = style || false;
/**
* 导出组件的样式文件的路径,不用太管
*/
this.styleLibraryDirectory = styleLibraryDirectory;
/**
* 根据导入的样式动态地决定该模块的引入路径,见 https://github.com/umijs/babel-plugin-import#customName,不用管
*/
this.customStyleName = normalizeCustomName(customStyleName);
this.fileName = fileName || '';
/**
* 根据导入的模块动态地决定该模块的引入路径,见 https://github.com/umijs/babel-plugin-import#customName
*/
this.customName = normalizeCustomName(customName);
/**
* 要处理的npm包没有默认导出时设为false, 若没配置则默认是 true
*/
this.transformToDefaultImport =
typeof transformToDefaultImport === 'undefined' ? true : transformToDefaultImport;
/**
* babel的types工具包
*/
this.types = types;
/**
* 每个插件实例的独立标识符
*/
this.pluginStateKey = `importPluginState${index}`;
/**
* The alias for the library, if provided
*/
this.alias = typeof alias === 'undefined' ? undefined : alias;
/**
* 是否根据导入的模块名转换导出文件名称,默认=true
*/
this.transferNameOn = typeof transferNameOn === 'undefined' ? true : transferNameOn;
}

/**
* 获取或初始化插件的状态对象,存储在 Babel 的 state 中。
* @param {*} state
* @returns
*/
getPluginState(state) {
if (!state[this.pluginStateKey]) {
state[this.pluginStateKey] = {}; // eslint-disable-line
}
return state[this.pluginStateKey];
}

ProgramEnter(path, state) {
const pluginState = this.getPluginState(state);
// 模块名称到本地别名的映射
pluginState.specified = Object.create(null);
// 默认导入模块的字典
pluginState.libraryObjs = Object.create(null);
// 指定导入的模块到解析后的导入路径的映射
pluginState.selectedMethods = Object.create(null);
pluginState.pathsToRemove = [];
}

ProgramExit(path, state) {
this.getPluginState(state).pathsToRemove.forEach(p => !p.removed && p.remove());
}

// ============================ 工具函数 ============================ //

/**
* 生成指定导入模块与之解析后的导入路径,以键值的形式储存在插件状态的selectedMethod下,并返回导入语句的identifier节点
* @param {*} methodName - 引入的模块
* @param {*} file - 当前正在处理的文件
* @param {*} pluginState - 插件状态
* @returns
*/
importMethod(methodName, file, pluginState) {
if (!pluginState.selectedMethods[methodName]) {
const { style, libraryDirectory } = this;
const transformedMethodName = this.camel2UnderlineComponentName // eslint-disable-line
? transCamel(methodName, '_')
: this.camel2DashComponentName
? transCamel(methodName, '-')
: methodName;
const { style, libraryDirectory, alias } = this;
let { transferNameOn } = this;
transferNameOn = !alias && transferNameOn;
let transformedMethodName = '';
if (transferNameOn && methodName !== 'default') {
transformedMethodName = this.camel2UnderlineComponentName
? transCamel(methodName, '_')
: this.camel2DashComponentName
? transCamel(methodName, '-')
: methodName;
}

const path = winPath(
this.customName
? this.customName(transformedMethodName, file)
: join(this.libraryName, libraryDirectory, transformedMethodName, this.fileName), // eslint-disable-line
? this.customName(methodName, file)
: join(this.libraryName, libraryDirectory, transformedMethodName, this.fileName),
);
pluginState.selectedMethods[methodName] = this.transformToDefaultImport // eslint-disable-line
? addDefault(file.path, path, { nameHint: methodName })
: addNamed(file.path, methodName, path);

pluginState.selectedMethods[methodName] =
methodName === 'default' || this.transformToDefaultImport
? addDefault(file.path, path, { nameHint: methodName })
: addNamed(file.path, methodName, path);

if (this.customStyleName) {
const stylePath = winPath(this.customStyleName(transformedMethodName, file));
addSideEffect(file.path, `${stylePath}`);
Expand Down Expand Up @@ -140,36 +231,47 @@ export default class Plugin {
}
}

ProgramEnter(path, state) {
const pluginState = this.getPluginState(state);
pluginState.specified = Object.create(null);
pluginState.libraryObjs = Object.create(null);
pluginState.selectedMethods = Object.create(null);
pluginState.pathsToRemove = [];
}
// ============================ 工具函数 ============================ //

ProgramExit(path, state) {
this.getPluginState(state).pathsToRemove.forEach(p => !p.removed && p.remove());
}
// ============================ 处理节点的函数 ============================ //

/**
* 无需多言
* @param {*} path - 当前节点路径
* @param {*} state - 插件状态
* @returns
*/
ImportDeclaration(path, state) {
const { node } = path;

// path maybe removed by prev instances.
if (!node) return;

const { value } = node.source;
const { libraryName } = this;
const { libraryName, alias } = this;
const { types } = this;
const pluginState = this.getPluginState(state);
if (value === libraryName) {

// Check if the import source matches either the libraryName or the alias
if (value === libraryName || (alias && value.startsWith(alias))) {
node.specifiers.forEach(spec => {
if (types.isImportSpecifier(spec)) {
// Named import
pluginState.specified[spec.local.name] = spec.imported.name;
} else if (types.isImportDefaultSpecifier(spec)) {
// Default import
pluginState.specified[spec.local.name] = 'default';
} else {
// Namespace import or other
pluginState.libraryObjs[spec.local.name] = true;
}
});

// If an alias is used, replace it with the actual libraryName
if (alias && value.startsWith(alias)) {
node.source.value = types.StringLiteral(value.replace(alias, libraryName));
}

pluginState.pathsToRemove.push(path);
}
}
Expand All @@ -183,10 +285,12 @@ export default class Plugin {

if (types.isIdentifier(node.callee)) {
if (pluginState.specified[name]) {
// 替换节点的callee为转译后的导入语句
node.callee = this.importMethod(pluginState.specified[name], file, pluginState);
}
}

// 检查函数调用的参数是否也是导入的,是的话重复上述逻辑,多用于嵌套组件
node.arguments = node.arguments.map(arg => {
const { name: argName } = arg;
if (
Expand Down Expand Up @@ -304,4 +408,6 @@ export default class Plugin {
const expressionsProps = node.expressions.map((_, index) => index);
this.buildExpressionHandler(node.expressions, expressionsProps, path, state);
}

// ============================ 处理节点的函数 ============================ //
}
62 changes: 48 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import assert from 'assert';
import Plugin from './Plugin';

/**
*
* @param {*} param0
* @returns - {visitor:
* {
* programm: {enter(){}, exit(){}}},
* [method: string]: Function
* }
*/
export default function ({ types }) {
let plugins = null;

Expand All @@ -10,6 +19,12 @@ export default function ({ types }) {
plugins = null;
};

/**
* 以plugins中的单个实例为上下文执行method方法,传参是[...args, context]
* @param {*} method
* @param {*} args
* @param {*} context
*/
function applyInstance(method, args, context) {
// eslint-disable-next-line no-restricted-syntax
for (const plugin of plugins) {
Expand All @@ -20,6 +35,14 @@ export default function ({ types }) {
}

const Program = {
/**
* 进入初始化生命周期时,创建Plugins实例
* opts是配置
* 如果 opts 是数组,则为每个配置项创建一个插件实例。
* 否则,为单个配置项创建一个插件实例。
* @param {*} path
* @param {*} param1
*/
enter(path, { opts = {} }) {
// Init plugin instances once.
if (!plugins) {
Expand All @@ -37,11 +60,16 @@ export default function ({ types }) {
fileName,
customName,
transformToDefaultImport,
alias,
transferNameOn,
},
index,
) => {
assert(libraryName, 'libraryName should be provided');
return new Plugin(
if (!types) {
throw new Error('types不存在!', { types });
}
return new Plugin({
libraryName,
libraryDirectory,
style,
Expand All @@ -52,27 +80,32 @@ export default function ({ types }) {
fileName,
customName,
transformToDefaultImport,
alias,
transferNameOn,
types,
index,
);
});
},
);
} else {
assert(opts.libraryName, 'libraryName should be provided');
plugins = [
new Plugin(
opts.libraryName,
opts.libraryDirectory,
opts.style,
opts.styleLibraryDirectory,
opts.customStyleName,
opts.camel2DashComponentName,
opts.camel2UnderlineComponentName,
opts.fileName,
opts.customName,
opts.transformToDefaultImport,
new Plugin({
libraryName: opts.libraryName,
libraryDirectory: opts.libraryDirectory,
style: opts.style,
styleLibraryDirectory: opts.styleLibraryDirectory,
customStyleName: opts.customStyleName,
camel2DashComponentName: opts.camel2DashComponentName,
camel2UnderlineComponentName: opts.camel2UnderlineComponentName,
fileName: opts.fileName,
customName: opts.customName,
transformToDefaultImport: opts.transformToDefaultImport,
alias: opts.alias,
transferNameOn: opts.transferNameOn,
types,
),
index: 0,
}),
];
}
}
Expand Down Expand Up @@ -118,3 +151,4 @@ export default function ({ types }) {

return ret;
}

2 changes: 1 addition & 1 deletion test/fixtures/custom-name-source-file/customName.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default function customName(name) {
return `antd/lib/${name}`;
return `antd/lib/${name.toLowerCase()}`;
}
Loading