十分鐘瞭解eslint配置 && 編寫自定義eslint規則

eslint介紹

ESLint 是一個開源的 JavaScript 代碼檢查工具,由 Nicholas C. Zakas 於2013年6月創建。代碼檢查是一種靜態的分析,常用於尋找有問題的模式或者代碼,並且不依賴於具體的編碼風格。對大多數編程語言來說都會有代碼檢查,一般來說編譯程序會內置檢查工具。

JavaScript 是一個動態的弱類型語言,在開發中比較容易出錯。因爲沒有編譯程序,爲了尋找 JavaScript 代碼錯誤通常需要在執行過程中不斷調試。像 ESLint 這樣的可以讓程序員在編碼的過程中發現問題而不是在執行的過程中。

ESLint 的初衷是爲了讓程序員可以創建自己的檢測規則。ESLint 的所有規則都被設計成可插拔的。爲了便於人們使用,ESLint 內置了一些規則,當然,你可以在使用過程中自定義規則。所有的規則默認都是禁用的。

ESLint 使用 Node.js 編寫。

eslint配置

配置方式
  1. 一般都採用.eslintrc.* 的配置文件進行配置, 如果放在項目的根目錄中,則會作用於整個項目。如果在項目的子目錄中也包含着.eslintrc文件,則對於子目錄中文件的檢查會忽略掉根目錄中的配置,而直接採用子目錄中的配置,這就能夠在不同的目錄範圍內應用不同的檢查規則,顯得比較靈活。ESLint採用逐級向上查找的方式查找.eslintrc.*文件,當找到帶有 "root": true 配置項的.eslintrc.* 文件時,將會停止向上查找。
  2. 在 package.json文件裏的 eslintConfig 字段進行配置。
具體配置規則

以使用項目爲例,簡單介紹一下eslint的具體配置及作用:

module.exports = {
    parser: 'babel-eslint', // parser指定解析器,默認的爲espree。babel-eslint是一個Babel parser的包裝器,這個包裝器使得 Babel parser 可以和 ESLint 協調工作
    parserOptions: {
        sourceType: 'module', // 設置爲 "script" (默認) 或 "module"(ES6)。
        ecmaFeatures: { // 這是個對象,表示你想使用的額外的語言特性:
            jsx: true // 啓用 JSX
        }
    },
    extends: ['eslint:recommended'], // 使用eslint推薦的規則作爲基礎配置,可以在rules中覆蓋
    plugins: ['html', 'vue', 'prettier', 'import'], // vue是eslint-plugin-vue的簡寫,此插件的作用是可以讓eslint識別.vue中的script代碼
    rules: { // 0或者off表示規則關閉,出錯也被忽略;1或者warn表示如果出錯會給出警告(不會導致程序退出);2或者error表示如果出錯會報出錯誤(會導致程序退出,退出碼是1)
        'no-console': 'off',
        'prefer-const': 'error',
        'prettier/prettier': 'warn',
        'prefer-arrow-callback': 'warn',
        'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
    },
    globals: { // 允許在代碼中使用全局變量
        location: true,
        setTimeout: true
    }
};

具體的配置文檔:http://eslint.cn/docs/user-guide/configuring
具體的eslint:recommended支持的規則:https://cn.eslint.org/docs/rules/

“extends”除了可以引入推薦規則,還可以以文件形式引入其它的自定義規則,然後在這些自定義規則的基礎上用rules去定義個別規則,從而覆蓋掉”extends”中引入的規則。

{
    "extends": [
        "./node_modules/coding-standard/eslintDefaults.js",
        // Override eslintDefaults.js
        "./node_modules/coding-standard/.eslintrc-es6",
        // Override .eslintrc-es6
        "./node_modules/coding-standard/.eslintrc-jsx",
    ],
    "rules": {
        // Override any settings from the "parent" configuration
        "eqeqeq": "warn"
    }
}

除了在配置文件中指定規則外,還可以在代碼中指定規則,代碼文件內以註釋配置的規則會覆蓋配置文件裏的規則,即優先級要更高。平時我們常用的就是 `eslint-disable-next-line
`

忽略檢查

