Eslint踩坑记

为啥写这篇文章呢

入职的第二个星期,提交代码的时候顺利提交,顺利的通过了代码审查,那时的我犹如脱缰的骏马
脱缰的我

结果其他小伙伴拉下代码,一启动翻车
这次的事件暴露了我对eslint的不了解 所以接着这次机会重温一下eslint的知识
那么这里开启了哲学三问 这是啥 为什么要做这个东西 这个东西有什么用?

这是个啥

让我们看看官网的说法

ESLint最初是由Nicholas C. Zakas 于2013年6月创建的开源项目。它的目标是提供一个插件化的javascript代码检测工具。

为什么要做这个东西

ESLint 的初衷是为了让程序员可以创建自己的检测规则。ESLint 的所有规则都被设计成可插入的。ESLint 的默认规则与其他的插件并没有什么区别,规则本身和测试可以依赖于同样的模式。为了便于人们使用,ESLint 内置了一些规则,当然,你可以在使用过程中自定义规则。

这个东西有什么用?

他可以规范你的代码风格同时可以进行错误检查

那么下面让我们开始从头学习eslint
第一步当然是安装了 没有npm的同学可以参考下这篇文章

npm install -g eslint

初始化一下规则配置文件

eslint --init

初始化之后有三个模式可以选择

  1. To check syntax only (只检查语法错误)
  2. To check syntax and find problems(检查语法错误和发现代码格式问题)
  3. To check syntax, find problems, and enforce code style(语法错误,代码格式,强制纠正)

往配置文件上配置一些规则
校验某个文件的js是否满足我们现有的规范(假设这个文件叫做xx.js)

eslint xx.js

发现报错信息看不懂 不知道怎么修改怎么办( 自动修复也可能会出问题 所以修复完之后还是要看一眼修复后的样子是否满足我们的预期)

eslint xx.js --fix

eslint规则文件的优先级

  1. JavaScript - 使用 .eslintrc.js 然后输出一个配置对象。
  2. YAML - 使用 .eslintrc.yaml 或 .eslintrc.yml 去定义配置的结构。
  3. JSON -使用 .eslintrc.json 去定义配置的结构,ESLint 的 JSON 文件允许 JavaScript 风格的注释。
  4. Deprecated -使用 .eslintrc,可以使 JSON 也可以是YAML。
  5. package.json - 在 package.json 里创建一个 eslintConfig属性,在那里定义你的配置。

如何配置我们的规则呢

上面的规则文件中会有一个rule的字段
需要配置哪些规则可以来这里看看
规则文档

规则的等级

  • “off” 或 0 - 关闭规则
  • “warn” 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  • “error” 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)

到这里或许我们会有疑问 规则是如何检查我们的代码呢

简单描述一下过程
  1. 我们的写好的代码首先是被浏览器解析成为可以读懂的一个AST(抽象语法树)
  2. 得到AST之后 我们可以通过某些规则对语法树进行分析
  3. 对不符合规则的代码 报错提醒
  4. 对于加了fix选项的指令 就需要对AST做出修改 从不符合规则的AST->符合规则的AST 最后再把AST转成我们可以看懂的新代码
    在这里插入图片描述
详细描述一下转化过程

在了解如何生成AST之前,有必要了解一下Parser(常见的Parser有esprima、traceur、acorn、shift等)。JS Parser其实是一个解析器,它是将js源码转化为抽象语法树(AST)的解析器。整个解析过程主要分为以下两个步骤:

分词(也就是词法分析):将整个代码字符串分割成最小语法单元数组
语法分析:在分词基础上建立分析语法单元之间的关系
1、什么是语法单元?
  语法单元是被解析语法当中具备实际意义的最小单元,简单的来理解就是自然语言中的词语。举个例子来说,下面这段话:“2019年是祖国70周年”,我们可以把这句话拆分成最小单元,即:2019年、是、祖国、70、周年。

这就是我们所说的分词,也是最小单元,因为如果我们把它再拆分出去的话,那就没有什么实际意义了。

Javascript 代码中的语法单元主要包括以下这么几种:

关键字:例如 var、let、const等
标识符:没有被引号括起来的连续字符,可能是一个变量,也可能是 if、else 这些关键字,又或者是 true、false 这些内置常量
运算符: +、-、 *、/ 等
数字:像十六进制,十进制,八进制以及科学表达式等语法
字符串:因为对计算机而言,字符串的内容会参与计算或显示
空格:连续的空格,换行,缩进等
注释:行注释或块注释都是一个不可拆分的最小语法单元
其他:大括号、小括号、分号、冒号等
下面我们实战一下 假设我们写了一段这样的代码

