From b13a079481802d7bc44c9b363f63c74599f11a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 14 Dec 2022 17:41:21 +0900 Subject: [PATCH] refactor(dbg-swc): Cleanup and document reducer (#6647) --- .../dbg-swc/src/{test.rs => es/exec_test.rs} | 9 +- .../src/{minify => es/minifier}/compare.rs | 3 +- .../{minify => es/minifier}/ensure_size.rs | 3 +- crates/dbg-swc/src/es/minifier/mod.rs | 35 +++++ .../minifier}/next/check_size.rs | 0 .../src/{minify => es/minifier}/next/mod.rs | 2 +- .../src/{minify => es/minifier}/reduce.rs | 30 ++++- crates/dbg-swc/src/es/mod.rs | 28 ++++ crates/dbg-swc/src/main.rs | 35 +---- crates/dbg-swc/src/minify/diff_options.rs | 124 ------------------ crates/dbg-swc/src/minify/mod.rs | 39 ------ 11 files changed, 105 insertions(+), 203 deletions(-) rename crates/dbg-swc/src/{test.rs => es/exec_test.rs} (93%) rename crates/dbg-swc/src/{minify => es/minifier}/compare.rs (94%) rename crates/dbg-swc/src/{minify => es/minifier}/ensure_size.rs (98%) create mode 100644 crates/dbg-swc/src/es/minifier/mod.rs rename crates/dbg-swc/src/{minify => es/minifier}/next/check_size.rs (100%) rename crates/dbg-swc/src/{minify => es/minifier}/next/mod.rs (84%) rename crates/dbg-swc/src/{minify => es/minifier}/reduce.rs (71%) create mode 100644 crates/dbg-swc/src/es/mod.rs delete mode 100644 crates/dbg-swc/src/minify/diff_options.rs delete mode 100644 crates/dbg-swc/src/minify/mod.rs diff --git a/crates/dbg-swc/src/test.rs b/crates/dbg-swc/src/es/exec_test.rs similarity index 93% rename from crates/dbg-swc/src/test.rs rename to crates/dbg-swc/src/es/exec_test.rs index edd4537f9bcf..71ff9ec7e048 100644 --- a/crates/dbg-swc/src/test.rs +++ b/crates/dbg-swc/src/es/exec_test.rs @@ -16,13 +16,14 @@ use tracing::info; use crate::{bundle::bundle, util::print_js}; -/// Execute a javascript file after performing some preprocessing. +/// [Experimental] Execute a javascript file after performing some +/// preprocessing. #[derive(Debug, Subcommand)] -pub enum TestCommand { +pub enum ExecForTestingCommand { MinifiedBundle(TestMinifiedBundleCommand), } -impl TestCommand { +impl ExecForTestingCommand { pub fn run(self, cm: Arc) -> Result<()> { let _timer = timer!("test"); @@ -30,7 +31,7 @@ impl TestCommand { let _timer = timer!("process"); match self { - TestCommand::MinifiedBundle(cmd) => cmd.run(cm), + ExecForTestingCommand::MinifiedBundle(cmd) => cmd.run(cm), }? }; diff --git a/crates/dbg-swc/src/minify/compare.rs b/crates/dbg-swc/src/es/minifier/compare.rs similarity index 94% rename from crates/dbg-swc/src/minify/compare.rs rename to crates/dbg-swc/src/es/minifier/compare.rs index 54a25ed50ae4..61a4f1147ccf 100644 --- a/crates/dbg-swc/src/minify/compare.rs +++ b/crates/dbg-swc/src/es/minifier/compare.rs @@ -10,7 +10,8 @@ use crate::util::{ print_js, }; -/// Opens vscode for diffing output of swc minifier and terser/esbuild +/// [Experimental] Opens vscode for diffing output of swc minifier and +/// terser/esbuild #[derive(Debug, Args)] pub struct CompareCommand { pub path: PathBuf, diff --git a/crates/dbg-swc/src/minify/ensure_size.rs b/crates/dbg-swc/src/es/minifier/ensure_size.rs similarity index 98% rename from crates/dbg-swc/src/minify/ensure_size.rs rename to crates/dbg-swc/src/es/minifier/ensure_size.rs index 027d6f554026..8c960e4ec0ab 100644 --- a/crates/dbg-swc/src/minify/ensure_size.rs +++ b/crates/dbg-swc/src/es/minifier/ensure_size.rs @@ -18,7 +18,8 @@ use crate::util::{ print_js, wrap_task, }; -/// Ensure that we are performing better than other minification tools. +/// [Experimental] Ensure that we are performing better than other minification +/// tools. #[derive(Debug, Args)] pub struct EnsureSize { #[clap(long)] diff --git a/crates/dbg-swc/src/es/minifier/mod.rs b/crates/dbg-swc/src/es/minifier/mod.rs new file mode 100644 index 000000000000..a585782d4456 --- /dev/null +++ b/crates/dbg-swc/src/es/minifier/mod.rs @@ -0,0 +1,35 @@ +use std::sync::Arc; + +use anyhow::Result; +use clap::Subcommand; +use swc_common::SourceMap; + +use self::{ + compare::CompareCommand, ensure_size::EnsureSize, next::NextCommand, reduce::ReduceCommand, +}; + +mod compare; +mod ensure_size; +mod next; +mod reduce; + +/// Debug the ECMAScript Minifier. +#[derive(Debug, Subcommand)] +pub enum MinifierCommand { + #[clap(subcommand)] + Next(NextCommand), + Reduce(ReduceCommand), + Compare(CompareCommand), + EnsureSize(EnsureSize), +} + +impl MinifierCommand { + pub fn run(self, cm: Arc) -> Result<()> { + match self { + MinifierCommand::Next(cmd) => cmd.run(cm), + MinifierCommand::Reduce(cmd) => cmd.run(cm), + MinifierCommand::EnsureSize(cmd) => cmd.run(cm), + MinifierCommand::Compare(cmd) => cmd.run(cm), + } + } +} diff --git a/crates/dbg-swc/src/minify/next/check_size.rs b/crates/dbg-swc/src/es/minifier/next/check_size.rs similarity index 100% rename from crates/dbg-swc/src/minify/next/check_size.rs rename to crates/dbg-swc/src/es/minifier/next/check_size.rs diff --git a/crates/dbg-swc/src/minify/next/mod.rs b/crates/dbg-swc/src/es/minifier/next/mod.rs similarity index 84% rename from crates/dbg-swc/src/minify/next/mod.rs rename to crates/dbg-swc/src/es/minifier/next/mod.rs index 3b583050824c..6d5b911f59bc 100644 --- a/crates/dbg-swc/src/minify/next/mod.rs +++ b/crates/dbg-swc/src/es/minifier/next/mod.rs @@ -8,7 +8,7 @@ use self::check_size::CheckSizeCommand; mod check_size; -/// Debug the minifier issue related to next.js application. +/// [Experimental] Debug the minifier issue related to next.js application. #[derive(Debug, Subcommand)] pub enum NextCommand { CheckSize(CheckSizeCommand), diff --git a/crates/dbg-swc/src/minify/reduce.rs b/crates/dbg-swc/src/es/minifier/reduce.rs similarity index 71% rename from crates/dbg-swc/src/minify/reduce.rs rename to crates/dbg-swc/src/es/minifier/reduce.rs index 6e75d3952a48..bda2aa4ea4d3 100644 --- a/crates/dbg-swc/src/minify/reduce.rs +++ b/crates/dbg-swc/src/es/minifier/reduce.rs @@ -18,14 +18,38 @@ use crate::{ CREDUCE_INPUT_ENV_VAR, CREDUCE_MODE_ENV_VAR, }; +/// Reduce input files to minimal reproduction cases +/// +/// This command requires `creduce` and `terser` in PATH. +/// +/// For `creduce`, see https://embed.cs.utah.edu/creduce/ for more information. +/// If you are using homebrew, install it with `brew install creduce`. +/// +/// For `terser`, this command uses `npx terser` to invoke `terser` for +/// comparison. +/// +/// After reducing, the reduced file will be moved to `.swc-reduce` directory. +/// +/// +/// Note: This tool is not perfect, and it may reduce input file way too much, +/// or fail to reduce an input file. #[derive(Debug, Args)] pub struct ReduceCommand { + /// The path to the input file. You can specify a directory if you want to + /// reduce every '.js' file within a directory, in a recursive manner. pub path: PathBuf, + /// In 'size' mode, this command tries to find the minimal input file where + /// the size of the output file of swc minifier is larger than the one from + /// terser. + /// + /// In 'semantics' mode, this command tries to reduce the input file to a + /// minimal reproduction case which triggers the bug. #[clap(long, arg_enum)] pub mode: ReduceMode, - /// If true, the input file will be removed after the reduction. + /// If true, the input file will be removed after the reduction. This can be + /// used for pausing and resuming the process of reducing. #[clap(long)] pub remove: bool, } @@ -115,9 +139,9 @@ fn move_to_data_dir(input_path: &Path) -> Result { let result = hasher.finalize(); let hash_str = format!("{:x}", result); - create_dir_all(format!("data/{}", hash_str)).context("failed to create `.data`")?; + create_dir_all(format!(".swc-reduce/{}", hash_str)).context("failed to create `.data`")?; - let to = PathBuf::from(format!("data/{}/input.js", hash_str)); + let to = PathBuf::from(format!(".swc-reduce/{}/input.js", hash_str)); fs::write(&to, src.as_bytes()).context("failed to write")?; Ok(to) diff --git a/crates/dbg-swc/src/es/mod.rs b/crates/dbg-swc/src/es/mod.rs new file mode 100644 index 000000000000..fb6fc76526f9 --- /dev/null +++ b/crates/dbg-swc/src/es/mod.rs @@ -0,0 +1,28 @@ +use std::sync::Arc; + +use anyhow::Result; +use clap::Subcommand; +use swc_common::SourceMap; + +use self::{exec_test::ExecForTestingCommand, minifier::MinifierCommand}; + +mod exec_test; +mod minifier; + +/// Debug modules related to ECMAScript +#[derive(Debug, Subcommand)] +pub(crate) enum EsCommand { + #[clap(subcommand)] + Minifier(MinifierCommand), + #[clap(subcommand)] + ExecForTesting(ExecForTestingCommand), +} + +impl EsCommand { + pub fn run(self, cm: Arc) -> Result<()> { + match self { + Self::Minifier(cmd) => cmd.run(cm), + Self::ExecForTesting(cmd) => cmd.run(cm), + } + } +} diff --git a/crates/dbg-swc/src/main.rs b/crates/dbg-swc/src/main.rs index d64c448c4bc0..0bd47447c16b 100644 --- a/crates/dbg-swc/src/main.rs +++ b/crates/dbg-swc/src/main.rs @@ -2,6 +2,7 @@ use std::{env, path::PathBuf, str::FromStr, sync::Arc}; use anyhow::{bail, Result}; use clap::{StructOpt, Subcommand}; +use es::EsCommand; use swc_common::{ errors::{ColorConfig, HANDLER}, Globals, SourceMap, GLOBALS, @@ -9,16 +10,11 @@ use swc_common::{ use swc_error_reporters::handler::{try_with_handler, HandlerOpts}; use tracing_subscriber::EnvFilter; -use self::{ - minify::MinifyCommand, - test::TestCommand, - util::{minifier::get_esbuild_output, print_js}, -}; +use self::util::print_js; use crate::util::minifier::{get_minified, get_terser_output}; mod bundle; -mod minify; -mod test; +mod es; mod util; const CREDUCE_INPUT_ENV_VAR: &str = "CREDUCE_INPUT"; @@ -34,9 +30,7 @@ struct AppArgs { #[derive(Debug, Subcommand)] enum Cmd { #[clap(subcommand)] - Minify(MinifyCommand), - #[clap(subcommand)] - Test(TestCommand), + Es(EsCommand), } fn init() -> Result<()> { @@ -89,24 +83,6 @@ fn main() -> Result<()> { return Ok(()); } - // We only care about length, so we can replace it. - // - // We target es5, but esbuild does not support it - let swc_output = swc_output.replace("\\n", "_"); - - let esbuild_output = get_esbuild_output(&input, true)?; - - if swc_output.len() > esbuild_output.len() { - // It's interesting, as our output is larger than esbuild's. - return Ok(()); - } - - println!( - "swc size = {}, esbuild size = {}", - swc_output.len(), - esbuild_output.len() - ); - bail!("We don't care about this file") } else if mode == "SEMANTICS" { let m = get_minified(cm.clone(), &input, true, false)?; @@ -142,8 +118,7 @@ fn main() -> Result<()> { |handler| { GLOBALS.set(&Globals::default(), || { HANDLER.set(handler, || match args.cmd { - Cmd::Minify(cmd) => cmd.run(cm), - Cmd::Test(cmd) => cmd.run(cm), + Cmd::Es(cmd) => cmd.run(cm), }) }) }, diff --git a/crates/dbg-swc/src/minify/diff_options.rs b/crates/dbg-swc/src/minify/diff_options.rs deleted file mode 100644 index b7dce4e87572..000000000000 --- a/crates/dbg-swc/src/minify/diff_options.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::{ - fs, - path::{Path, PathBuf}, - process::Command, - sync::Arc, -}; - -use anyhow::{Context, Result}; -use clap::Args; -use rayon::prelude::*; -use swc_common::{SourceMap, GLOBALS}; -use swc_ecma_minifier::option::{CompressOptions, MinifyOptions}; -use swc_ecma_transforms_base::fixer::fixer; -use swc_ecma_visit::VisitMutWith; -use tracing::info; - -use crate::util::{all_js_files, make_pretty, parse_js, print_js}; - -/// Useful for checking if the minifier is working correctly, even with the new -/// option. -#[derive(Debug, Args)] -pub struct DiffOptionCommand { - pub path: PathBuf, - - #[clap(long)] - pub open: bool, -} - -impl DiffOptionCommand { - pub fn run(self, cm: Arc) -> Result<()> { - let js_files = all_js_files(&self.path)?; - - let inputs = js_files - .into_iter() - .filter(|p| p.file_name() == Some("input.js".as_ref())) - .collect::>(); - - let files = GLOBALS.with(|globals| { - inputs - .into_par_iter() - .map(|f| GLOBALS.set(globals, || self.process_file(cm.clone(), &f))) - .collect::>>() - })?; - - for (orig_path, new_path) in files.into_iter().flatten() { - if self.open { - let mut c = Command::new("code"); - c.arg("--diff").arg("--wait"); - c.arg(&orig_path); - c.arg(&new_path); - c.output().context("failed to run vscode")?; - } - } - - Ok(()) - } - - fn process_file(&self, cm: Arc, f: &Path) -> Result> { - info!("Processing `{}`", f.display()); - - let fm = cm.load_file(f)?; - let m = parse_js(fm)?; - - let orig = { - let m = m.clone(); - let mut m = swc_ecma_minifier::optimize( - m.module.into(), - cm.clone(), - Some(&m.comments), - None, - &MinifyOptions { - compress: Some(Default::default()), - mangle: None, - ..Default::default() - }, - &swc_ecma_minifier::option::ExtraOptions { - unresolved_mark: m.unresolved_mark, - top_level_mark: m.top_level_mark, - }, - ) - .expect_module(); - m.visit_mut_with(&mut fixer(None)); - print_js(cm.clone(), &m, false)? - }; - - let new = { - let mut m = swc_ecma_minifier::optimize( - m.module.into(), - cm.clone(), - Some(&m.comments), - None, - &MinifyOptions { - compress: Some(CompressOptions { - conditionals: true, - ..Default::default() - }), - mangle: None, - ..Default::default() - }, - &swc_ecma_minifier::option::ExtraOptions { - unresolved_mark: m.unresolved_mark, - top_level_mark: m.top_level_mark, - }, - ) - .expect_module(); - m.visit_mut_with(&mut fixer(None)); - print_js(cm, &m, false)? - }; - - if orig == new { - fs::remove_file(f)?; - return Ok(None); - } - - let orig_path = f.with_extension("orig.js"); - fs::write(&orig_path, orig)?; - make_pretty(&orig_path)?; - let new_path = f.with_extension("new.js"); - fs::write(&new_path, new)?; - make_pretty(&new_path)?; - - Ok(Some((orig_path, new_path))) - } -} diff --git a/crates/dbg-swc/src/minify/mod.rs b/crates/dbg-swc/src/minify/mod.rs deleted file mode 100644 index d160131f9129..000000000000 --- a/crates/dbg-swc/src/minify/mod.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::sync::Arc; - -use anyhow::Result; -use clap::Subcommand; -use swc_common::SourceMap; - -use self::{ - compare::CompareCommand, diff_options::DiffOptionCommand, ensure_size::EnsureSize, - next::NextCommand, reduce::ReduceCommand, -}; - -mod compare; -mod diff_options; -mod ensure_size; -mod next; -mod reduce; - -/// Debug swc es minifier -#[derive(Debug, Subcommand)] -pub enum MinifyCommand { - #[clap(subcommand)] - Next(NextCommand), - Reduce(ReduceCommand), - Compare(CompareCommand), - DiffOption(DiffOptionCommand), - EnsureSize(EnsureSize), -} - -impl MinifyCommand { - pub fn run(self, cm: Arc) -> Result<()> { - match self { - MinifyCommand::Next(cmd) => cmd.run(cm), - MinifyCommand::Reduce(cmd) => cmd.run(cm), - MinifyCommand::EnsureSize(cmd) => cmd.run(cm), - MinifyCommand::Compare(cmd) => cmd.run(cm), - MinifyCommand::DiffOption(cmd) => cmd.run(cm), - } - } -}