AST (Abstract Syntax Tree)
Why talk about AST?
devDependencies
in any current mainstream project, you will find that countless plug-ins were born in the past few years. We can summarize: ES6
translation, code compression, css
preprocessor, eslint
, prettier
etc. Many of these modules will not be used in the production environment, but they play a very important role in the development environment. The birth of these tools is built on the shoulders of the giant AST
What is AST?
It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.
abstract syntax tree ( abstract syntax code,AST
) is a tree representation of the abstract syntax structure of the source code. , each node on the tree represents a structure in the source code, so it is abstract because the abstract syntax tree does not It will show every detail of the actual grammar. For example, nested parentheses are implicit in the structure of the tree and are not presented in the form of nodes. The abstract syntax tree does not depend on the grammar of the source language, that is to say, the context used in the grammatical analysis stage has no grammar, because when writing a grammar, the grammar is often equivalently transformed (elimination of left recursion, backtracking, and ambiguity) Sex, etc.), this will introduce some extra elements to the grammar analysis, adversely affect the subsequent stages, and even make the combined stage become chaotic. Because of this, many compilers often have to independently construct a parse tree to establish a clear interface for the front end and the back end.
The data converted from plain text into a tree structure, that is, AST
, each entry corresponds to a node in the tree.
AST process
This part will let you understand the whole process from source code to lexical analysis to generate tokens
and then to grammatical analysis to generate AST
AST
from the source code? The current compiler helps to do this, how does the compiler do it?
The compilation process of a compiler (translating high-level language into binary bits) is very complicated, but we only need to focus on lexical analysis and grammatical analysis. These two steps are the key to AST
first step, the lexical analyzer, also known as the scanner , will scan the entire code first. When it encounters spaces, operators, or special symbols, it decides that a word is completed, and the identified one Words, operators, symbols, etc. are recorded in the tokens
{type, value, range, loc }
), and the comments are additionally stored in a comments
array.
For example, var a = 1;
, @typescript-eslint/parser
generated by the tokens
parser is as follows:
tokens: [
{
"type": "Keyword",
"value": "var",
"range": [112, 115],
"loc": {
"start": {
"line": 11,
"column": 0
},
"end": {
"line": 11,
"column": 3
}
}
},
{
"type": "Identifier",
"value": "a",
"range": [116, 117],
"loc": {
"start": {
"line": 11,
"column": 4
},
"end": {
"line": 11,
"column": 5
}
}
},
{
"type": "Punctuator",
"value": "=",
"range": [118, 119],
"loc": {
"start": {
"line": 11,
"column": 6
},
"end": {
"line": 11,
"column": 7
}
}
},
{
"type": "Numeric",
"value": "1",
"range": [120, 121],
"loc": {
"start": {
"line": 11,
"column": 8
},
"end": {
"line": 11,
"column": 9
}
}
},
{
"type": "Punctuator",
"value": ";",
"range": [121, 122],
"loc": {
"start": {
"line": 11,
"column": 9
},
"end": {
"line": 11,
"column": 10
}
}
}
]
second step, the syntax analyzer, also known as the parser tokens
array obtained from lexical analysis into a tree structure representation, verifies the language grammar and throws a grammatical error (if this happens)
var a = 1;
converted from the tokens
array to a tree structure as shown below:
{
type: 'Program',
body: [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "a",
"range": [
116,
117
],
"loc": {
"start": {
"line": 11,
"column": 4
},
"end": {
"line": 11,
"column": 5
}
}
},
"init": {
"type": "Literal",
"value": 1,
"raw": "1",
"range": [
120,
121
],
"loc": {
"start": {
"line": 11,
"column": 8
},
"end": {
"line": 11,
"column": 9
}
}
},
"range": [
116,
121
],
"loc": {
"start": {
"line": 11,
"column": 4
},
"end": {
"line": 11,
"column": 9
}
}
}
],
"kind": "var",
"range": [
112,
122
],
"loc": {
"start": {
"line": 11,
"column": 0
},
"end": {
"line": 11,
"column": 10
}
}
}
]
}
When generating the tree, the parser will remove some unnecessary tags (such as redundant parentheses), so the created "abstract syntax tree" is not 100% matching the source code, but it is enough to let us know how to deal with it. On the other hand, the tree generated by the parser that completely covers all code structures is called the "concrete syntax tree"
Compiler extension
Want to learn more about the compiler? the-super-tiny-compiler , this is a compiler written JavaScript
About 200 lines of code, the idea behind it is to Lisp
compiled into C
language, almost every line has a comment.
LangSandbox , a better project, it shows how to create a programming language. Of course, books like design programming languages are also full of books on the market. Therefore, this project is more in-depth, unlike the the-super-tiny-compiler project, which converts Lisp
to C
language. In this project, you can write your own language and compile it into C
language or machine language, and finally Run it.
Can you directly use the tripartite library to generate AST
? sure! There are a bunch of tripartite libraries available. You can visit astexplorer and choose the library you like. astexplorer
is a great website, you can play AST
online, and besides JavaScript
, it also contains many other languages AST
library
I want to emphasize one of them, in my opinion it is a very good one, babylon
It Babel
, maybe this is the reason for its popularity. Because it is supported by the Babel
project, it will always be JS
features and can be used boldly. In addition, its API
also very simple and easy to use.
OK, now that you know how to generate code AST
, let us continue to discuss real-life use cases.
The first use case I want to talk about is code translation, which is of course Babel
.
Babel is not a ‘tool for having ES6 support’. Well, it is, but it is far not only what it is about.
Babel
and ES6/7/8
features has many connections, which is why we often use it. But it is just a set of plug-ins, we can also use it for code compression, React
related syntax conversion (such as JSX
), Flow
plug-in and so on.
Babel
is a JavaScript
compiler. Its compilation has three stages: parsing ( parsing
), translating ( transforming
), and generating ( generation
). You give Babel
some of JavaScript
code, which modify the code and generate a new code, how it is to modify the code? That's right! It builds AST
, traverses it, babel-plugin
, and then generates new code AST
Let's see this in a simple code example.
As I mentioned before, Babel
uses Babylon
, so, we first parse the code to generate AST
, then traverse AST
and reverse all variable names, and finally generate the code. As we have seen, the first (parsing) and third (code generation) phases seem to be very common and will be done every time. So, Babel
takes over these two steps, what we are really interested in is the AST
conversion ( Babel-plugin
modification).
When developing Babel-plugin
, you only need to describe the node “visitors”
, it will change your AST
. Add it to your babel
list of plug-ins, set your webpack
of babel-loader
configuration or .babelrc
in plugins
to
If you want to know more about how to create babel-plugin
, you can check Babel-handbook .
Application of AST in ESLint
Before officially writing the ESLint
plug-in, you need to understand the working principle ESLint
Among them, the ESLint
should be familiar to everyone. I will not explain it here. If you don't understand, please click the official document How to configure ESLint in the project.
In project development, the source code written by different developers is different, so ESLint
analyze the source code written by each person?
That's right, it is AST
( Abstract Syntax Tree
(Abstract Syntax Tree)), and then sacrifice the picture that has been viewed hundreds of times.
In ESLint
, by default, esprima
used to parse Javascript
, generate an abstract syntax tree, and then go to intercept check whether it meets our prescribed writing method, and finally let it display an error, a warning or a normal pass. ESLint
is the rule ( rules
), and the core of defining the rule is to use AST
for verification. Each rule is independent of each other, you can set to disable off
, warning warn
⚠️ and error error
❌, of course, there is no need to give any prompts for normal passage.
Teach you how to write Eslint plugin
Goals & knowledge points involved
ESLint
plug-in in this article is designed to check whether the code comment :
- Every declarative function and function expression need to be commented;
- Comments are required for each
interface
- Every
enum
header and field need comments; - Each
type
header requires comments; - ......
Knowledge points
AST
abstract syntax treeESLint
Mocha
unit testNpm
released
Scaffolding construction project
Here we use yeoman and generator-eslint to build the scaffolding code of the plug-in, install:
npm install -g yo generator-eslint
Create a new local folder eslint-plugin-pony-comments
:
mkdir eslint-plugin-pony-comments
cd eslint-plugin-pony-comments
Initialize the project structure of the ESLint
plug-in from the command line:
yo eslint:plugin
Next enter the command line interactive process, after the process is over, the ESLint
plug-in project framework and files will be generated
$ yo eslint:plugin
? What is your name? xxx // 作者
? What is the plugin ID? eslint-plugin-pony-comments // 插件名称
? Type a short description of this plugin: 检查代码注释 // 插件描述
? Does this plugin contain custom ESLint rules? (Y/n) Y
? Does this plugin contain custom ESLint rules? Yes // 这个插件是否包含自定义规则
? Does this plugin contain one or more processors? (y/N) N
? Does this plugin contain one or more processors? No // 该插件是否需要处理器
create package.json
create lib\index.js
create README.md
The directory structure of the file at this time is:
.
├── README.md
├── lib
│ ├── processors // 处理器,选择不需要时没有该目录
│ ├── rules // 自定义规则目录
│ └── index.js // 导出规则、处理器以及配置
├── package.json
└── tests
├── processors // 处理器,选择不需要时没有该目录
└── lib
└── rules // 编写规则的单元测试用例
Installation dependencies:
npm install // 或者yarn
At this point, the environment is set up.
Create rules
Take the realization of "each interface
header and field need to be commented" as an example to create a rule, the terminal executes:
yo eslint:rule // 生成默认 eslint rule 模版文件
Now enter the command line interaction process:
$ yo eslint:rule
? What is your name? xxx // 作者
? Where will this rule be published? ESLint Plugin // 选择生成插件模板
? What is the rule ID? no-interface-comments // 规则名称
? Type a short description of this rule: 校验interface注释 // 规则描述
? Type a short example of the code that will fail:
create docs\rules\no-interface-comments.md
create lib\rules\no-interface-comments.js
create tests\lib\rules\no-interface-comments.js
The project structure at this time is:
.
├── README.md
├── docs // 说明文档
│ └── rules
│ └── no-interface-comments.md
├── lib // eslint 规则开发
│ ├── index.js
│ └── rules // 此目录下可以构建多个规则,本文只拿一个规则来讲解
│ └── no-interface-comments.js
├── package.json
└── tests // 单元测试
└── lib
└── rules
└── no-interface-comments.js
ESLint
has three files named after its identifier (for example, no-interface-comments
).
- In the
lib/rules
directory: a source file (for example,no-interface-comments.js
) - In the
tests/lib/rules
directory: a test file (for example,no-interface-comments.js
) - In the
docs/rules
directory: aMarkdown
document file (for example,no-interface-comments
)
Before officially entering the development rules, let’s take a look at the generated rule template no-interface-comments.js
:
/**
* @fileoverview no-interface-comments
* @author xxx
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: "no console.time()",
category: "Fill me in",
recommended: false
},
fixable: null, // or "code" or "whitespace"
schema: [
// fill in your schema
]
},
create: function(context) {
// variables should be defined here
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
// any helper functions should go here or else delete this section
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
// give me methods
};
}
};
This file gives a template for writing rules. A rule corresponds to an exportable node
module, which consists of two parts: meta
and create
meta
: Metadata representing this rule, such as its category, document,schema
parameters, and so on.create
: Ifmeta
expresses what we want to do, thencreate
expresses how thisrule
will analyze the code;
create
returns an object, the most common keys is AST
abstract syntax tree selector , the selector, we can get the contents of the corresponding selected, then we can make some judgments on the selected content To see if our rules are met. If you are not satisfied, you can use context.report
throw a problem, and ESLint
will use our configuration to display the thrown content differently. For details, please refer to: context.report
Before writing the no-interface-comments
rule, let’s look at the AST Explorer see how the interface
code is parsed into AST
.
According to the above AST
structure, we create two selectors verification code comments, TSInterfaceDeclaration
selector check interface
whether the head of the Notes, TSPropertySignature
whether selector check field annotated. Traverse AST
may need to use the following API
, please refer to the official website for details:
fixer.insertTextAfter(nodeOrToken, text)
-Insert text after a given node or tagfixer.insertTextBefore(nodeOrToken, text)
-Insert text before a given node or tagsourceCode.getAllComments()
-Returns an array of all comments in the source codecontext.getSourceCode()
-Get the source code
/**
* @fileoverview interface定义类型注释校验
* @author xxx
*/
'use strict';
const {
docsUrl,
getLastEle,
getAllComments,
judgeNodeType,
getComments,
genHeadComments,
report,
isTailLineComments,
getNodeStartColumn,
genLineComments,
} = require('../utils');
module.exports = {
meta: {
/**
* 规则的类型
* "problem" 意味着规则正在识别将导致错误或可能导致混淆行为的代码。开发人员应将此视为优先解决的问题。
* "suggestion" 意味着规则正在确定可以以更好的方式完成的事情,但如果不更改代码,则不会发生错误。
* "layout" 意味着规则主要关心空格、分号、逗号和括号,程序的所有部分决定了代码的外观而不是它的执行方式。这些规则适用于 AST 中未指定的部分代码。
*/
type: 'layout',
docs: {
description: 'interface定义类型注释校验', // 规则描述
category: 'Fill me in',
recommended: true, // 是配置文件中的"extends": "eslint:recommended"属性是否启用规则
url: 'https://github.com/Revelation2019/eslint-plugin-pony-comments/tree/main/docs/rules/no-interface-comments.md', // 该规则对应在github上的文档介绍
},
fixable: 'whitespace', // or "code" or "whitespace"
schema: [ // 指定选项,比如'pony-comments/no-interface-comments: [2, 'always', { leadingCommentType: 'Block', propertyComments: { pos: 'lead', commentsType: 'Block'}}]'
{
'enum': ['always', 'never'],
},
{
'type': 'object',
'properties': {
/**
* 是否需要头部注释
* 'No':表示不需要头部注释
* 'Line': 表示头部需要单行注释
* 'Block':表示头部需要多行注释
*/
'leadingCommentType': {
'type': 'string',
},
/** 字段注释采用单行还是多行注释 */
'propertyComments': {
'type': 'object',
'properties': {
'pos': {
'type': 'string', // lead || tail 表示注释位置是行头还是行尾
},
'commentsType': {
'type': 'string', // No || Line || Block 表示注释是单行还是多行,或者不需要注释
},
},
},
},
'additionalProperties': false,
},
],
},
create: function(context) {
// 获取选项
const options = context.options;
const leadingCommentsType = options.length > 0 ? getLastEle(options).leadingCommentType : null;
const propertyComments = options.length > 0 ? getLastEle(options).propertyComments : {};
const { pos, commentsType } = propertyComments;
/** 获取所有的注释节点 */
const comments = getAllComments(context);
// 有效的选项值
const commentsTypeArr = ['No', 'Line', 'Block'];
return {
/** 校验interface定义头部注释 */
'TSInterfaceDeclaration': (node) => {
/** 不需要头部注释 */
if (leadingCommentsType === 'No' || !commentsTypeArr.includes(leadingCommentsType)) return;
const { id } = node;
const { name } = id;
// 判断interface的父节点是否是export
if (judgeNodeType(node, 'ExportNamedDeclaration')) {
/** export interface XXX {} */
const { leading } = getComments(context, node.parent);
if (!leading.length) {
// 没有头部注释,抛出断言
report(context, node.parent, '导出的类型头部没有注释', genHeadComments(node.parent, name, leadingCommentsType));
}
} else {
/** enum interface {} */
const { leading } = getComments(context, node); // 获取节点头部和尾部注释
if (!leading.length) {
// 没有头部注释,抛出断言
report(context, node, '类型头部没有注释', genHeadComments(node, name, leadingCommentsType));
}
}
},
/** 校验interface定义字段注释 */
'TSPropertySignature': (node) => {
if (commentsType === 'No' || !commentsTypeArr.includes(commentsType)) return;
/** 避免 export const Main = (props: { name: string }) => {} */
if (judgeNodeType(node, 'TSInterfaceBody')) {
const { key } = node;
const { name } = key;
const { leading } = getComments(context, node); // 获取节点头部和尾部注释
const errorMsg = '类型定义的字段没有注释';
if (isTailLineComments(comments, node) || (leading.length && getNodeStartColumn(getLastEle(leading)) === getNodeStartColumn(node))) {
/**
* 节点尾部已有注释 或者 头部有注释并且注释开头与节点开头列数相同
* 这里判断节点开始位置column与注释开头位置column是因为getComments获取到的头部注释可能是不是当前节点的,比如
interface xxx {
id: string; // id
name: string; // name
}
leading拿到的是// id,但这个注释不是name字段的
*/
return;
}
// 根据选项报出断言,并自动修复
if (commentsType === 'Block' || (commentsType === 'Line' && pos === 'lead')) {
// 自动添加行头多行注释
report(context, node, errorMsg, genHeadComments(node, name, commentsType));
} else {
// 自动添加行尾单行注释
report(context, node, errorMsg, genLineComments(node, name));
}
}
},
};
},
};
Automatic repair function:
/**
* @description 在函数头部加上注释
* @param {Object} node 当前节点
* @param {String} text 注释内容
* @returns
*/
const genHeadComments = (node, text, commentsType) => {
if (!text) return null;
const eol = require('os').EOL; // 获取换行符,window是CRLF,linux是LF
let content = '';
if (commentsType && commentsType.toLowerCase === 'line') {
content = `// ${text}${eol}`;
} else if (commentsType && commentsType.toLowerCase === 'block') {
content = `/** ${text} */${eol}`;
} else {
content = `/** ${text} */${eol}`;
}
return (fixer) => {
return fixer.insertTextBefore(
node,
content,
);
};
};
/**
* @description 生成行尾单行注释
* @param {Object} node 当前节点
* @param {String} value 注释内容
* @returns
*/
const genLineComments = (node, value) => {
return (fixer) => {
return fixer.insertTextAfter(
node,
`// ${value}`,
);
};
};
At this point, the no-interface-comments
rule is basically completed
Configuration in the plugin
You can configs
, which is useful when you want to provide some custom rules that support it. Refer to official website
// lib/index.js
module.exports = {
configs: {
recommended: {
plugins: ['pony-comments'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2018,
},
rules: {
'pony-comments/no-interface-comments': [2, 'always', { leadingCommentType: 'Block', propertyComments: { pos: 'tail', commentsType: 'Line' } }],
}
},
}
};
Plug-in rules will be inherited through the extends configuration:
{
"extends": ["plugin:pony-comments/recommended"]
}
Note: Please note that the configuration by default will not enable any plugin rules, but should be treated as an independent configuration. This means that you must specify your plugin name and any rules you want to enable plugins
Any plugin rules must be prefixed with a short or long plugin name
Create a processor
The processor can tell ESLint
how to handle files other than JavaScript, such as extracts from other types of files in JavaScript
code, and then let ESLint
to JavaScript
code lint
, or for any purpose processor can convert pretreatment JavaScript
code. Refer to official website
// 在lib/index.js中导出自定义处理器,或者将其抽离
module.exports = {
processors: {
"markdown": {
// takes text of the file and filename
preprocess: function(text, filename) {
// here, you can strip out any non-JS content
// and split into multiple strings to lint
return [ // return an array of code blocks to lint
{ text: code1, filename: "0.js" },
{ text: code2, filename: "1.js" },
];
},
// takes a Message[][] and filename
postprocess: function(messages, filename) {
// `messages` argument contains two-dimensional array of Message objects
// where each top-level array item contains array of lint messages related
// to the text that was returned in array from preprocess() method
// you need to return a one-dimensional array of the messages you want to keep
return [].concat(...messages);
},
supportsAutofix: true // (optional, defaults to false)
}
}
};
To specify the processor in the configuration file, use processor
with the key of the connection string of the plug-in name and the processor name (by slashes). For example, the following enable pony-comments
provided by the plug markdown
processors:
{
"plugins": ["pony-comments"],
"processor": "pony-comments/markdown"
}
To specify a processor for a specific type of file, use a combination of the overrides
key and the processor
For example, the following uses the processor pony-comments/markdown
process the *.md
file.
{
"plugins": ["pony-comments"],
"overrides": [
{
"files": ["*.md"],
"processor": "pony-comments/markdown"
}
]
}
The processor may generate named code blocks, such as 0.js
and 1.js
. ESLint
treats such named code blocks as sub-files of the original file. You can overrides
in config
specify additional configuration as a named code block section. For example, the following strict
disables the .js
that markdown
ends with 0616bd472a743a.
{
"plugins": ["pony-comments"],
"overrides": [
{
"files": ["*.md"],
"processor": "pony-comments/markdown"
},
{
"files": ["**/*.md/*.js"],
"rules": {
"strict": "off"
}
}
]
}
ESLint
checks the file path of the named code block, and if any overrides
entries do not match the file path, ignore those. Be sure to add overrides
if you want lint other than the entry for the named code block *.js
.
File extension processor
If the processor name starts with .
, then ESLint
treats the processor as a file extension processor, and automatically applies the processor to the file type. People don't need to specify the file extension processor in their configuration file. E.g:
module.exports = {
processors: {
// This processor will be applied to `*.md` files automatically.
// Also, people can use this processor as "plugin-id/.md" explicitly.
".md": {
preprocess(text, filename) { /* ... */ },
postprocess(messageLists, filename) { /* ... */ }
}
}
}
Write unit tests
eslint.RuleTester
is a utility for writing tests ESLint
RuleTester
constructor accepts an optional object parameter, which can be used to specify the default value of the test case ( official website ). For example, if you can specify @typescript-eslint/parser
parse your test case:
const ruleTester = new RuleTester({ parser: require.resolve('@typescript-eslint/parser') });
When you need to parse the .tsx
file, you need to specify a specific parser, such as @typescript-eslint/parser
, because the default parser used by the eslint
esprima
, which does not support typescript
and react
If the following error is reported during the execution of the test case:
AssertionError [ERR_ASSERTION]: Parsers provided as strings to RuleTester must be absolute paths
This is because the parser need to use absolute paths configuration ESLint RuleTester Parser to use Typescript
/**
* @fileoverview interface定义类型注释校验
* @author xxx
*/
'use strict';
const rule = require('../../../lib/rules/no-interface-comments');
const RuleTester = require('eslint').RuleTester;
const ruleTester = new RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
comment: true,
useJSXTextNode: true,
},
});
ruleTester.run('no-interface-comments', rule, {
// 有效测试用例
valid: [
{
code: `
export const Main = (props: { name: string }) => {}
`,
options: ['always', { leadingCommentType: 'Block', propertyComments: { pos: 'lead', commentsType: 'Block' } }],
},
{
code: `
/** 类型 */
export interface IType {
id: string; // id
name: string; // 姓名
age: number; // 年龄
}
`,
options: ['always', { leadingCommentType: 'Block', propertyComments: { pos: 'tail', commentsType: 'Line' } }],
},
{
code: `
/** 类型 */
interface IType {
/** id */
id: string;
/** 姓名 */
name: string;
/** 年龄 */
age: number;
}
`,
options: ['always', { leadingCommentType: 'Block', propertyComments: { pos: 'lead', commentsType: 'Block' } }],
},
],
// 无效测试用例
invalid: [
{
code: `
export interface IType {
/** id */
id: string;
/** 姓名 */
name: string;
/** 年龄 */
age: number;
}
`,
errors: [{
message: 'interface头部必须加上注释',
type: 'TSInterfaceDeclaration',
}],
options: ['always', { leadingCommentType: 'Block', propertyComments: { pos: 'lead', commentsType: 'Block' } }],
output: `
/** 类型 */
export interface IType {
/** id */
id: string;
/** 姓名 */
name: string;
/** 年龄 */
age: number;
}
`,
},
{
code: `
/** 类型 */
interface IType {
id: string;
name: string;
age: number;
}
`,
errors: [{
message: 'interface字段必须加上注释',
type: 'TSPropertySignature',
}],
options: ['always', { leadingCommentType: 'Block', propertyComments: { pos: 'lead', commentsType: 'Block' } }],
output: `
/** 类型 */
interface IType {
/** id */
id: string;
/** 姓名 */
name: string;
/** 年龄 */
age: number;
}
`,
},
],
});
When yarn test
executes the test case, the console output:
github
Portal: https://github.com/Revelation2019/eslint-plugin-pony-comments
Debug test cases in VSCode
Follow the procedure below to create launch.json
.vscode
directory will be generated in the root directory
launch.json
default content of 0616bd472a764d is as follows:
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\lib\\index.js" // debugger默认启动的脚本
}
]
}
Here we want to debug no-interface-comments
rule, we need to make the following changes:
"program": "${workspaceFolder}\\tests\\lib\\rules\\no-interface-comments.js"
Then, you can debug debugger
Used in the project
(1) Install eslint-plugin-pony-comments
yarn add eslint-plugin-pony-comments -D
(2) Configuration .eslintrc.json
{
"extends": "plugin:@casstime/inquiry-eslint/recommended",
"parser": "@typescript-eslint/parser",
}
(3) Restart the project, restart the eslint
service, and check the results as follows
If yarn start
start many projects Times have not changed the file checksum error, because create-react-app
scaffolding in the default configuration eslint-loader
, in start
or build
when calls eslint
go again to view all files, reference: HTTPS: // V4 .webpack.docschina.org/loaders/eslint-loader/
In view of the above problems, here are two solutions:
- Delete the
node_modules/.cache
folder and restart the project. This is becauseeslint-loader
default bootcache
, thelinting
result cache write./node_modules/.cache
directory, which is essential when performing a complete buildlinting
time is particularly useful, but may affect thestart
orbuild
wheneslint
viewing - In the root directory
config-overrides.js
ofoverride
add functionconfig.module.rules.splice(1, 1);
, because it will review and submit the code when writing code, not at the start of the project or build and then view again, to kill theeslint-loader
release
(1) npm
account login
npm login --registry=仓库镜像
# 输入用户名/密码/邮箱
(2) Perform the build
yarn build
(3) Release
yarn publish --registry=仓库镜像
If the following error is reported, the email may be incorrect
Extracurricular Knowledge: A Brief History of Lint
Lint
is a tool to solve various problems caused by imprecise code. For example ==
and ===
can cause some strange problems.
JSLint and JSHint
In 2002, Douglas Crockford
developed what may be the first for JavaScript
syntax detection tools - JSLint
, and open source in 2010.
JSLint
market, it really helped many JavaScript
developers to save a lot of time in troubleshooting code errors. But JSLint
is also obvious- almost impossible to configure , all code styles and rules are built-in; plus Douglas Crockford
promotes the Taoist tradition of "use and use", and will not open to developers. Configure or modify the rules that he thinks are right. So Anton Kovalyov
Tucao: " JSLint
is to make your code style is more like Douglas Crockford
of it", and in 2011 Fork
original project developed JSHint
. "Why I forked JSLint to JSHint"
JSHint
feature is configurable , while documentation is also relatively well, but also to the developer-friendly. Soon everyone switched JSLint
JSHint
.
The birth of ESLint
In later years it will JSHint
as a code detection tool of choice, but the turning point in 2013, Zakas
found JSHint
unable to meet their own needs rules and and Anton
After discussion, it is impossible to find in JShint
achieve the same time Zakas
also envisaged invention based AST
of lint
. So in June 2013, Zakas
released the new lint
tool- ESLint
. "Introducing ESLint"
var ast = esprima.parse(text, { loc: true, range: true }),
walk = astw(ast);
walk(function(node) {
api.emit(node.type, node);
});
return messages;
ESLint's counterattack
The appearance of ESLint
did not shake the dominance of JSHint
Because the former uses the AST
processing rules and uses Esprima
parse the code, the execution speed is much slower than that of JSHint
. 1616bd472a7b1b. Secondly, there are already many editors that support JSHint
. What really makes ESLint
counterattack is the appearance ECMAScript 6
In June 2015, the ES2015
specification was officially released. However, after the release, the browsers on the market have extremely limited support for the latest standards. If you want to advance to experience the latest standards of grammar, it relies on Babel
tools like code compiled into ES5
even lower version, and some experimental features can rely Babel
conversion. But at this time, JSHint
cannot provide support in the short term, but ESLint
only needs a suitable parser to continue to check lint
Babel
team developed an alternative to the default parser ESLint
babel-eslint
, which makes ESLint
the first lint
tool that ES6
syntax.
Also in 2015, React
became more and more widespread, and the new JSX
became more and more popular. ESLint
itself does not support JSX
syntax. But because scalability, eslint-plugin-react
appear to make ESLint
also gave support to React
-specific rules.
In 2016, the JSCS
development team believed that the ESLint
and JSCS
too similar, and the problems that needed to be solved were the same, and finally chose to merge to ESLint
and stop the maintenance of JSCS
The current mainstream lint
tools and trend charts on the market:
Since then, ESLint
unified the world and has become the mainstream front-end tool to JSHint
refer to:
The transformation of mediocre front-end
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。