1use std::{env, path::PathBuf, str::FromStr, sync::Arc};
2
3use anyhow::{bail, Error, Result};
4use clap::{StructOpt, Subcommand};
5use es::EsCommand;
6use swc_common::{
7 errors::{ColorConfig, HANDLER},
8 Globals, SourceMap, GLOBALS,
9};
10use swc_error_reporters::handler::{try_with_handler, HandlerOpts};
11use tracing_subscriber::EnvFilter;
12
13use self::util::print_js;
14use crate::util::minifier::{get_minified, get_terser_output};
15
16mod bundle;
17mod es;
18mod util;
19
20const CREDUCE_INPUT_ENV_VAR: &str = "CREDUCE_INPUT";
21
22const CREDUCE_MODE_ENV_VAR: &str = "CREDUCE_COMPARE";
23
24#[derive(Debug, clap::Parser)]
25struct AppArgs {
26 #[clap(subcommand)]
27 cmd: Cmd,
28}
29
30#[derive(Debug, Subcommand)]
31enum Cmd {
32 #[clap(subcommand)]
33 Es(EsCommand),
34}
35
36fn init() -> Result<()> {
37 let log_env =
38 env::var("RUST_LOG").unwrap_or_else(|_| "info,swc_ecma_minifier=warn,swc_timer=off".into());
39
40 let logger = tracing_subscriber::FmtSubscriber::builder()
41 .without_time()
42 .with_target(false)
43 .with_ansi(true)
44 .with_env_filter(EnvFilter::from_str(&log_env).unwrap())
45 .with_writer(std::io::stderr)
46 .pretty()
47 .finish();
48
49 tracing::subscriber::set_global_default(logger)?;
50
51 Ok(())
52}
53
54fn main() -> Result<(), Error> {
55 init()?;
56
57 let cm = Arc::new(SourceMap::default());
58
59 if let Ok(mode) = env::var(CREDUCE_MODE_ENV_VAR) {
60 return try_with_handler(
61 cm.clone(),
62 HandlerOpts {
63 color: ColorConfig::Always,
64 skip_filename: false,
65 },
66 |handler| {
67 GLOBALS.set(&Globals::default(), || {
68 HANDLER.set(handler, || {
69 let input = PathBuf::from(
71 env::var(CREDUCE_INPUT_ENV_VAR)
72 .expect("creduce is invoked without the name of input file"),
73 );
74
75 if mode == "SIZE" {
76 let m = get_minified(cm.clone(), &input, true, true)?;
77
78 let swc_output = print_js(cm.clone(), &m.module, true)?;
79
80 let terser_output = get_terser_output(&input, true, true)?;
81 if swc_output.trim().len() > terser_output.trim().len() {
82 return Ok(());
84 }
85
86 bail!("We don't care about this file")
87 } else if mode == "SEMANTICS" {
88 let m = get_minified(cm.clone(), &input, true, false)?;
89
90 let swc_output = print_js(cm.clone(), &m.module, true)?;
91
92 let terser_output = get_terser_output(&input, true, false)?;
93
94 if swc_output.trim() == terser_output.trim() {
95 bail!("We don't care about this file")
96 }
97
98 Ok(())
101 } else {
102 unreachable!("Unknown mode `{}`", mode)
103 }
104 })
105 })
106 },
107 )
108 .map_err(|e| e.to_pretty_error());
109 }
110
111 let args = AppArgs::parse();
112
113 try_with_handler(
114 cm.clone(),
115 HandlerOpts {
116 color: ColorConfig::Always,
117 skip_filename: false,
118 },
119 |handler| {
120 GLOBALS.set(&Globals::default(), || {
121 HANDLER.set(handler, || match args.cmd {
122 Cmd::Es(cmd) => cmd.run(cm.clone()),
123 })
124 })
125 },
126 )
127 .map_err(|e| e.to_pretty_error())
128}