Open a-x- opened 5 years ago
I got: FunctionDeclaration has comments field instead of VariableDeclarations's leadingComments
but ast explorer shows leadingComments for functions too.
where is my bad?
Recast uses a different representation for comments than you may be used to. Instead of leadingComments
and trailingComments
properties, there's just a comments
array attached to some nodes, with each comment object in the array having optional boolean leading
and/or trailing
properties. This representation is created by this attachComments
call in lib/parser.ts
based on the recast.parse(...).comments
array, if your parser provides that information.
This representation has proven itself more flexible and effective than having separate {leading,trailing}Comments
arrays, at least for tools that need to handle comments in a reasonable way (Babel doesn't really need to do a good job with comments, for example, because comment placement doesn't matter much in generated code).
One reason for this flexibility is that not all comments are leading or trailing: some are neither, such as if (x) { /*empty*/ }
, and Recast represents this weird but common case by setting both node.leading
and node.trailing
to false
. The Recast representation of comments was inherited by Prettier (Prettier is a fork of Recast's pretty-printer).
Long story short, my advice would be to stop letting your parser attach comments to the nodes it thinks the comments pertain to, and instead let Recast do the attachment. If you can tell me what parser you're using and how it's configured, I'm happy to help you get this working.
I use pre-configured babelv7: 'recast/parsers/babel'
and my parser config:
const babel7 = require('recast/parsers/babel')
const recast = require('recast');
module.exports.parse = function parse (code) {
return recast.parse(code, {
parser: {
parse(source) {
return babel7.parse(source, {
sourceType: "module",
plugins: ["jsx", 'optionalChaining', 'classProperties'],
});
}
}
});
};
Ok, I agree this should work with that setup. I suspect @babel/parser
isn't returning the comments in the expected format, which is definitely something we can work around in recast/parsers/babel
, one way or another.
This seems to be fixed, no?
not fixed. Issue seems abandoned. See #1272
I got: FunctionDeclaration has comments field instead of VariableDeclarations's leadingComments
but ast explorer shows leadingComments for functions too.
where is my bad?
With recast 0.23.9 I am also having FunctionDeclaration having comments block instead of leadingComments or trailingComments. The problem is when I try to extract function AST node, comments are getting missed.
Let I have this code:
/**
* Comments
*/
function testFunction() { return 1;
}
I can access function block like this:
recast.types.visit(ast, {
// Handle function declarations (e.g., function foo() { ... })
visitFunctionDeclaration(path) {
console.log('Comments:', path.node.comments);
functionCallback({path: path.node, type: 'FunctionDeclaration', name: path.node.id.name,
...(path.node.comments?.length ? { comments: safeStringify(path.node.comments, ) } : {}) });
this.traverse(path);
},
});
This prints comments block into console. But when trying to access the same path.node.comments inside functionCallback it becomes an empty array. So I am using stringify()/JSON.parse() workaround to preserve comments:
const funcNode = func.path;
if (func.comments) { // missing comments fix
funcNode.comments = JSON.parse(func.comments);
}
functionDefinitions[func.name] = generateCode(funcNode);
The same behaviour when parsing code to AST tree with default recast.parse(code);
or with babel
recast.parse(code, {
parser: require("recast/parsers/babel"),
});
The only difference is that with recast.parse(code);
I can use JSON.stringify(path.node.comments)
when with babel I have to use a special function to handle circular structures.
ast example
```json { "type": "FunctionDeclaration", "start": 5, "end": 25, "loc": { "start": { "line": 2, "column": 0 }, "end": { "line": 3, "column": 1 } }, "id": { "type": "Identifier", "start": 14, "end": 18, "loc": { "start": { "line": 2, "column": 9 }, "end": { "line": 2, "column": 13 }, "identifierName": "name" }, "name": "name" }, "generator": false, "async": false, "params": [], "body": { "type": "BlockStatement", "start": 22, "end": 25, "loc": { "start": { "line": 2, "column": 17 }, "end": { "line": 3, "column": 1 } }, "body": [], "directives": [] }, "leadingComments": [ { "type": "CommentLine", "value": " 1", "start": 0, "end": 4, "loc": { "start": { "line": 1, "column": 0 }, "end": { "line": 1, "column": 4 } } } ] } ````print(ast).code` result: ```js function name () { } ``` expected: ``` // 1 function name () { } ``` leadingComments is working for VariableDeclarations for example