#![deny(clippy::all)]
#![deny(clippy::all)]
#![deny(unused)]
#![allow(clippy::result_unit_err)]
use std::{
    env,
    fs::{self, create_dir_all, read_to_string, OpenOptions},
    io::Write,
    mem::{take, transmute},
    panic,
    path::{Path, PathBuf},
    process::Command,
    rc::Rc,
};
use ansi_term::Color;
use anyhow::Error;
use base64::prelude::{Engine, BASE64_STANDARD};
use serde::de::DeserializeOwned;
use sha2::{Digest, Sha256};
use swc_common::{
    chain,
    comments::{Comments, SingleThreadedComments},
    errors::{Handler, HANDLER},
    source_map::SourceMapGenConfig,
    sync::Lrc,
    FileName, Mark, SourceMap, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_codegen::to_code_default;
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
use swc_ecma_testing::{exec_node_js, JsExecOptions};
use swc_ecma_transforms_base::{
    fixer,
    helpers::{inject_helpers, HELPERS},
    hygiene,
    pass::noop,
};
use swc_ecma_utils::{quote_ident, quote_str, ExprFactory};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, FoldWith, VisitMut};
use tempfile::tempdir_in;
use testing::{
    assert_eq, find_executable, NormalizedOutput, CARGO_TARGET_DIR, CARGO_WORKSPACE_ROOT,
};
pub mod babel_like;
pub struct Tester<'a> {
    pub cm: Lrc<SourceMap>,
    pub handler: &'a Handler,
    pub comments: Rc<SingleThreadedComments>,
}
impl Tester<'_> {
    pub fn run<F, Ret>(op: F) -> Ret
    where
        F: FnOnce(&mut Tester<'_>) -> Result<Ret, ()>,
    {
        let comments = Rc::new(SingleThreadedComments::default());
        let out = ::testing::run_test(false, |cm, handler| {
            HANDLER.set(handler, || {
                HELPERS.set(&Default::default(), || {
                    let cmts = comments.clone();
                    let c = Box::new(unsafe {
                        transmute::<&dyn Comments, &'static dyn Comments>(&*cmts)
                    }) as Box<dyn Comments>;
                    swc_common::comments::COMMENTS.set(&c, || {
                        op(&mut Tester {
                            cm,
                            handler,
                            comments,
                        })
                    })
                })
            })
        });
        match out {
            Ok(ret) => ret,
            Err(stderr) => panic!("Stderr:\n{}", stderr),
        }
    }
    pub(crate) fn run_captured<F, T>(op: F) -> (Option<T>, NormalizedOutput)
    where
        F: FnOnce(&mut Tester<'_>) -> Result<T, ()>,
    {
        let mut res = None;
        let output = ::testing::Tester::new().print_errors(|cm, handler| -> Result<(), _> {
            HANDLER.set(&handler, || {
                HELPERS.set(&Default::default(), || {
                    let result = op(&mut Tester {
                        cm,
                        handler: &handler,
                        comments: Default::default(),
                    });
                    res = result.ok();
                    Err(())
                })
            })
        });
        let output = output
            .err()
            .unwrap_or_else(|| NormalizedOutput::from(String::from("")));
        (res, output)
    }
    pub fn with_parser<F, T>(
        &mut self,
        file_name: &str,
        syntax: Syntax,
        src: &str,
        op: F,
    ) -> Result<T, ()>
    where
        F: FnOnce(&mut Parser<Lexer>) -> Result<T, swc_ecma_parser::error::Error>,
    {
        let fm = self
            .cm
            .new_source_file(FileName::Real(file_name.into()).into(), src.into());
        let mut p = Parser::new(syntax, StringInput::from(&*fm), Some(&self.comments));
        let res = op(&mut p).map_err(|e| e.into_diagnostic(self.handler).emit());
        for e in p.take_errors() {
            e.into_diagnostic(self.handler).emit()
        }
        res
    }
    pub fn parse_module(&mut self, file_name: &str, src: &str) -> Result<Module, ()> {
        self.with_parser(file_name, Syntax::default(), src, |p| p.parse_module())
    }
    pub fn parse_stmts(&mut self, file_name: &str, src: &str) -> Result<Vec<Stmt>, ()> {
        let stmts = self.with_parser(file_name, Syntax::default(), src, |p| {
            p.parse_script().map(|script| script.body)
        })?;
        Ok(stmts)
    }
    pub fn parse_stmt(&mut self, file_name: &str, src: &str) -> Result<Stmt, ()> {
        let mut stmts = self.parse_stmts(file_name, src)?;
        assert!(stmts.len() == 1);
        Ok(stmts.pop().unwrap())
    }
    pub fn apply_transform<T: Fold>(
        &mut self,
        mut tr: T,
        name: &str,
        syntax: Syntax,
        is_module: Option<bool>,
        src: &str,
    ) -> Result<Program, ()> {
        let program =
            self.with_parser(
                name,
                syntax,
                src,
                |parser: &mut Parser<Lexer>| match is_module {
                    Some(true) => parser.parse_module().map(Program::Module),
                    Some(false) => parser.parse_script().map(Program::Script),
                    None => parser.parse_program(),
                },
            )?;
        Ok(program.fold_with(&mut tr))
    }
    pub fn print(&mut self, program: &Program, comments: &Rc<SingleThreadedComments>) -> String {
        to_code_default(self.cm.clone(), Some(comments), program)
    }
}
struct RegeneratorHandler;
impl VisitMut for RegeneratorHandler {
    noop_visit_mut_type!();
    fn visit_mut_module_item(&mut self, item: &mut ModuleItem) {
        if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = item {
            if &*import.src.value != "regenerator-runtime" {
                return;
            }
            let s = import.specifiers.iter().find_map(|v| match v {
                ImportSpecifier::Default(rt) => Some(rt.local.clone()),
                _ => None,
            });
            let s = match s {
                Some(v) => v,
                _ => return,
            };
            let init = CallExpr {
                span: DUMMY_SP,
                callee: quote_ident!("require").as_callee(),
                args: vec![quote_str!("regenerator-runtime").as_arg()],
                ..Default::default()
            }
            .into();
            let decl = VarDeclarator {
                span: DUMMY_SP,
                name: s.into(),
                init: Some(init),
                definite: Default::default(),
            };
            *item = VarDecl {
                span: import.span,
                kind: VarDeclKind::Var,
                declare: false,
                decls: vec![decl],
                ..Default::default()
            }
            .into()
        }
    }
}
fn make_tr<F, P>(op: F, tester: &mut Tester<'_>) -> impl Fold
where
    F: FnOnce(&mut Tester<'_>) -> P,
    P: Fold,
{
    chain!(op(tester), as_folder(RegeneratorHandler))
}
#[track_caller]
pub fn test_transform<F, P>(
    syntax: Syntax,
    is_module: Option<bool>,
    tr: F,
    input: &str,
    expected: &str,
) where
    F: FnOnce(&mut Tester) -> P,
    P: Fold,
{
    Tester::run(|tester| {
        let expected = tester.apply_transform(
            as_folder(::swc_ecma_utils::DropSpan),
            "output.js",
            syntax,
            is_module,
            expected,
        )?;
        let expected_comments = take(&mut tester.comments);
        println!("----- Actual -----");
        let tr = make_tr(tr, tester);
        let actual = tester.apply_transform(tr, "input.js", syntax, is_module, input)?;
        match ::std::env::var("PRINT_HYGIENE") {
            Ok(ref s) if s == "1" => {
                let hygiene_src = tester.print(
                    &actual.clone().fold_with(&mut HygieneVisualizer),
                    &tester.comments.clone(),
                );
                println!("----- Hygiene -----\n{}", hygiene_src);
            }
            _ => {}
        }
        let actual = actual
            .fold_with(&mut as_folder(::swc_ecma_utils::DropSpan))
            .fold_with(&mut hygiene::hygiene())
            .fold_with(&mut fixer::fixer(Some(&tester.comments)));
        println!("{:?}", tester.comments);
        println!("{:?}", expected_comments);
        {
            let (actual_leading, actual_trailing) = tester.comments.borrow_all();
            let (expected_leading, expected_trailing) = expected_comments.borrow_all();
            if actual == expected
                && *actual_leading == *expected_leading
                && *actual_trailing == *expected_trailing
            {
                return Ok(());
            }
        }
        let (actual_src, expected_src) = (
            tester.print(&actual, &tester.comments.clone()),
            tester.print(&expected, &expected_comments),
        );
        if actual_src == expected_src {
            return Ok(());
        }
        println!(">>>>> {} <<<<<\n{}", Color::Green.paint("Orig"), input);
        println!(">>>>> {} <<<<<\n{}", Color::Green.paint("Code"), actual_src);
        if actual_src != expected_src {
            panic!(
                r#"assertion failed: `(left == right)`
            {}"#,
                ::testing::diff(&actual_src, &expected_src),
            );
        }
        Err(())
    });
}
#[doc(hidden)]
#[track_caller]
pub fn test_inline_input_output<F, P>(
    syntax: Syntax,
    is_module: Option<bool>,
    tr: F,
    input: &str,
    output: &str,
) where
    F: FnOnce(&mut Tester) -> P,
    P: Fold,
{
    let _logger = testing::init();
    let expected = output;
    let expected_src = Tester::run(|tester| {
        let expected_program =
            tester.apply_transform(noop(), "expected.js", syntax, is_module, expected)?;
        let expected_src = tester.print(&expected_program, &Default::default());
        println!(
            "----- {} -----\n{}",
            Color::Green.paint("Expected"),
            expected_src
        );
        Ok(expected_src)
    });
    let actual_src = Tester::run_captured(|tester| {
        println!("----- {} -----\n{}", Color::Green.paint("Input"), input);
        let tr = tr(tester);
        println!("----- {} -----", Color::Green.paint("Actual"));
        let actual = tester.apply_transform(tr, "input.js", syntax, is_module, input)?;
        match ::std::env::var("PRINT_HYGIENE") {
            Ok(ref s) if s == "1" => {
                let hygiene_src = tester.print(
                    &actual.clone().fold_with(&mut HygieneVisualizer),
                    &Default::default(),
                );
                println!(
                    "----- {} -----\n{}",
                    Color::Green.paint("Hygiene"),
                    hygiene_src
                );
            }
            _ => {}
        }
        let actual = actual
            .fold_with(&mut crate::hygiene::hygiene())
            .fold_with(&mut crate::fixer::fixer(Some(&tester.comments)));
        let actual_src = tester.print(&actual, &Default::default());
        Ok(actual_src)
    })
    .0
    .unwrap();
    assert_eq!(
        expected_src, actual_src,
        "Exepcted:\n{expected_src}\nActual:\n{actual_src}\n",
    );
}
#[doc(hidden)]
#[track_caller]
pub fn test_inlined_transform<F, P>(
    test_name: &str,
    syntax: Syntax,
    module: Option<bool>,
    tr: F,
    input: &str,
) where
    F: FnOnce(&mut Tester) -> P,
    P: Fold,
{
    let loc = panic::Location::caller();
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
    let test_file_path = CARGO_WORKSPACE_ROOT.join(loc.file());
    let snapshot_dir = manifest_dir.join("tests").join("__swc_snapshots__").join(
        test_file_path
            .strip_prefix(&manifest_dir)
            .expect("test_inlined_transform does not support paths outside of the crate root"),
    );
    test_fixture_inner(
        syntax,
        Box::new(move |tester| Box::new(tr(tester))),
        input,
        &snapshot_dir.join(format!("{test_name}.js")),
        FixtureTestConfig {
            module,
            ..Default::default()
        },
    )
}
#[doc(hidden)]
#[macro_export]
macro_rules! test_location {
    () => {{
        $crate::TestLocation {}
    }};
}
#[macro_export]
macro_rules! test_inline {
    (ignore, $syntax:expr, $tr:expr, $test_name:ident, $input:expr, $output:expr) => {
        #[test]
        #[ignore]
        fn $test_name() {
            $crate::test_inline_input_output($syntax, None, $tr, $input, $output)
        }
    };
    ($syntax:expr, $tr:expr, $test_name:ident, $input:expr, $output:expr) => {
        #[test]
        fn $test_name() {
            $crate::test_inline_input_output($syntax, None, $tr, $input, $output)
        }
    };
}
test_inline!(
    ignore,
    Syntax::default(),
    |_| noop(),
    test_inline_ignored,
    "class Foo {}",
    "class Foo {}"
);
test_inline!(
    Syntax::default(),
    |_| noop(),
    test_inline_pass,
    "class Foo {}",
    "class Foo {}"
);
#[test]
#[should_panic]
fn test_inline_should_fail() {
    test_inline_input_output(Default::default(), None, |_| noop(), "class Foo {}", "");
}
#[macro_export]
macro_rules! test {
    (ignore, $syntax:expr, $tr:expr, $test_name:ident, $input:expr) => {
        #[test]
        #[ignore]
        fn $test_name() {
            $crate::test_inlined_transform(stringify!($test_name), $syntax, None, $tr, $input)
        }
    };
    ($syntax:expr, $tr:expr, $test_name:ident, $input:expr) => {
        #[test]
        fn $test_name() {
            $crate::test_inlined_transform(stringify!($test_name), $syntax, None, $tr, $input)
        }
    };
    (module, $syntax:expr, $tr:expr, $test_name:ident, $input:expr) => {
        #[test]
        fn $test_name() {
            $crate::test_inlined_transform(stringify!($test_name), $syntax, Some(true), $tr, $input)
        }
    };
    (script, $syntax:expr, $tr:expr, $test_name:ident, $input:expr) => {
        #[test]
        fn $test_name() {
            $crate::test_inlined_script_transform(
                stringify!($test_name),
                $syntax,
                Some(false),
                $tr,
                $input,
            )
        }
    };
    ($syntax:expr, $tr:expr, $test_name:ident, $input:expr, ok_if_code_eq) => {
        #[test]
        fn $test_name() {
            $crate::test_inlined_transform(stringify!($test_name), $syntax, None, $tr, $input)
        }
    };
}
pub fn compare_stdout<F, P>(syntax: Syntax, tr: F, input: &str)
where
    F: FnOnce(&mut Tester<'_>) -> P,
    P: Fold,
{
    Tester::run(|tester| {
        let tr = make_tr(tr, tester);
        let program = tester.apply_transform(tr, "input.js", syntax, Some(true), input)?;
        match ::std::env::var("PRINT_HYGIENE") {
            Ok(ref s) if s == "1" => {
                let hygiene_src = tester.print(
                    &program.clone().fold_with(&mut HygieneVisualizer),
                    &tester.comments.clone(),
                );
                println!("----- Hygiene -----\n{}", hygiene_src);
            }
            _ => {}
        }
        let mut program = program
            .fold_with(&mut hygiene::hygiene())
            .fold_with(&mut fixer::fixer(Some(&tester.comments)));
        let src_without_helpers = tester.print(&program, &tester.comments.clone());
        program = program.fold_with(&mut inject_helpers(Mark::fresh(Mark::root())));
        let transformed_src = tester.print(&program, &tester.comments.clone());
        println!(
            "\t>>>>> Orig <<<<<\n{}\n\t>>>>> Code <<<<<\n{}",
            input, src_without_helpers
        );
        let expected = stdout_of(input).unwrap();
        println!("\t>>>>> Expected stdout <<<<<\n{}", expected);
        let actual = stdout_of(&transformed_src).unwrap();
        assert_eq!(expected, actual);
        Ok(())
    })
}
pub fn exec_tr<F, P>(_test_name: &str, syntax: Syntax, tr: F, input: &str)
where
    F: FnOnce(&mut Tester<'_>) -> P,
    P: Fold,
{
    Tester::run(|tester| {
        let tr = make_tr(tr, tester);
        let program = tester.apply_transform(
            tr,
            "input.js",
            syntax,
            Some(true),
            &format!(
                "it('should work', async function () {{
                    {}
                }})",
                input
            ),
        )?;
        match ::std::env::var("PRINT_HYGIENE") {
            Ok(ref s) if s == "1" => {
                let hygiene_src = tester.print(
                    &program.clone().fold_with(&mut HygieneVisualizer),
                    &tester.comments.clone(),
                );
                println!("----- Hygiene -----\n{}", hygiene_src);
            }
            _ => {}
        }
        let mut program = program
            .fold_with(&mut hygiene::hygiene())
            .fold_with(&mut fixer::fixer(Some(&tester.comments)));
        let src_without_helpers = tester.print(&program, &tester.comments.clone());
        program = program.fold_with(&mut inject_helpers(Mark::fresh(Mark::root())));
        let src = tester.print(&program, &tester.comments.clone());
        println!(
            "\t>>>>> {} <<<<<\n{}\n\t>>>>> {} <<<<<\n{}",
            Color::Green.paint("Orig"),
            input,
            Color::Green.paint("Code"),
            src_without_helpers
        );
        exec_with_node_test_runner(&src).map(|_| {})
    })
}
fn calc_hash(s: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(s.as_bytes());
    let sum = hasher.finalize();
    hex::encode(sum)
}
fn exec_with_node_test_runner(src: &str) -> Result<(), ()> {
    let root = CARGO_TARGET_DIR.join("swc-es-exec-testing");
    create_dir_all(&root).expect("failed to create parent directory for temp directory");
    let hash = calc_hash(src);
    let success_cache = root.join(format!("{}.success", hash));
    if env::var("SWC_CACHE_TEST").unwrap_or_default() == "1" {
        println!("Trying cache as `SWC_CACHE_TEST` is `1`");
        if success_cache.exists() {
            println!("Cache: success");
            return Ok(());
        }
    }
    let tmp_dir = tempdir_in(&root).expect("failed to create a temp directory");
    create_dir_all(&tmp_dir).unwrap();
    let path = tmp_dir.path().join(format!("{}.test.js", hash));
    let mut tmp = OpenOptions::new()
        .create(true)
        .truncate(true)
        .write(true)
        .open(&path)
        .expect("failed to create a temp file");
    write!(tmp, "{}", src).expect("failed to write to temp file");
    tmp.flush().unwrap();
    let test_runner_path = find_executable("mocha").expect("failed to find `mocha` from path");
    let mut base_cmd = if cfg!(target_os = "windows") {
        let mut c = Command::new("cmd");
        c.arg("/C").arg(&test_runner_path);
        c
    } else {
        Command::new(&test_runner_path)
    };
    let output = base_cmd
        .arg(format!("{}", path.display()))
        .arg("--color")
        .current_dir(root)
        .output()
        .expect("failed to run mocha");
    println!(">>>>> {} <<<<<", Color::Red.paint("Stdout"));
    println!("{}", String::from_utf8_lossy(&output.stdout));
    println!(">>>>> {} <<<<<", Color::Red.paint("Stderr"));
    println!("{}", String::from_utf8_lossy(&output.stderr));
    if output.status.success() {
        fs::write(&success_cache, "").unwrap();
        return Ok(());
    }
    let dir_name = path.display().to_string();
    ::std::mem::forget(tmp_dir);
    panic!("Execution failed: {dir_name}")
}
fn stdout_of(code: &str) -> Result<String, Error> {
    exec_node_js(
        code,
        JsExecOptions {
            cache: true,
            module: false,
            ..Default::default()
        },
    )
}
#[macro_export]
macro_rules! test_exec {
    (@check) => {
        if ::std::env::var("EXEC").unwrap_or(String::from("")) == "0" {
            return;
        }
    };
    (ignore, $syntax:expr, $tr:expr, $test_name:ident, $input:expr) => {
        #[test]
        #[ignore]
        fn $test_name() {
            $crate::exec_tr(stringify!($test_name), $syntax, $tr, $input)
        }
    };
    ($syntax:expr, $tr:expr, $test_name:ident, $input:expr) => {
        #[test]
        fn $test_name() {
            test_exec!(@check);
            $crate::exec_tr(stringify!($test_name), $syntax, $tr, $input)
        }
    };
}
#[macro_export]
macro_rules! compare_stdout {
    ($syntax:expr, $tr:expr, $test_name:ident, $input:expr) => {
        #[test]
        fn $test_name() {
            $crate::compare_stdout($syntax, $tr, $input)
        }
    };
}
pub struct HygieneTester;
impl Fold for HygieneTester {
    fn fold_ident(&mut self, ident: Ident) -> Ident {
        Ident {
            sym: format!("{}__{}", ident.sym, ident.ctxt.as_u32()).into(),
            ..ident
        }
    }
    fn fold_member_prop(&mut self, p: MemberProp) -> MemberProp {
        match p {
            MemberProp::Computed(..) => p.fold_children_with(self),
            _ => p,
        }
    }
    fn fold_prop_name(&mut self, p: PropName) -> PropName {
        match p {
            PropName::Computed(..) => p.fold_children_with(self),
            _ => p,
        }
    }
}
pub struct HygieneVisualizer;
impl Fold for HygieneVisualizer {
    fn fold_ident(&mut self, ident: Ident) -> Ident {
        Ident {
            sym: format!("{}{:?}", ident.sym, ident.ctxt).into(),
            ..ident
        }
    }
}
pub fn parse_options<T>(dir: &Path) -> T
where
    T: DeserializeOwned,
{
    type Map = serde_json::Map<String, serde_json::Value>;
    let mut value = Map::default();
    fn check(dir: &Path) -> Option<Map> {
        let file = dir.join("options.json");
        if let Ok(v) = read_to_string(&file) {
            eprintln!("Using options.json at {}", file.display());
            eprintln!("----- {} -----\n{}", Color::Green.paint("Options"), v);
            return Some(serde_json::from_str(&v).unwrap_or_else(|err| {
                panic!("failed to deserialize options.json: {}\n{}", err, v)
            }));
        }
        None
    }
    let mut c = Some(dir);
    while let Some(dir) = c {
        if let Some(new) = check(dir) {
            for (k, v) in new {
                if !value.contains_key(&k) {
                    value.insert(k, v);
                }
            }
        }
        c = dir.parent();
    }
    serde_json::from_value(serde_json::Value::Object(value.clone()))
        .unwrap_or_else(|err| panic!("failed to deserialize options.json: {}\n{:?}", err, value))
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FixtureTestConfig {
    pub sourcemap: bool,
    pub allow_error: bool,
    pub module: Option<bool>,
}
pub fn test_fixture<P>(
    syntax: Syntax,
    tr: &dyn Fn(&mut Tester) -> P,
    input: &Path,
    output: &Path,
    config: FixtureTestConfig,
) where
    P: Fold,
{
    let input = fs::read_to_string(input).unwrap();
    test_fixture_inner(
        syntax,
        Box::new(|tester| Box::new(tr(tester))),
        &input,
        output,
        config,
    );
}
fn test_fixture_inner<'a>(
    syntax: Syntax,
    tr: Box<dyn 'a + FnOnce(&mut Tester) -> Box<dyn 'a + Fold>>,
    input: &str,
    output: &Path,
    config: FixtureTestConfig,
) {
    let _logger = testing::init();
    let expected = read_to_string(output);
    let _is_really_expected = expected.is_ok();
    let expected = expected.unwrap_or_default();
    let expected_src = Tester::run(|tester| {
        let expected_program =
            tester.apply_transform(noop(), "expected.js", syntax, config.module, &expected)?;
        let expected_src = tester.print(&expected_program, &tester.comments.clone());
        println!(
            "----- {} -----\n{}",
            Color::Green.paint("Expected"),
            expected_src
        );
        Ok(expected_src)
    });
    let mut src_map = if config.sourcemap {
        Some(Vec::new())
    } else {
        None
    };
    let mut sourcemap = None;
    let (actual_src, stderr) = Tester::run_captured(|tester| {
        eprintln!("----- {} -----\n{}", Color::Green.paint("Input"), input);
        let tr = tr(tester);
        eprintln!("----- {} -----", Color::Green.paint("Actual"));
        let actual = tester.apply_transform(tr, "input.js", syntax, config.module, input)?;
        eprintln!("----- {} -----", Color::Green.paint("Comments"));
        eprintln!("{:?}", tester.comments);
        match ::std::env::var("PRINT_HYGIENE") {
            Ok(ref s) if s == "1" => {
                let hygiene_src = tester.print(
                    &actual.clone().fold_with(&mut HygieneVisualizer),
                    &tester.comments.clone(),
                );
                println!(
                    "----- {} -----\n{}",
                    Color::Green.paint("Hygiene"),
                    hygiene_src
                );
            }
            _ => {}
        }
        let actual = actual
            .fold_with(&mut crate::hygiene::hygiene())
            .fold_with(&mut crate::fixer::fixer(Some(&tester.comments)));
        let actual_src = {
            let module = &actual;
            let comments: &Rc<SingleThreadedComments> = &tester.comments.clone();
            if let Some(src_map) = &mut src_map {
                sourcemap = Some(tester.cm.build_source_map_with_config(
                    src_map,
                    None,
                    SourceMapConfigImpl,
                ));
            }
            to_code_default(tester.cm.clone(), Some(comments), module)
        };
        Ok(actual_src)
    });
    if config.allow_error {
        stderr
            .compare_to_file(output.with_extension("stderr"))
            .unwrap();
    } else if !stderr.is_empty() {
        panic!("stderr: {}", stderr);
    }
    if let Some(actual_src) = actual_src {
        eprintln!("{}", actual_src);
        if let Some(sourcemap) = &sourcemap {
            eprintln!("----- ----- ----- ----- -----");
            eprintln!("SourceMap: {}", visualizer_url(&actual_src, sourcemap));
        }
        if actual_src != expected_src {
            NormalizedOutput::from(actual_src)
                .compare_to_file(output)
                .unwrap();
        }
    }
    if let Some(sourcemap) = sourcemap {
        let map = {
            let mut buf = Vec::new();
            sourcemap.to_writer(&mut buf).unwrap();
            String::from_utf8(buf).unwrap()
        };
        NormalizedOutput::from(map)
            .compare_to_file(output.with_extension("map"))
            .unwrap();
    }
}
fn visualizer_url(code: &str, map: &sourcemap::SourceMap) -> String {
    let map = {
        let mut buf = Vec::new();
        map.to_writer(&mut buf).unwrap();
        String::from_utf8(buf).unwrap()
    };
    let code_len = format!("{}\0", code.len());
    let map_len = format!("{}\0", map.len());
    let hash = BASE64_STANDARD.encode(format!("{}{}{}{}", code_len, code, map_len, map));
    format!("https://evanw.github.io/source-map-visualization/#{}", hash)
}
struct SourceMapConfigImpl;
impl SourceMapGenConfig for SourceMapConfigImpl {
    fn file_name_to_source(&self, f: &swc_common::FileName) -> String {
        f.to_string()
    }
    fn inline_sources_content(&self, _: &swc_common::FileName) -> bool {
        true
    }
}