lint类工具是我们在前端开发工作中经常的工具,比如eslint和stylelint。lint可以帮助我们发现代码中可能存在的错误,有时候我们也会利用其提供的api来实现一些自定义的代码检查。但我们其实很少了解lint的实现原理,只是为用而用,这篇文章就来简单探究下stylelint的使用与实现原理。
使用
- 首先安装stylelint,所有样式文件的校验都是通过stylelint完成的
yarn add -D stylelint
- 然后在项目的根目录下创建配置文件.stylelintrc.json,主要的配置项如下
{
"extends": ["xx"], // 继承另外的stylelint配置
"plugins": ["xxx"], // 一系列自定义的规则
"customSyntax": xxx, // 自定义解释器
"rules": {
"xxxxx": true,
} // 一系列官方提供的规则
}
- 最后在package.json中加入校验的脚本,可以配合git hooks和CI来加强校验
{
"scripts": {
"stylelint-fix": "stylelint **/*.{css,less,scss} --fix"
}
}
执行后效果如下图
基础的配置建议直接extends以下三个config
stylelint-config-recommended: 此config打开了官方规则中提供所有可能导致错误的规则
stylelint-config-rational-order: 此config是用来决定css书写顺序的,可以提供用户良好的编码习惯
stylelint-config-prettier: 此config关闭了官方规则中可能与prettier冲突的规则
实现原理
入口
直接从stylelint执行命令开始分析
npx stylelint "**/*.css" --fix
这段命令即是对项目下所有后缀为css的文件进行stylelint的校验并进行自动修复,其余较重要的可指定参数为
--config
指定stylelint的配置文件地址,如果没有指定,则会按具体规则寻找
--config-basedir
当config中extends和plugins是相对路径时可用,指定config目录
--ignore-path
指定哪些文件不需要进行lint检查
--fix
是否自动修复检测出来的问题
--custom-syntax
指定postcss解释器
--stdin
直接输入需要lint检查的语法
--formatter
将stylelint结果以默认方式序列化输出
--custom-fomatter
指定方式格式化stylelint的结果
接下来直接进入代码逻辑
可以看到执行lint操作的时候其实是执行了stylelint文件中导出的standalone方法,传入options就是我们指定的参数
核心逻辑
standalone主要是下面的步骤:
- 将参数处理并用来创建stylelint对象
-
运用stylelint对象上的_lintSource方法对文件或输入的类css语法进行校验,_lintSource是stylelint的核心方法,就是在这个方法中完成了对css的校验,我们详细拆解下这个方法
a. 首先通过语法解析拿到AST(可以在ast explore查看AST结果),这里使用的是postcss解释器,可以通过customSyntax属性来配置使用的是哪个解释器
b. 拿到AST之后就用lintPostcssResult方法来对AST进行相关规则的校验,该方法的核心逻辑为使用用户配置的rule对应的function来校验相关语法
- 校验完成后如果有配置自动修复,则会对有问题的代码尝试自动
- 调用方法对lint之后的结果进行处理输出,其中包含resultProcessor和fomatter等处理
流程图
实现自定义lint
有了上面的基础,我们就可以很轻松的实现一个最简单的lint工具。这个工具接受一段代码输入,分析之后返回lint结果。
这里拿graphql来举例,下面是一段graphql查询语句
query{
ListAZ(version:"yyy", version:"zzz"){
AZ{
Region,
AZ,
Desc
}
}
}
一般来说,lint规则需要提供两种能力
- 找出可能出错的语法,避免出现预期外的错误
- formatter相关的能力,规范代码的写法
这里我们简单点,默认只有一个规则
- 找出重复的参数,将第一个参数去掉
首先我们需要通过parser(词法分析和语法分析)将之转换为AST,这里我们可以直接使用graphql提供的parser
const {parse} = require('graphql');
const ast = parse(code)
然后我们需要把规则注册好,在AST上查询是否有多余参数的实现
module.exports = (root) => {
// 遍历ast查询重复的参数
const results = root.definitions.map(item => item?.selectionSet?.selections?.map(item => {
const result = {};
const dupArgs = [];
item?.arguments?.forEach(args => {
/** 不存在参数则存储,存在参数则先把旧参数存入重复集中,再delete旧参数,最后存入新参数*/
if (!result[args.name.value]) {
result[args.name.value] = args
} else {
dupArgs.push(result[args.name.value]);
delete result[args.name.value];
result[args.name.value] = args
}
})
return dupArgs;
}));
const dupArgs = (results || []).flat(2);
return !Boolean(dupArgs.length);
}
根据用户配置的规则使用相应的规则来校验,最后返回这次lint的结果
const performRules = [];
const rules = Object.keys(config.rules);
for (const rule of rules) {
performRules.push(ruleFunctions[rule](ast))
}
const result = performRules.every(Boolean);
console.log('lint结果:', result);
return result;
运行结果示例
总结
如果看完了上面的实现原理以及自定义lint,应该很清楚lint类工具的大概原理和步骤了,简单来说只有三步:
- 通过parser拿到相关AST
- 调用ruleFunction对AST进行检验
- 修复相关的代码并处理结果
最后
如果有时间,大家也可以想一下如何实现别的编程语言的lint工具,或者进一步探索如何给上文自定义lint实现自动修复的功能。
今天的文章stylelint实现原理分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/21288.html