ESLint

npm i --save-dev eslint
 
> eslint --init
 
? How would you like to configure ESLint? Answer questions about your style
// 是否校验 Es6 语法
? Are you using ECMAScript 6 features? Yes
// 是否校验 Es6 模块语法
? Are you using ES6 modules? Yes
// 代码运行环境,Browser 指浏览器
? Where will your code run? Browser
// 是否校验 CommonJs 语法
? Do you use CommonJS? Yes
// 是否校验 JSX 语法
? Do you use JSX? Yes
// 是否校验 React 语法
? Do you use React? Yes
// 首行空白选择 Tab 键还是 Space
? What style of indentation do you use? Tabs
// 字符串使用单引号 'string' 还是双引号 "string"
? What quotes do you use for strings? Double
// 操作系统
? What line endings do you use? Windows
// 每行代码结尾是否校验加分号
? Do you require semicolons? Yes
// 以 .js 格式生成配置文件
? What format do you want your config file to be in? JavaScript
// 因为要校验 Reac 语法,所以这里需要下载一个 React 语法规则的包
Installing eslint-plugin-react@latest

off” 或 0 - 关闭规则

“warn” 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)

“error” 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)

// http://eslint.org/docs/user-guide/configuring
// .eslintrc.js
module.exports = {
  //此项是用来告诉eslint找当前配置文件不能往父级查找
  root: true,
  //此项是用来指定eslint解析器的,解析器必须符合规则,babel-eslint解析器是对babel解析器的包装使其与ESLint解析
  parser: 'babel-eslint',
  parserOptions: {
    sourceType: 'module'
  },
  env: {
    browser: true,
   // "node": true,
    //"commonjs": true,
    //"es6": true,
    //"amd": true
  },
  // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
  // 此项是用来配置标准的js风格,
  extends: 'standard',
   // 此项是用来提供插件的,插件名称省略了eslint-plugin-,
  plugins: [
    'html'
  ],
  // add your custom rules here
  'rules': {
    'prettier/prettier': ['error'],// [error,wran,off]
    // allow paren-less arrow functions
    'arrow-parens': 0,
    // allow async-await
    'generator-star-spacing': 0,
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
    "no-irregular-whitespace": 0,//不能有不规则的空格
    "no-mixed-operators": 0,
    "sort-vars": 0,//变量声明时排序
    "prefer-const": 0,//首选const
    "newline-after-var": 0,//变量声明后是否需要空一行
    "quotes": [0, "single"],//引号类型 `` "" ''
    "quote-props":[0, "always"],//对象字面量中的属性名是否强制双引号
    "array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格
    "arrow-spacing": 0,//=>的前/后括号
    "spaced-comment": 0,//注释风格要不要有空格什么的
    "eol-last": 0,//文件以单一的换行符结束
    "no-extra-semi": 0,//禁止多余的冒号
    "no-inline-comments": 0,//禁止行内备注
    "no-else-return": 0,//如果if语句里面有return,后面不能跟else语句
    "comma-dangle": [0, "never"],//对象字面量项尾不能有逗号
    "comma-spacing": 0,//逗号前后的空格
    "func-names": 0,//函数表达式必须有名字
    "guard-for-in": 0,//for in循环要用if语句过滤
    "handle-callback-err": 0,//nodejs 处理错误
    "space-after-keywords": [0, "always"],//关键字后面是否要空一格
    "space-before-blocks": [0, "always"],//不以新行开始的块{前面要不要有空格
    "max-len": [0, 80, 4],//字符串最大长度
    "no-unneeded-ternary":0,
    "camelcase": 'off',
    '@typescript-eslint/camelcase': 0,
    "no-self-assign": 0,
    "no-delete-var": 1,//不能对var声明的变量使用delete操作符
    "no-div-regex": 1,//不能使用看起来像除法的正则表达式/=foo/
    "indent": [2, 4],//缩进风格
    "semi-spacing": [2, {"before": false, "after": false}],//分号前后空格
    "no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1}
    "no-duplicate-case": 2,//switch中的case标签不能重复
    "comma-style": [2, "last"],//逗号风格,换行时在行首还是行尾
    "no-ex-assign": 2,//禁止给catch语句中的异常参数赋值
    "no-extend-native": 2,//禁止扩展native对象
    "no-extra-bind": 2,//禁止不必要的函数绑定
    "no-extra-boolean-cast": 2,//禁止不必要的bool转换
    "no-extra-parens": 2,//禁止非必要的括号
    "eqeqeq": 2,//必须使用全等
    "no-implied-eval": 2,//禁止使用隐式eval
    "no-inner-declarations": [2, "functions"],//禁止在块语句中使用声明(变量或函数)
    "no-invalid-regexp": 2,//禁止无效的正则表达式
    "no-invalid-this": 2,//禁止无效的this,只能用在构造器,类,对象字面量
    "no-dupe-args": 2,//函数参数不能重复
    "valid-typeof": 2,//必须使用合法的typeof的值
    "no-with": 2,//禁用with
    "semi": [2, "always"],//语句强制分号结尾
    "key-spacing": [2, { "beforeColon": false, "afterColon": true }],//对象字面量中冒号的前后空格
  }
}