可以通過在項目目錄下建立.eslintignore文件,並在其中配置忽略掉對哪些文件的檢查。需要注意的是,不管你有沒有在.eslintignore中進行配置,eslint都會默認忽略掉對/node_modules/** 的檢查。也可以在package.json文件的 eslintIgnore 字段進行配置。

eslint檢查原理

要實現靜態分析則需要自建一個預編譯階段對代碼進行解析。

首先我們看看大部分編譯器工作時的三個階段:

解析:將未經處理的代碼解析成更爲抽象的表達式,通常爲抽象語法樹,即 AST。
轉換:通過修改解析後的代碼表達式,將其轉換爲符合預期的新格式。
代碼生成:將轉換後的表達式生成爲新的目標代碼。

對於eslint來說,規則校驗發生在將JavaScript 代碼解析爲 AST 之後,遍歷 AST 的過程中。eslint採用 Espree 來生成AST。具體的生成方法在這裏
我們可以使用AST explorer來查看代碼被解析後生成的AST。

rules工作原理

首先來看看eslint源碼中關於rules的編寫。eslint中的rules源碼存在於lib/rules下。每一個rules都是一個node模塊,用module.exports導出一個meta對象及一個create函數。

module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "disallow unnecessary semicolons",
            category: "Possible Errors",
            recommended: true,
            url: "https://eslint.org/docs/rules/no-extra-semi"
        },
        fixable: "code",
        schema: [] // no options
    },
    create: function(context) {
        return {
            // callback functions
        };
    }
};

meta 代表了這條規則的元數據,如這條規則的類別,文檔,可接收的參數 schema 等等。

create 返回一個對象,其中定義了一些在 AST 遍歷訪問到對應節點需要執行的方法等等。函數接受一個context對象作爲參數,裏面包含了例如可以報告錯誤或者警告的context.report()、可以獲取源代碼的context.getSourceCode()等方法,可以簡化規則的編寫。

function checkLastSegment (node) {
    // report problem for function if last code path segment is reachable
}

module.exports = {
    meta: { ... },
    create: function(context) {
        // declare the state of the rule
        return {
            ReturnStatement: function(node) {
                // 在AST從上向下遍歷到ReturnStatement node 時執行
            },
            // 在AST 從下向上遍歷到 function expression node 時執行:
            "FunctionExpression:exit": checkLastSegment,
            "ArrowFunctionExpression:exit": checkLastSegment,
            onCodePathStart: function (codePath, node) {
                // 在分析代碼路徑開始時執行
            },
            onCodePathEnd: function(codePath, node) {
                // 在分析代碼路徑結束時執行
            }
        };
    }
};

遍歷 AST 的過程中會以“從上至下”再“從下至上”的順序經過節點兩次,selector 默認會在下行的過程中執行對應的訪問函數,如果需要再上行的過程中執行,則需要添加:exit。

詳細的原理在官方文檔中有說明,點這裏
詳細的代碼路徑分析在這裏

如何編寫一個rules

知道了rules的原理,接下來可以自定義一個rules。每一個rules需要有三個以該規則名命名的文件,分別是:

  • 在 lib/rules 目錄下: 一個源文件(例如,no-extra-semi.js)
  • 在 tests/lib/rules 目錄下: 一個測試文件 (例如, no-extra-semi.js)
  • 在 docs/rules 目錄: 一個 markdown 文檔文件 (例如, no-extra-semi)

接下來我們來編寫一個簡單的rules,例如禁止塊級註釋,當代碼中使用了塊級註釋,eslint將報錯。

rules文件:

// lib/rules/no-block-comments.js
module.exports = {
  meta: {
    docs: {
      description: '禁止塊級註釋',
      category: 'Stylistic Issues',
      recommended: true
    }
  },

  create (context) {
    // 獲取源代碼
    const sourceCode = context.getSourceCode()

    return {
      Program () {
        // 獲取源代碼中所有的註釋
        const comments = sourceCode.getAllComments()

        const blockComments = comments.filter(({ type }) => type === 'Block')

        blockComments.length && context.report({
          node: node,
          message: 'No block comments'
        })
      }
    }
  }
}

rules的測試文件:

// tests/lib/rules/no-block-comments.js
const RuleTester = require("eslint").RuleTester;
const rule = require("../../../lib/rules/no-block-comments");

const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } }); // You do have to tell eslint what js you're using

ruleTester.run("no-block-comments", rule, {
    valid: ["var a = 1; console.log(a)"],
    invalid: [
        {
            code: "var a = 1; /* block comments */ console.log(a)",
            errors: [
                {
                    messageId: "blockComments",
                    line: 1,
                    nodeType: "Block"
                }
            ]
        }
    ]
});

官網的working with rules文檔中有關於如何編寫一個rules的詳細介紹。

如何使用自定義的rules

編寫好的rules需要發佈到npm上,作爲一個eslint-plugin,在項目中下載下來才能夠使用。例子中代碼的npm在這裏

在項目中的配置:

// .eslintrc.js
module.exports = {
    ...
    "plugins": [
        "eslint-plugin-no-block-comments"
        // 你 publish 的 npm 包名稱,可以省略 eslint-plugin
      ],
    "rules": { // 啓用的規則及其各自的錯誤級別
        'no-console': 'off',
        "no-block-comments/no-block-comments": 2 // 引用no-block-comments插件中的no-block-comments規則
    }
};

之後就可以對代碼進行檢查了。比如我要檢查的代碼如下:

// src/index.js
const a = 1;
/*
    這裏是塊級註釋
*/
console.log(a);

在命令行中執行eslint src,就可以看到報錯結果。

參考文章:

ESlint官網

ESLint 工作原理探討

開發 eslint 規則

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章