8000 feat(dbg-swc): Add command to compare minifier (#4653) · swc-project/swc@af1d195 · GitHub
[go: up one dir, main page]

Skip to content

Commit af1d195

Browse files
authored
feat(dbg-swc): Add command to compare minifier (#4653)
1 parent 484dce6 commit af1d195

File tree

9 files changed

+426
-20
lines changed

9 files changed

+426
-20
lines changed

crates/dbg-swc/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.output.js

crates/dbg-swc/Cargo.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ name = "dbg-swc"
77
repository = "https://github.com/kdy1/dbg-swc.git"
88
version = "0.12.1"
99

10+
[[bin]]
11+
bench = false
12+
name = "dbg-swc"
13+
1014
[dependencies]
1115
anyhow = "1.0.57"
12-
clap = {version = "3", features = ["derive"]}
16+
clap = { version = "3", features = ["derive"] }
1317
rayon = "1.5.2"
1418
swc_atoms = { version = "0.2.11", path = "../swc_atoms" }
15-
swc_common = { version = "0.18.0", features = ["concurrent"], path = "../swc_common" }
19+
swc_common = { version = "0.18.0", features = [
20+
"concurrent",
21+
], path = "../swc_common" }
1622
swc_ecma_ast = { version = "0.78.0", path = "../swc_ecma_ast" }
1723
swc_ecma_codegen = { version = "0.107.0", path = "../swc_ecma_codegen" }
1824
swc_ecma_minifier = { version = "0.113.1", path = "../swc_ecma_minifier" }
@@ -22,5 +28,5 @@ swc_ecma_visit = { version = "0.64.0", path = "../swc_ecma_visit" }
2228
swc_error_reporters = { version = "0.2.0", path = "../swc_error_reporters" }
2329
swc_timer = { version = "0.6.0", path = "../swc_timer" }
2430
tracing = "0.1.34"
25-
tracing-subscriber = {version = "0.3.11", features = ["fmt"]}
31+
tracing-subscriber = { version = "0.3.11", features = ["fmt", "env-filter"] }
2632
url = "2"

crates/dbg-swc/src/main.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![feature(box_syntax)]
22

3-
use std::sync::Arc;
3+
use std::{env, str::FromStr, sync::Arc};
44

55
use anyhow::Result;
66
use clap::{StructOpt, Subcommand};
@@ -9,6 +9,7 @@ use swc_common::{
99
Globals, SourceMap, GLOBALS,
1010
};
1111
use swc_error_reporters::handler::{try_with_handler, HandlerOpts};
12+
use tracing_subscriber::EnvFilter;
1213

1314
use self::{bundle::BundleCommand, minify::MinifyCommand, test::TestCommand};
1415

@@ -34,6 +35,20 @@ enum Cmd {
3435
}
3536

3637
fn main() -> Result<()> {
38+
let log_env =
39+
env::var("RUST_LOG").unwrap_or_else(|_| "info,swc_ecma_minifier=warn,swc_timer=off".into());
40+
41+
let logger = tracing_subscriber::FmtSubscriber::builder()
42+
.without_time()
43+
.with_target(false)
44+
.with_ansi(true)
45+
.with_env_filter(EnvFilter::from_str(&log_env).unwrap())
46+
.with_test_writer()
47+
.pretty()
48+
.finish();
49+
50+
tracing::subscriber::set_global_default(logger)?;
51+
3752
let args = AppArgs::parse();
3853

3954
let cm = Arc::new(SourceMap::default());
@@ -48,7 +63,7 @@ fn main() -> Result<()> {
4863
GLOBALS.set(&Globals::default(), || {
4964
HANDLER.set(handler, || match args.cmd {
5065
Cmd::Bundle(_) => todo!(),
51-
Cmd::Minify(_) => todo!(),
66+
Cmd::Minify(cmd) => cmd.run(cm),
5267
Cmd::Test(cmd) => cmd.run(cm),
5368
})
5469
})

crates/dbg-swc/src/minify.rs

Lines changed: 0 additions & 5 deletions
This file was deleted.

crates/dbg-swc/src/minify/compare.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use std::{
2+
path::{Path, PathBuf},
3+
process::{Command, Stdio},
4+
sync::Arc,
5+
};
6+
7+
use anyhow::{bail, Context, Result};
8+
use clap::Args;
9+
use swc_common::SourceMap;
10+
use swc_ecma_minifier::option::MinifyOptions;
11+
use swc_ecma_transforms_base::fixer::fixer;
12+
use swc_ecma_visit::VisitMutWith;
13+
14+
use super::get_esbuild_output;
15+
use crate::util::{parse_js, print_js};
16+
17+
/// Opens vscode for diffing output of swc minifier and terser/esbuild
18+
#[derive(Debug, Args)]
19+
pub struct CompareCommand {
20+
pub path: PathBuf,
21+
}
22+
23+
impl CompareCommand {
24+
pub fn run(self, cm: Arc<SourceMap>) -> Result<()> {
25+
let fm = cm.load_file(&self.path).context("failed to load file")?;
26+
let i = parse_js(fm)?;
27+
let code_mangled = {
28+
let mut minified_mangled = {
29+
let m = i.module.clone();
30+
swc_ecma_minifier::optimize(
31+
m,
32+
cm.clone(),
33+
None,
34+
None,
35+
&MinifyOptions {
36+
compress: Some(Default::default()),
37+
mangle: Some(Default::default()),
38+
..Default::default()
39+
},
40+
&swc_ecma_minifier::option::ExtraOptions {
41+
unresolved_mark: i.unresolved_mark,
42+
top_level_mark: i.top_level_mark,
43+
},
44+
)
45+
};
46+
47+
minified_mangled.visit_mut_with(&mut fixer(None));
48+
49+
print_js(cm, &minified_mangled, true).context("failed to convert ast to code")?
50+
};
51+
52+
let esbuild_mangled = get_esbuild_output(&self.path, true)?;
53+
54+
std::fs::write("swc.output.js", code_mangled.as_bytes())
55+
.context("failed to write swc.output.js")?;
56+
57+
make_pretty("swc.output.js".as_ref())?;
58+
59+
std::fs::write("esbuild.output.js", esbuild_mangled.as_bytes())
60+
.context("failed to write swc.output.js")?;
61+
62+
make_pretty("esbuild.output.js".as_ref())?;
63+
64+
{
65+
let mut c = Command::new("code");
66+
c.arg("--diff");
67+
c.arg("swc.output.js");
68+
c.arg("esbuild.output.js");
69+
c.output().context("failed to run vscode")?;
70+
}
71+
72+
Ok(())
73+
}
74+
}
75+
76+
fn make_pretty(f: &Pa F438 th) -> Result<()> {
77+
let mut c = Command::new("npx");
78+
c.stderr(Stdio::inherit());
79+
c.arg("js-beautify").arg("--replace").arg(f);
80+
81+
let output = c.output().context("failed to run prettier")?;
82+
83+
if !output.status.success() {
84+
bail!("prettier failed");
85+
}
86+
87+
Ok(())
88+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
use std::{
2+
path::{Path, PathBuf},
3+
sync::Arc,
4+
};
5+
6+
use anyhow::{bail, Context, Result};
7+
use clap::Args;
8+
use rayon::prelude::*;
9+
use swc_common::{SourceFile, SourceMap, GLOBALS};
10+
use swc_ecma_minifier::option::MinifyOptions;
11+
use swc_ecma_transforms_base::fixer::fixer;
12+
use swc_ecma_visit::VisitMutWith;
13+
use tracing::info;
14+
15+
use crate::{
16+
minify::{get_esbuild_output, get_terser_output},
17+
util::{all_js_files, parse_js, print_js, wrap_task},
18+
};
19+
20+
/// Ensure that we are performing better than other minification tools.
21+
#[derive(Debug, Args)]
22+
pub struct EnsureSize {
23+
#[clap(long)]
24+
pub no_terser: bool,
25+
26+
#[clap(long)]
27+
pub no_esbuild: bool,
28+
29+
/// This can be a directyory or a file.
30+
///
31+
/// If this is a directory, all `.js` files in it will be verified.
32+
pub path: PathBuf,
33+
}
34+
35+
impl EnsureSize {
36+
pub fn run(self, cm: Arc<SourceMap>) -> Result<()> {
37+
let all_files = all_js_files(&self.path)?;
38+
39+
dbg!(&all_files);
40+
41+
let results = GLOBALS.with(|globals| {
42+
all_files
43+
.par_iter()
44+
.map(|js_file| GLOBALS.set(globals, || self.check_file(cm.clone(), js_file)))
45+
.filter_map(|v| v.transpose())
46+
.collect::<Result<Vec<_>>>()
47+
})?;
48+
49+
if results.is_empty() {
50+
return Ok(());
51+
}
52+
for report in &results {
53+
dbg!(&report);
54+
}
55+
56+
bail!("found some issues")
57+
}
58+
59+
fn check_file(&self, cm: Arc<SourceMap>, js_file: &Path) -> Result<Option<SizeIssue>> {
60+
wrap_task(|| {
61+
info!("Checking {}", js_file.display());
62+
63+
let fm = cm.load_file(js_file).context("failed to load file")?;
64+
let i = parse_js(fm.clone())?;
65+
66+
let code_mangled = {
67+
let mut minified_mangled = {
68+
let m = i.module.clone();
69+
swc_ecma_minifier::optimize(
70+
m,
71+
cm.clone(),
72+
None,
73+
None,
74+
&MinifyOptions {
75+
compress: Some(Default::default()),
76+
mangle: Some(Default::default()),
77+
..Default::default()
78+
},
79+
&swc_ecma_minifier::option::ExtraOptions {
80+
unresolved_mark: i.unresolved_mark,
81+
top_level_mark: i.top_level_mark,
82+
},
83+
)
84+
};
85+
86+
minified_mangled.visit_mut_with(&mut fixer(None));
87+
88+
print_js(cm.clone(), &minified_mangled, true)
89+
.context("failed to convert ast to code")?
90+
};
91+
92+
let swc_no_mangle = {
93+
let mut minified_mangled = {
94+
let m = i.module.clone();
95+
96+
swc_ecma_minifier::optimize(
97+
m,
98+
cm.clone(),
99+
None,
100+
None,
101+
&MinifyOptions {
102+
compress: Some(Default::default()),
103+
mangle: None,
104+
..Default::default()
105+
},
106+
&swc_ecma_minifier::option::ExtraOptions {
107+
unresolved_mark: i.unresolved_mark,
108+
top_level_mark: i.top_level_mark,
109+
},
110+
)
111+
};
112+
113+
minified_mangled.visit_mut_with(&mut fixer(None));
114+
115+
print_js(cm, &minified_mangled, true).context("failed to convert ast to code")?
116+
};
117+
118+
// eprintln!("The output size of swc minifier: {}", code_mangled.len());
119+
120+
let mut size_issue = SizeIssue {
121+
fm,
122+
swc: MinifierOutput {
123+
mangled_size: code_mangled.len(),
124+
no_mangle_size: swc_no_mangle.len(),
125+
},
126+
terser: Default::default(),
127+
esbuild: Default::default(),
128+
};
129+
130+
if !self.no_terser {
131+
let terser_mangled = get_terser_output(js_file, true, true)?;
132+
let terser_no_mangle = get_terser_output(js_file, true, false)?;
133+
134+
if terser_mangled.len() < code_mangled.len() {
135+
// eprintln!("The output size of terser: {}", terser_mangled.len());
136+
// eprintln!(
137+
// "The output size of terser without mangler: {}",
138+
// terser_no_mangle.len()
139+
// );
140+
141+
size_issue.terser = Some(MinifierOutput {
142+
mangled_size: terser_mangled.len(),
143+
no_mangle_size: terser_no_mangle.len(),
144+
});
145+
}
146+
}
147+
148+
if !self.no_esbuild {
149+
let esbuild_mangled = get_esbuild_output(js_file, true)?;
150+
let esbuild_no_mangle = get_esbuild_output(js_file, false)?;
151+
152+
if esbuild_mangled.len() < code_mangled.len() {
153+
// eprintln!("The output size of esbuild: {}", esbuild_mangled.len());
154+
// eprintln!(
155+
// "The output size of esbuild without mangler: {}",
156+
// esbuild_no_mangle.len()
157+
// );
158+
159+
size_issue.esbuild = Some(MinifierOutput {
160+
mangled_size: esbuild_mangled.len(),
161+
no_mangle_size: esbuild_no_mangle.len(),
162+
});
163+
}
164+
}
165+
166+
if size_issue.terser.is_none() && size_issue.esbuild.is_none() {
167+
return Ok(None);
168+
}
169+
170+
Ok(Some(size_issue))
171+
})
172+
.with_context(|| format!("failed to check file: {}", js_file.display()))
173+
}
174+
}
175+
176+
#[allow(unused)]
177+
#[derive(Debug)]
178+
struct SizeIssue {
179+
fm: Arc<SourceFile>,
180+
181+
swc: MinifierOutput,
182+
183+
terser: Option<MinifierOutput>,
184+
185+
esbuild: Option<MinifierOutput>,
186+
}
187+
188+
#[allow(unused)]
189+
#[derive(Debug)]
190+
struct MinifierOutput {
191+
mangled_size: usize,
192+
no_mangle_size: usize,
193+
}

0 commit comments

Comments
 (0)
0