编写思路
- 通过编写 eslint 插件
- 在 ast 遍历过程中筛选出所有导入语句
- 记录排序前导入语句代码块内容
- 对导入语句进行排序
- 比对排序前后导入语句代码块
- 若不一样则进行替换
排序规则
- 对导入语句进行分组
- npm 包
- 相对路径/绝对路径
- 判断路径中是否携带指定路径类型(ex.
['components', 'api']
),若携带则划分至该路径类型组 - ex.
import aa from './../seller/components/aa'
,划分至 components 组 - 若不携带,则正向遍历找到第一个路径,划分至该路径分组
- ex.
import aa from './../hooks/aa'
,划分至 hooks 组 - 若存在分组内仅有一条导入语句,划分至合并分组
- 判断路径中是否携带指定路径类型(ex.
- import type
- 属性排序
- 针对具名导入,属性由短至长排序
- 组内排序
- 导入语句由短至长排序
- 组间排序
- npm 包最前,合并分组 & 类型导入分组位于最后
- 其余分组按照导入语句数量由短至长排序
- 组间空一行
插件使用
arduino
pnpm add eslint-plugin-for-sort-import -D
js
// eslint.config.js
import importSortPlugin from 'eslint-plugin-for-sort-import'
export default [
...importSortPlugin(),
]
若未生效可重启编辑器 or
bash
ctrl + shift + p,输入 restart eslint server
参数说明
csharp
export interface PluginConfig {
/** 行尾风格: 'lf' 或 'crlf' */
end_of_line: 'lf' | 'crlf'
/** 缓存大小限制 */
max_cache_size: number
/** 路径类型识别列表 */
path_type: string[]
}
- end_of_line: 行尾风格,默认 'lf'
- 用于决定换行符采用 '\n' 还是 '\r\n'
- max_cache_size: 缓存大小限制
- 插件采用了 LRU Cache,缓存文件排序后的结果
- path_type: 路径类型识别列表
- 用于指定分组
核心逻辑
此处仅展示了最简单的将导入语句按照长短排序
js
export const sortImportsRule = {
meta: {
type: 'suggestion',
docs: {
description: '导入语句按照指定规则排序',
category: 'Stylistic Issues',
recommended: false
},
fixable: 'code'
},
create(context) {
return {
Program(node) {
const sourceCode = context.getSourceCode()
// 筛选出所有导入声明
const importDeclarations = node.body.filter((node) => node.type === 'ImportDeclaration')
if (importDeclarations.length === 0) return
// 获取当前导入代码块
const start = importDeclarations[0].range[0]
const end = importDeclarations[importDeclarations.length - 1].range[1]
const currentText = sourceCode.text.slice(start, end)
// 生成排序后的导入代码块
const group = [...importDeclarations]
group.forEach((node, index) => {
group[index].processedText = sourceCode.getText(node)
}
group.sort((a, b) => a.processedText.length - b.processedText.length)
const newImports = group.map((node) => node.processedText).join('\n')
// 如果当前导入代码块不等于排序后的导入代码块,则重新排序,进行修复
if (currentText !== newImports) {
context.report({
node: importDeclarations[0],
message: '导入语句未按照排序规则进行排序',
fix(fixer) {
return fixer.replaceTextRange([start, end], newImports)
}
})
}
}
}
}
}
最后
感兴趣的可移步源码