swc_ecma_codegen 0.2.0

Ecmascript code generator for the swc project.
extern crate swc_ecma_parser;
extern crate testing;
use self::{
    swc_ecma_parser::{PResult, Parser, Session, SourceFileInput},
    testing::NormalizedOutput,
};
use super::*;
use crate::config::Config;
use sourcemap::SourceMapBuilder;
use std::{
    io::Write,
    path::Path,
    sync::{Arc, RwLock},
};
use swc_common::{FileName, FilePathMapping, SourceMap};

struct Noop;
impl Handlers for Noop {}

struct Builder {
    cfg: Config,
    cm: Lrc<SourceMap>,
}

fn test() -> Builder {
    let src = SourceMap::new(FilePathMapping::empty());

    Builder {
        cfg: Default::default(),
        cm: Lrc::new(src),
    }
}

impl Builder {
    pub fn with<F, Ret>(self, src: &str, s: &mut Vec<u8>, op: F) -> Ret
    where
        F: FnOnce(&mut Emitter) -> Ret,
    {
        let mut src_map_builder = SourceMapBuilder::new(Some(src));
        let mut e = Emitter {
            cfg: self.cfg,
            cm: self.cm.clone(),
            wr: box text_writer::JsWriter::new(self.cm.clone(), "\n", s, &mut src_map_builder),
            handlers: box Noop,
            pos_of_leading_comments: Default::default(),
        };

        let ret = op(&mut e);

        ret
    }

    pub fn text<F>(self, src: &str, op: F) -> String
    where
        F: FnOnce(&mut Emitter),
    {
        let mut buf = vec![];

        self.with(src, &mut buf, op);

        String::from_utf8(buf).unwrap()
    }
}

fn test_from_to(from: &str, to: &str) {
    fn with_parser<F, Ret>(
        file_name: &Path,
        s: &str,
        f: F,
    ) -> std::result::Result<Ret, NormalizedOutput>
    where
        F: for<'a> FnOnce(&mut Parser<'a, SourceFileInput>) -> PResult<'a, Ret>,
    {
        self::testing::run_test(|logger, cm, handler| {
            let src = cm.new_source_file(FileName::Real(file_name.into()), s.to_string());
            println!(
                "Source: \n{}\nPos: {:?} ~ {:?}",
                s, src.start_pos, src.end_pos
            );

            let res = f(&mut Parser::new(
                Session {
                    logger: &logger,
                    handler: &handler,
                    cfg: Default::default(),
                },
                (&*src).into(),
            ));

            res
        })
    }
    let res = with_parser(Path::new("test.js"), from, |p| p.parse_module()).unwrap();

    assert_eq!(test().text(from, |e| e.emit_module(&res).unwrap()), to,);
}

#[test]
fn empty_stmt() {
    test_from_to(";", ";\n");
}

#[test]
#[ignore]
fn simple_if_else_stmt() {
    test_from_to("if(true);else;", "if (true) ; else ;\n");
}

#[test]
#[ignore]
fn arrow() {
    test_from_to("()=>void a", "()=>void a;");
}

#[test]
#[ignore]
fn array() {
    test_from_to("[a, 'b', \"c\"]", "[a, 'b', 'c'];");
}

#[test]
#[ignore]
fn comment_1() {
    test_from_to(
        "// foo
a",
        "// foo
a;",
    );
}

#[test]
#[ignore]
fn comment_2() {
    test_from_to("a // foo", "a; // foo");
}

#[test]
#[ignore]
fn comment_3() {
    test_from_to(
        "// foo
        // bar
        a
        // foo
        b
        // bar",
        "// foo
        // bar
        a
        // foo
        b
        // bar",
    );
}

#[test]
#[ignore]
fn comment_4() {
    test_from_to("/** foo */ a", "/** foo */  a;");
}

#[test]
#[ignore]
fn comment_5() {
    test_from_to(
        "// foo
        // bar
        a",
        "// foo
        // bar
        a;",
    );
}

#[derive(Debug, Clone)]
struct Buf(Arc<RwLock<Vec<u8>>>);
impl Write for Buf {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        self.0.write().unwrap().write(data)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.write().unwrap().flush()
    }
}