Search code examples
rustswc-compiler

How can I get comments from swc_ecma_parser


use swc_common::sync::Lrc;
use swc_common::{FileName, SourceMap};
use swc_ecma_parser::{Parser, StringInput, Syntax};

fn main() {
    let cm: Lrc<SourceMap> = Default::default();

    // Real usage
    // let fm = cm
    //     .load_file(Path::new("test.js"))
    //     .expect("failed to load test.js");
    let fm = cm.new_source_file(
        FileName::Custom("test.js".into()),
        "
        // test
        function foo() {}
        "
        .into(),
    );

    let ts_config = swc_ecma_parser::TsConfig {
        decorators: true,
        // dynamic_import: true,
        tsx: true,
        ..Default::default()
    };

    let mut parser = Parser::new(
        Syntax::Typescript(ts_config),
        StringInput::from(&*fm),
        Default::default(),
    );

    let module = parser
        .parse_typescript_module()
        .expect("failed to parser module");

    module.body.into_iter().for_each(|module_item| {
        println!("{:?}", module_item);
    });

}

I am trying to parse script file and do some modification to the AST, and then generate new code based on modifed AST. But when I do a simple test, swc_ecma_parser doesn't provide comment node. See below output of println:

Stmt(Decl(Fn(FnDecl { ident: Ident { span: Span { lo: BytePos(35), hi: BytePos(38), ctxt: #0 }, sym: "foo", optional: false }, declare: false, function: Function { params: [], decorators: [], span: Span { lo: BytePos(26), hi: BytePos(43), ctxt: #0 }, body: Some(BlockStmt { span: Span { lo: BytePos(41), hi: BytePos(43), ctxt: #0 }, stmts: [] }), is_generator: false, is_async: false, type_params: None, return_type: None } })))

It only has the function declarion node, the comment // test is stripped.

How can I get the comment, and change the comment // test to // modified comment. Thanks


Solution

  • The comments are indeed not part of the AST, but the parser handles them separately. The third argument of Parser::new is comments: Option<&'a dyn Comments>. In your code you provide Default::default() here, which is simply None for an Option, suppressing any comment parsing. You can instead create one of the implementations of the Comments trait, such as SingleThreadedComments:

    let comments: swc_common::comments::SingleThreadedComments = Default::default();
    let mut parser = Parser::new(
        Syntax::Typescript(ts_config),
        StringInput::from(&*fm),
        Some(&comments),
    );
    

    After invoking the parser, comments should be updated (the API uses a shared reference for reasons). You should be able to mutably iterate through the comments using borrow_all_mut.

    Finally, it seems like swc::Compiler::print is the method you will want to use to convert the AST back into code. Again, this method accepts comments: Option<&dyn Comments> as its last argument.