dbg_swc/
main.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::{env, path::PathBuf, str::FromStr, sync::Arc};

use anyhow::{bail, Error, Result};
use clap::{StructOpt, Subcommand};
use es::EsCommand;
use swc_common::{
    errors::{ColorConfig, HANDLER},
    Globals, SourceMap, GLOBALS,
};
use swc_error_reporters::handler::{try_with_handler, HandlerOpts};
use tracing_subscriber::EnvFilter;

use self::util::print_js;
use crate::util::minifier::{get_minified, get_terser_output};

mod bundle;
mod es;
mod util;

const CREDUCE_INPUT_ENV_VAR: &str = "CREDUCE_INPUT";

const CREDUCE_MODE_ENV_VAR: &str = "CREDUCE_COMPARE";

#[derive(Debug, clap::Parser)]
struct AppArgs {
    #[clap(subcommand)]
    cmd: Cmd,
}

#[derive(Debug, Subcommand)]
enum Cmd {
    #[clap(subcommand)]
    Es(EsCommand),
}

fn init() -> Result<()> {
    let log_env =
        env::var("RUST_LOG").unwrap_or_else(|_| "info,swc_ecma_minifier=warn,swc_timer=off".into());

    let logger = tracing_subscriber::FmtSubscriber::builder()
        .without_time()
        .with_target(false)
        .with_ansi(true)
        .with_env_filter(EnvFilter::from_str(&log_env).unwrap())
        .with_writer(std::io::stderr)
        .pretty()
        .finish();

    tracing::subscriber::set_global_default(logger)?;

    Ok(())
}

fn main() -> Result<(), Error> {
    init()?;

    let cm = Arc::new(SourceMap::default());

    if let Ok(mode) = env::var(CREDUCE_MODE_ENV_VAR) {
        return try_with_handler(
            cm.clone(),
            HandlerOpts {
                color: ColorConfig::Always,
                skip_filename: false,
            },
            |handler| {
                GLOBALS.set(&Globals::default(), || {
                    HANDLER.set(handler, || {
                        //
                        let input = PathBuf::from(
                            env::var(CREDUCE_INPUT_ENV_VAR)
                                .expect("creduce is invoked without the name of input file"),
                        );

                        if mode == "SIZE" {
                            let m = get_minified(cm.clone(), &input, true, true)?;

                            let swc_output = print_js(cm.clone(), &m.module, true)?;

                            let terser_output = get_terser_output(&input, true, true)?;
                            if swc_output.trim().len() > terser_output.trim().len() {
                                // It's interesting, as our output is larger than terser's.
                                return Ok(());
                            }

                            bail!("We don't care about this file")
                        } else if mode == "SEMANTICS" {
                            let m = get_minified(cm.clone(), &input, true, false)?;

                            let swc_output = print_js(cm.clone(), &m.module, true)?;

                            let terser_output = get_terser_output(&input, true, false)?;

                            if swc_output.trim() == terser_output.trim() {
                                bail!("We don't care about this file")
                            }

                            // It's interesting, as our output may have a bug

                            Ok(())
                        } else {
                            unreachable!("Unknown mode `{}`", mode)
                        }
                    })
                })
            },
        )
        .map_err(|e| e.to_pretty_error());
    }

    let args = AppArgs::parse();

    try_with_handler(
        cm.clone(),
        HandlerOpts {
            color: ColorConfig::Always,
            skip_filename: false,
        },
        |handler| {
            GLOBALS.set(&Globals::default(), || {
                HANDLER.set(handler, || match args.cmd {
                    Cmd::Es(cmd) => cmd.run(cm.clone()),
                })
            })
        },
    )
    .map_err(|e| e.to_pretty_error())
}