在节点js中使用抽象语法树获取行号
Get line number with abstract syntax tree in node js
我正在制作一个程序,通过参数获取一些代码,并转换代码,向代码中添加一些 console.logs。这是程序:
const escodegen = require('escodegen');
const espree = require('espree');
const estraverse = require('estraverse');
function addLogging(code) {
const ast = espree.parse(code);
estraverse.traverse(ast, {
enter: function(node, parent) {
if (node.type === 'FunctionDeclaration' ||
node.type === 'FunctionExpression') {
addBeforeCode(node);
}
}
});
return escodegen.generate(ast);
}
function addBeforeCode(node) {
const name = node.id ? node.id.name : '<anonymous function>';
const beforeCode = "console.log('Entering " + name + "()');";
const beforeNodes = espree.parse(beforeCode).body;
node.body.body = beforeNodes.concat(node.body.body);
}
因此,如果我们将此代码传递给函数:
console.log(addLogging(`
function foo(a, b) {
var x = 'blah';
var y = (function () {
return 3;
})();
}
foo(1, 'wut', 3);
`));
这是这个程序的输出:
function foo(a, b) {
console.log('Entering foo()');
var x = 'blah';
var y = function () {
console.log('Entering <anonymous function>()');
return 3;
}();
}
foo(1, 'wut', 3);
这是传递给 addLoggin 的最后一个函数的 AST(抽象语法树):
https://astexplorer.net/#/gist/b5826862c47dfb7dbb54cec15079b430/latest
所以我想向控制台日志添加更多信息,例如我们所在的行号。据我所知,在 ast 中,该节点有一个名为 'start' 和 'end' 的值,它指示该节点从哪个字符开始以及从哪里结束。我如何使用它来获取我们所在的行号?老实说,我似乎很困惑。我正在考虑按“\n”分割文件,这样我就有了总行号,但是我怎么知道我在哪一个呢?
提前致谢。
你的想法很好。首先找到原始代码中每行开始的偏移量。然后将节点的 start
索引与那些收集到的索引进行比较,以确定行号。
我在这里假设您希望报告的行号引用原始代码,而不是您的函数返回的代码。
因此,从下往上,进行以下更改。首先期望行号作为 addBeforeCode
:
的参数
function addBeforeCode(node, lineNum) {
const name = node.id ? node.id.name : '<anonymous function>';
const beforeCode = `console.log("${lineNum}: Entering ${name}()");`;
const beforeNodes = espree.parse(beforeCode).body;
node.body.body = beforeNodes.concat(node.body.body);
}
定义一个函数来收集原始代码中对应于行开头的偏移量:
function getLineOffsets(str) {
const regex = /\r?\n/g;
const offsets = [0];
while (regex.exec(str)) offsets.push(regex.lastIndex);
offsets.push(str.length);
return offsets;
}
注意:如果你支持matchAll
,那么上面可以写得更简洁一些。
然后在你的主函数中使用上面的代码:
function addLogging(code) {
const lineStarts = getLineOffsets(code); // <---
let lineNum = 0; // <---
const ast = espree.parse(code);
estraverse.traverse(ast, {
enter: function(node, parent) {
if (node.type === 'FunctionDeclaration' ||
node.type === 'FunctionExpression') {
// Look for the corresponding line number in the source code:
while (lineStarts[lineNum] < node.body.body[0].start) lineNum++;
// Actually we now went one line too far, so pass one less:
addBeforeCode(node, lineNum-1);
}
}
});
return escodegen.generate(ast);
}
与您的问题无关,但请注意函数可以是箭头函数,它具有表达式语法。所以他们不会有一个块,你将无法以相同的方式注入 console.log
。您可能想让您的代码能够处理该问题,或者跳过这些问题。
我正在制作一个程序,通过参数获取一些代码,并转换代码,向代码中添加一些 console.logs。这是程序:
const escodegen = require('escodegen');
const espree = require('espree');
const estraverse = require('estraverse');
function addLogging(code) {
const ast = espree.parse(code);
estraverse.traverse(ast, {
enter: function(node, parent) {
if (node.type === 'FunctionDeclaration' ||
node.type === 'FunctionExpression') {
addBeforeCode(node);
}
}
});
return escodegen.generate(ast);
}
function addBeforeCode(node) {
const name = node.id ? node.id.name : '<anonymous function>';
const beforeCode = "console.log('Entering " + name + "()');";
const beforeNodes = espree.parse(beforeCode).body;
node.body.body = beforeNodes.concat(node.body.body);
}
因此,如果我们将此代码传递给函数:
console.log(addLogging(`
function foo(a, b) {
var x = 'blah';
var y = (function () {
return 3;
})();
}
foo(1, 'wut', 3);
`));
这是这个程序的输出:
function foo(a, b) {
console.log('Entering foo()');
var x = 'blah';
var y = function () {
console.log('Entering <anonymous function>()');
return 3;
}();
}
foo(1, 'wut', 3);
这是传递给 addLoggin 的最后一个函数的 AST(抽象语法树):
https://astexplorer.net/#/gist/b5826862c47dfb7dbb54cec15079b430/latest
所以我想向控制台日志添加更多信息,例如我们所在的行号。据我所知,在 ast 中,该节点有一个名为 'start' 和 'end' 的值,它指示该节点从哪个字符开始以及从哪里结束。我如何使用它来获取我们所在的行号?老实说,我似乎很困惑。我正在考虑按“\n”分割文件,这样我就有了总行号,但是我怎么知道我在哪一个呢?
提前致谢。
你的想法很好。首先找到原始代码中每行开始的偏移量。然后将节点的 start
索引与那些收集到的索引进行比较,以确定行号。
我在这里假设您希望报告的行号引用原始代码,而不是您的函数返回的代码。
因此,从下往上,进行以下更改。首先期望行号作为 addBeforeCode
:
function addBeforeCode(node, lineNum) {
const name = node.id ? node.id.name : '<anonymous function>';
const beforeCode = `console.log("${lineNum}: Entering ${name}()");`;
const beforeNodes = espree.parse(beforeCode).body;
node.body.body = beforeNodes.concat(node.body.body);
}
定义一个函数来收集原始代码中对应于行开头的偏移量:
function getLineOffsets(str) {
const regex = /\r?\n/g;
const offsets = [0];
while (regex.exec(str)) offsets.push(regex.lastIndex);
offsets.push(str.length);
return offsets;
}
注意:如果你支持matchAll
,那么上面可以写得更简洁一些。
然后在你的主函数中使用上面的代码:
function addLogging(code) {
const lineStarts = getLineOffsets(code); // <---
let lineNum = 0; // <---
const ast = espree.parse(code);
estraverse.traverse(ast, {
enter: function(node, parent) {
if (node.type === 'FunctionDeclaration' ||
node.type === 'FunctionExpression') {
// Look for the corresponding line number in the source code:
while (lineStarts[lineNum] < node.body.body[0].start) lineNum++;
// Actually we now went one line too far, so pass one less:
addBeforeCode(node, lineNum-1);
}
}
});
return escodegen.generate(ast);
}
与您的问题无关,但请注意函数可以是箭头函数,它具有表达式语法。所以他们不会有一个块,你将无法以相同的方式注入 console.log
。您可能想让您的代码能够处理该问题,或者跳过这些问题。