var say = “hello world”

那么他会被转成怎样的AST呢

答案是

{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": {
            "type": "Identifier",
            "name": "say"
          },
          "init": {
            "type": "Literal",
            "value": "hello world",
            "raw": "\"hello world\""
          }
        }
      ],
      "kind": "var"
    }
  ],
  "sourceType": "script"
}

这就是 var say = “hello world” 所转换的 AST;(这里推荐一下esprima AST的可视化工具,esprima)
或者可以使用方案二(npm i esprima)

依次输入以下命令

mkdir testEsprima
cd testEsprima
touch testEsprima.js
npm init
npm i esprima
vim testEsprima.js
扔进去这一段代码
:wq

let esprima = require('esprima');
let estraverse = require('estraverse')
let code = 'let say = "hello world"'
function analyzeCode(code) {  
    var ast = esprima.parse(code);  
    console.log(ast.body)
    estraverse.traverse(ast, {  
        enter: function (node) {  
            console.log(node);  
        }
    });
}  
   
let ast = analyzeCode(code);  
console.log(ast)

node testEsprima.js

esprima.parse()方法接收两种类型的参数:字符串或Node的Buffer对象,它也可以收附加的选项作为参数。解析后返回结果即为抽象语法树(AST),AST遵守Mozilla SpiderMonkey的解析器API


  VariableDeclaration {
    type: 'VariableDeclaration',
    declarations: [ [VariableDeclarator] ],
    kind: 'let'
  }
]
Script {
  type: 'Program',
  body: [
    VariableDeclaration {
      type: 'VariableDeclaration',
      declarations: [Array],
      kind: 'let'
    }
  ],
  sourceType: 'script'
}
VariableDeclaration {
  type: 'VariableDeclaration',
  declarations: [
    VariableDeclarator {
      type: 'VariableDeclarator',
      id: [Identifier],
      init: [Literal]
    }
  ],
  kind: 'let'
}
VariableDeclarator {
  type: 'VariableDeclarator',
  id: Identifier { type: 'Identifier', name: 'say' },
  init: Literal {
    type: 'Literal',
    value: 'hello world',
    raw: '"hello world"'
  }
}
Identifier { type: 'Identifier', name: 'say' }
Literal { type: 'Literal', value: 'hello world', raw: '"hello world"' }
didi@localhost a % node a.js
[
  VariableDeclaration {
    type: 'VariableDeclaration',
    declarations: [ [VariableDeclarator] ],
    kind: 'let'
  }
]
Script {
  type: 'Program',
  body: [
    VariableDeclaration {
      type: 'VariableDeclaration',
      declarations: [Array],
      kind: 'let'
    }
  ],
  sourceType: 'script'
}
VariableDeclaration {
  type: 'VariableDeclaration',
  declarations: [
    VariableDeclarator {
      type: 'VariableDeclarator',
      id: [Identifier],
      init: [Literal]
    }
  ],
  kind: 'let'
}
VariableDeclarator {
  type: 'VariableDeclarator',
  id: Identifier { type: 'Identifier', name: 'say' },
  init: Literal {
    type: 'Literal',
    value: 'hello world',
    raw: '"hello world"'
  }
}
Identifier { type: 'Identifier', name: 'say' }
Literal { type: 'Literal', value: 'hello world', raw: '"hello world"' }

再在下面加入以下代码
作用是把定义

const estraverse = require('estraverse');

estraverse.traverse(ast, {
    enter: function (node) {
        node.kind = "let";
    }
});
console.log(ast);
// 输出结果
Script {
  type: 'Program',
  body: [
    VariableDeclaration {
      type: 'VariableDeclaration',
      declarations: [Array],
      kind: 'let'
    }
  ],
  sourceType: 'script',
  kind: 'let'
}

看起来ast这个树已经被我们成功的修改了
但是这个树 电脑看得懂我看的费劲呀
咋办呢
转成我们自己的函数呗
加入以下代码

const escodegen = require("escodegen");
const transformCode = escodegen.generate(ast)
console.log(transformCode);
let say = 'hello world';

到这里就转成功了 这就是我们最后获取到的更新代码
其他更复杂的规则有其他的分析办法 但是原理和流程就是按照这样子
分析代码 ->生成ast->分析ast->修改ast->生成理想ast->转化ast成为代码

篇幅有限 下一篇会讲一下eslint和prettier 学习去了 冲冲冲~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值