发布时间:2020-01-19编辑:佚名阅读(1834)
Esprima 是一个用于对 JS 代码做词法或者语法分析的工具
只支持js,不支持 flow 或者 typescript 格式
当前最新版本是4.0,主要提供两个API:
parseScript 解析不包含 import 和 export 之类的js 代码
parseModule 解析含 import 和 export 之类的js 代码
4.0 以下的版本仅支持 parse 方法,需自行判断是 script 还是 module
语法格式
esprima.parseScript(input, config, delegate) esprima.parseModule(input, config, delegate)
input 代表原始 js 字符串
config 是如下的配置对象:
config
delegate参数
// node 包含节点类型等信息,metadata 包含该节点位置等信息 function (node, metadata) { console.log(node.type, metadata); }
Esprima 是用来做词法和语法分析的,这需要对其解析之后的对象结构有清楚的了解,本节分析 Esprima 解析后生成的语法结构树。
语法树的总体结构就两种
interface Program { type: 'Program'; sourceType: 'script'; body: StatementListItem[]; } interface Program { type: 'Program'; sourceType: 'module'; body: ModuleItem[]; }
StatementListItem && ModuleItem
其中 ModuleItem 只是比 StatementListItem 多了导入和导出两个module才会用到的类型,这两个类型用的少,所以只用关心 StatementListItem
type StatementListItem = Declaration | Statement; type ModuleItem = ImportDeclaration | ExportDeclaration | StatementListItem;
从 StatementListItem 可看出其只包含 Declaration(变量声明) 和 Statement(执行语句)
type Declaration = ClassDeclaration | FunctionDeclaration | VariableDeclaration;
type Statement = BlockStatement | BreakStatement | ContinueStatement | DebuggerStatement | DoWhileStatement | EmptyStatement | ExpressionStatement | ForStatement | ForInStatement | ForOfStatement | FunctionDeclaration | IfStatement | LabeledStatement | ReturnStatement | SwitchStatement | ThrowStatement | TryStatement | VariableDeclaration | WhileStatement | WithStatement;
其中 ExpressionStatement 比较复杂
interface ExpressionStatement { type: 'ExpressionStatement'; expression: Expression; directive?: string; } // Expression 类型 type Expression = ThisExpression | Identifier | Literal | ArrayExpression | ObjectExpression | FunctionExpression | ArrowFunctionExpression | ClassExpression | TaggedTemplateExpression | MemberExpression | Super | MetaProperty | NewExpression | CallExpression | UpdateExpression | AwaitExpression | UnaryExpression | BinaryExpression | LogicalExpression | ConditionalExpression | YieldExpression | AssignmentExpression | SequenceExpression;
Esprima 本质上将 js 代码解析成了两大部分:
3 种变量声明(函数、变量和类)
表达式
其中表达式又被分为了两大类:
关键字组成的 statement,如 IfStatement, ForStatement等,这里面的BlockStatement有些特殊,因为其body又是 StatementListItem,产生递归。
运算语句(赋值、计算之类的操作)组成的 ExpressionStatement
看个例子:
// 解析 var answer = 6 * 7; if(true){answer =1} // 结果 { "type": "Program", "sourceType": "script", "body": [ { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": { "type": "Identifier", "name": "answer" }, "init": { "type": "BinaryExpression", "operator": "*", "left": { "type": "Literal", "value": 6, "raw": "6" }, "right": { "type": "Literal", "value": 7, "raw": "7" } } } ], "kind": "var" }, { "type": "IfStatement", "test": { "type": "Literal", "value": true, "raw": "true" }, "consequent": { "type": "BlockStatement", "body": [ { "type": "ExpressionStatement", "expression": { "type": "AssignmentExpression", "operator": "=", "left": { "type": "Identifier", "name": "answer" }, "right": { "type": "Literal", "value": 1, "raw": "1" } } } ] }, "alternate": null } ] }
去除 console.log() 语句,主要利用了 delegate 的第二个参数获取 console.log() 语句的位置,然后做字符串拼接
const esprima = require('esprima'); // console.log(x) or console['error'](y) function isConsoleCall(node) { return (node.type === 'CallExpression') && (node.callee.type === 'MemberExpression') && (node.callee.object.type === 'Identifier') && (node.callee.object.name === 'console'); } function removeCalls(source) { const entries = []; esprima.parseScript(source, {}, function (node, meta) { if (isConsoleCall(node)) { entries.push({ start: meta.start.offset, end: meta.end.offset }); } }); entries.sort((a, b) => { return b.end - a.end }).forEach(n => { source = source.slice(0, n.start) + source.slice(n.end); }); return source; }
关键字: JS Esprima 基本用法
上一篇:Node.js安全清单
0人
0人
0人
0人