寫一個 Eslint 插件:Vue Template 中 Class 順序的檢查和自動修復
有小伙伴問我如何修復 vue template 中的 class 名的順序、屬性名順序,還有 options 中的屬性順序的問題,用 eslint 可以做到么。
答案是能,但是需要寫 eslint 插件來檢查和修復。
考慮到他可能沒有寫過 eslint 插件,所以我先把相對簡單的校驗和修復 class 名的順序的插件摘出來實現了一下。
思路分析
首先,eslint 是否能 parse vue 的模版呢?
是可以的,因為 eslint 的 parser 支持切換,而 vue 實現了對應的 parser,所以可以在 eslint 中使用 vue-eslint-parser 來解析模版。
我們可以使用 astexplorer.net 來看一下 parse 生成的 AST。
我們要處理的是 class 屬性,也就是 VAttribute 的 value 部分
可以支持傳入比較器來自定義順序,排序完之后設置回去。
當然,vue 的模版支持 {} 來引用 data,這種我們不處理,可以過濾掉。
思路比較簡單,下面我們寫代碼來實現一下。
代碼實現
我們可以給插件起名為 vue-class-order。
首先,我們引入 eslint,設置 useEslintrc 為 false 也就是不使用配置文件,然后在 overrideConfig 屬性設置各種配置,rules 里填入這個插件。
- const { ESLint } = require("eslint");
- const engine = new ESLint({
- fix: false,
- overrideConfig: {
- parser: 'vue-eslint-parser',
- parserOptions: {
- sourceType: 'module'
- },
- rules: {
- "vue-class-order": [ "error" ]
- }
- },
- rulePaths: ['./'],
- useEslintrc: false
- });
這里的 parser 要使用 vue-eslint-parser 才可以,并且 rulePaths 也就是查找 rule 的路徑也要設置下。fix 設置為 false 代表不自動修復。
之后,調用它的 lintText 方法來對代碼進行 lint。
- (async function main() {
- const results = await engine.lintText(`
- <template>
- <div>
- <p class="c d e" >dongdong</p>
- <p class="c a b">guangguang</p>
- </div>
- </template>
- <script>
- export default {
- };
- </script>
- `);
- console.log(results[0].output);
- const formatter = await engine.loadFormatter("stylish");
- const resultText = formatter.format(results);
- console.log(resultText);
- })();
之后在插件里面取出來:
- module.exports = {
- meta: {
- fixable: true
- },
- create(context) {
- const comparator = context.options[0];
- }
- };
這里的 comparator 就是從 context 中取出的參數。
插件的結構是 meta,create 兩部分,meta 是各種描述插件本身的元信息,create 部分是插件的主要邏輯。
create 部分返回一個 visitor,聲明對什么節點進行什么操作。但是因為我們用的 parser 是 vue 自定義的(vue-eslint-parser),所以這里 visitor 也要用它提供的,也就是:
- module.exports = {
- meta: {
- fixable: true
- },
- create(context) {
- const comparator = context.options[0];
- return context.parserServices.defineTemplateBodyVisitor({
- "VAttribute[key.name=class]"(node) {
- }
- });
- }
- };
在 context.parserServices.defineTemplateBodyVisitor 方法中傳入具體的 visitor,比如我們需要對 VAttribute 節點做處理。
eslint 支持esqury 的寫法,也就是可以通過選擇器的方式來指定要處理的節點,這里我們指定 key.name 為 class 的 VAttribute 節點
之后要拿到節點的值,排序一下,看看是否是對的,不對就報錯。
- "VAttribute[key.name=class]"(node) {
- const classStr = node.value.value;
- if (!classStr.includes('{')) { //過濾掉有插值表達式的 class
- const curOrder = classStr.split(/\s/);
- const shouldOrder = [...curOrder].sort(comparator);
- if (curOrder.some((item, index) => curOrder[index] !== shouldOrder[index])) {
- context.report({
- node,
- message: 'className 順序不對:' + classStr,
- loc: node.value.loc
- });
- }
- }
- }
這樣,我們就實現了對 vue 模版中 class 的順序的 lint。
我們試一下效果:
我們實現了對 className 順序的 lint!
當然,只報錯不修復比較耍流氓,我們還得實現下自動修復。
修復的話就是把 value 的部分替換掉,也就是拿到 value 的 range(開始和結束的下標),把該 range 的文本使用 fixer 的 api 替換掉。(這里要考慮引號)
- context.report({
- node,
- message: 'className 順序不對:' + classStr,
- loc: node.value.loc,
- *fix(fixer) {
- const [ start, end ] = node.value.range;
- yield fixer.replaceTextRange([start + 1, end - 1], shouldOrder.join(' '))
- }
- });
我們把 fixer 設置為 true,再跑一下:
做了自動的修復,沒有報錯了!
我們實現了對 vue 模版中 class 的順序的檢查和自動修復!
總結
Eslint 可以基于 AST 做代碼格式的檢查和修復。
基于 AST 那就得有對應的 parser, eslint 支持 parser 的擴展,所以有很多 eslint parser 可用,要 parse vue 模版就可以用 vue-eslint-parser。可以用 astexplorer.net可視化的查看。
我們要實現對 vue 模版中 class 的順序的檢查,分析之后就是要取出 key 為 class 的 VAttribute 節點的 value,然后根據傳入的比較器進行排序,如果順序不一致,就報錯。并且還可以通過 fixer 的 api 進行自動修復,也就是對該段 range 的文本進行替換。
這里我們通過 api 來調用的 eslint,通過 cli 也一樣。
這篇文章實現了一個相對簡單的 eslint 插件,對 vue template 中的代碼格式做了檢查和修復,希望能夠幫助大家理清 eslint 插件開發的思路。