写一个 eslint 插件

// node_modules/@typescript-eslint/eslint-plugin
// package.json 中的 main 指向了 dist/index.js 文件,因此 index.js 是入口文件
 
// dist/index.js(部分代码)
const rules_1 = __importDefault(require("./rules"));
module.exports = {
    rules: rules_1.default,
};
 
// dist/rules/index.js(部分代码)
const ban_ts_comment_1 = __importDefault(require("./ban-ts-comment"));
exports.default = {
     // 设计一条校验规则 ban-ts-comment
    'ban-ts-comment': ban_ts_comment_1.default,
};
 
// dist/rules/ban-ts-comment.js(部分代码)
 
// 以下代码不用细看,可以简单理解为从以下注释中匹配,如果能够匹配上,则发布一个 ESLint 错误或者警告
// @ts-ignore
// @ts-nocheck
// @ts-expoect-error
// @ts-check
 
exports.default = util.createRule({
      name: 'ban-ts-comment',
      create(context, [options]) {
            /*
              The regex used are taken from the ones used in the official TypeScript repo -
              https://github.com/microsoft/TypeScript/blob/408c760fae66080104bc85c449282c2d207dfe8e/src/compiler/scanner.ts#L288-L296
            */
            const commentDirectiveRegExSingleLine = /^/*\s*@ts-(?<directive>expect-error|ignore|check|nocheck)(?<description>.*)/;
            const commentDirectiveRegExMultiLine = /^\s*(?:/|*)*\s*@ts-(?<directive>expect-error|ignore|check|nocheck)(?<description>.*)/;
            const sourceCode = context.getSourceCode();
            const descriptionFormats = new Map();
            for (const directive of [
                'ts-expect-error',
                'ts-ignore',
                'ts-nocheck',
                'ts-check',
            ]) {
                const option = options[directive];
                if (typeof option === 'object' && option.descriptionFormat) {
                    descriptionFormats.set(directive, new RegExp(option.descriptionFormat));
                }
            }
            return {
                Program() {
                    const comments = sourceCode.getAllComments();
                    comments.forEach(comment => {
                        const regExp = comment.type === utils_1.AST_TOKEN_TYPES.Line
                            ? commentDirectiveRegExSingleLine
                            : commentDirectiveRegExMultiLine;
                        const match = regExp.exec(comment.value);
                        if (!match) {
                            return;
                        }
                        const { directive, description } = match.groups;
                        const fullDirective = ts-${directive};
                        const option = options[fullDirective];
                        if (option === true) {
                            // https://eslint.org/docs/latest/developer-guide/working-with-rules#contextreport
                            // 一旦匹配上 TypeScript 上述注释,则通过 context.report 在 ESLint 中发布一个警告或者错误
                            // The main method you’ll use is context.report(), which publishes a warning or error (depending on the configuration being used). 
                            context.report({
                                // (optional) placeholder data for message.
                                data: { directive },
                                //  (optional) the AST node related to the problem. 
                                node: comment,
                                // the problem message.
                                messageId: 'tsDirectiveComment',
                            });
                        }
                        if (option === 'allow-with-description' ||
                            (typeof option === 'object' && option.descriptionFormat)) {
                            const { minimumDescriptionLength = exports.defaultMinimumDescriptionLength, } = options;
                            const format = descriptionFormats.get(fullDirective);
                            if (description.trim().length < minimumDescriptionLength) {
                                context.report({
                                    data: { directive, minimumDescriptionLength },
                                    node: comment,
                                    messageId: 'tsDirectiveCommentRequiresDescription',
                                });
                            }
                            else if (format && !format.test(description)) {
                                context.report({
                                    data: { directive, format: format.source },
                                    node: comment,
                                    messageId: 'tsDirectiveCommentDescriptionNotMatchPattern',
                                });
                            }
                        }
                    });
                },
            };
        }
})
 

参考文章