Espree 是 ESLint 使用的 JavaScript 解析器,基于 Acorn 实现,结合 Acorn-JSX 插件也可解析 React.js JSX。
Espree 将 JavaScript 解析成 AST ,再将 AST Node 传递给 ESLint rule 获得 report 给开发工具,在代码中错误和不符合规范的地方给予开发者提示。当我们在开发 ESLint 规则时,node 将作为参数获取,用于分析代码风格。
以下是解析一段 js 代码的示例:
// demo.js
const a = "123";
console.log("xxx", a);
const jsx = <div className="test">{a}</div>
const issueName = { a: 123, b: 123, c: 123 };
for (let i = 0; i < 99; i++) {
console.log('xxx', i)
}
if (1 > 0) {
console.log('xxx', true)
}
// 使用 espree 解析 demo.js 文件
import { readFile, writeFile } from "fs/promises";
import * as espree from "espree";
// 解析成 AST 并保存
async function parseSave(code) {
const node = espree.parse(code, {
ecmaVersion: 11,
ecmaFeatures: { jsx: true },
})
await writeFile("./file/nodeResult.json", JSON.stringify(node, null, 4))
}
// 解析成 token 并保存
async function tokenSave(code) {
const token = espree.tokenize(code, {
ecmaVersion: 11,
ecmaFeatures: { jsx: true },
})
await writeFile("./file/tokenResult.json", JSON.stringify(token, null, 4))
}
async function parseFile(filePath) {
const file = readFile(filePath, { encoding: "utf-8" })
const code = await file
parseSave(code)
tokenSave(code)
}
parseFile('./file/demo.js')
// tokenResult.json 解析为 token 结果(部分)
[
{
"type": "Keyword",
"value": "const",
"start": 0,
"end": 5
},
// ...
]
// nodeResult.json 解析为 node 结果(部分)
{
"type": "Program",
"start": 0,
"end": 233,
"body": [
{
"type": "VariableDeclaration",
"start": 0,
"end": 16,
"declarations": [
{
"type": "VariableDeclarator",
"start": 6,
"end": 15,
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"name": "a"
},
"init": {
"type": "Literal",
"start": 10,
"end": 15,
"value": "123",
"raw": "\"123\""
}
}
],
"kind": "const"
},
// ...
],
"sourceType": "script"
}
// VisitorKeys.json AST 中的 node 类型,
// 通过 espree.VisitorKeys 获取,
// 用于遍历 AST(all visitor keys for traversing the AST)
{
"AssignmentExpression": ["left", "right"],
"AssignmentPattern": ["left", "right"],
"ArrayExpression": ["elements"],
"ArrayPattern": ["elements"],
"ArrowFunctionExpression": ["params", "body"],
"AwaitExpression": ["argument"],
"BlockStatement": ["body"],
"BinaryExpression": ["left", "right"],
"BreakStatement": ["label"],
"CallExpression": ["callee", "arguments"],
"CatchClause": ["param", "body"],
"ChainExpression": ["expression"],
"ClassBody": ["body"],
"ClassDeclaration": ["id", "superClass", "body"],
"ClassExpression": ["id", "superClass", "body"],
"ConditionalExpression": ["test", "consequent", "alternate"],
"ContinueStatement": ["label"],
"DebuggerStatement": [],
"DoWhileStatement": ["body", "test"],
"EmptyStatement": [],
"ExportAllDeclaration": ["exported", "source"],
"ExportDefaultDeclaration": ["declaration"],
"ExportNamedDeclaration": ["declaration", "specifiers", "source"],
"ExportSpecifier": ["exported", "local"],
"ExpressionStatement": ["expression"],
"ExperimentalRestProperty": ["argument"],
"ExperimentalSpreadProperty": ["argument"],
"ForStatement": ["init", "test", "update", "body"],
"ForInStatement": ["left", "right", "body"],
"ForOfStatement": ["left", "right", "body"],
"FunctionDeclaration": ["id", "params", "body"],
"FunctionExpression": ["id", "params", "body"],
"Identifier": [],
"IfStatement": ["test", "consequent", "alternate"],
"ImportDeclaration": ["specifiers", "source"],
"ImportDefaultSpecifier": ["local"],
"ImportExpression": ["source"],
"ImportNamespaceSpecifier": ["local"],
"ImportSpecifier": ["imported", "local"],
"JSXAttribute": ["name", "value"],
"JSXClosingElement": ["name"],
"JSXElement": ["openingElement", "children", "closingElement"],
"JSXEmptyExpression": [],
"JSXExpressionContainer": ["expression"],
"JSXIdentifier": [],
"JSXMemberExpression": ["object", "property"],
"JSXNamespacedName": ["namespace", "name"],
"JSXOpeningElement": ["name", "attributes"],
"JSXSpreadAttribute": ["argument"],
"JSXText": [],
"JSXFragment": ["openingFragment", "children", "closingFragment"],
"Literal": [],
"LabeledStatement": ["label", "body"],
"LogicalExpression": ["left", "right"],
"MemberExpression": ["object", "property"],
"MetaProperty": ["meta", "property"],
"MethodDefinition": ["key", "value"],
"NewExpression": ["callee", "arguments"],
"ObjectExpression": ["properties"],
"ObjectPattern": ["properties"],
"PrivateIdentifier": [],
"Program": ["body"],
"Property": ["key", "value"],
"PropertyDefinition": ["key", "value"],
"RestElement": ["argument"],
"ReturnStatement": ["argument"],
"SequenceExpression": ["expressions"],
"SpreadElement": ["argument"],
"StaticBlock": ["body"],
"Super": [],
"SwitchStatement": ["discriminant", "cases"],
"SwitchCase": ["test", "consequent"],
"TaggedTemplateExpression": ["tag", "quasi"],
"TemplateElement": [],
"TemplateLiteral": ["quasis", "expressions"],
"ThisExpression": [],
"ThrowStatement": ["argument"],
"TryStatement": ["block", "handler", "finalizer"],
"UnaryExpression": ["argument"],
"UpdateExpression": ["argument"],
"VariableDeclaration": ["declarations"],
"VariableDeclarator": ["id", "init"],
"WhileStatement": ["test", "body"],
"WithStatement": ["object", "body"],
"YieldExpression": ["argument"]
}