mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 09:38:16 +03:00
refactor(es/minifier): Use swc_timer
(#3087)
swc_bundler: - Add `Config.disable_fixer`. - Add `Config.disable_hygiene`. swc_ecma_minifier: - Use `swc_timer`.
This commit is contained in:
parent
f1b81266cf
commit
0e4dce694d
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2935,6 +2935,7 @@ dependencies = [
|
|||||||
"swc_ecma_utils",
|
"swc_ecma_utils",
|
||||||
"swc_ecma_visit",
|
"swc_ecma_visit",
|
||||||
"swc_node_base",
|
"swc_node_base",
|
||||||
|
"swc_timer",
|
||||||
"testing",
|
"testing",
|
||||||
"tracing",
|
"tracing",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
cargo profile instruments --release -t time --bench typescript --features concurrent,tracing/release_max_level_info $@
|
cargo profile instruments --release -t time --bench typescript --features concurrent,tracing/release_max_level_info $@
|
||||||
|
# MINIFY=1 cargo profile instruments --release -t time --bench typescript --features concurrent,tracing/release_max_level_info $@
|
@ -7,36 +7,50 @@ use std::{
|
|||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
env, fs,
|
env, fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
time::Instant,
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use swc_atoms::js_word;
|
use swc_atoms::js_word;
|
||||||
use swc_bundler::{Bundle, Bundler, Load, ModuleData, ModuleRecord, Resolve};
|
use swc_bundler::{Bundle, Bundler, Load, ModuleData, ModuleRecord, Resolve};
|
||||||
use swc_common::{
|
use swc_common::{
|
||||||
errors::{ColorConfig, Handler},
|
errors::{ColorConfig, Handler},
|
||||||
sync::Lrc,
|
sync::Lrc,
|
||||||
FileName, Globals, SourceMap, Span,
|
FileName, Globals, Mark, SourceMap, Span, GLOBALS,
|
||||||
};
|
};
|
||||||
use swc_ecma_ast::*;
|
use swc_ecma_ast::*;
|
||||||
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
use swc_ecma_codegen::{
|
||||||
|
text_writer::{omit_trailing_semi, JsWriter, WriteJs},
|
||||||
|
Emitter,
|
||||||
|
};
|
||||||
use swc_ecma_loader::{
|
use swc_ecma_loader::{
|
||||||
resolvers::{lru::CachingResolver, node::NodeModulesResolver},
|
resolvers::{lru::CachingResolver, node::NodeModulesResolver},
|
||||||
TargetEnv,
|
TargetEnv,
|
||||||
};
|
};
|
||||||
|
use swc_ecma_minifier::option::{
|
||||||
|
CompressOptions, ExtraOptions, MangleOptions, MinifyOptions, TopLevelOptions,
|
||||||
|
};
|
||||||
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax};
|
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax};
|
||||||
|
use swc_ecma_transforms_base::fixer::fixer;
|
||||||
|
use swc_ecma_visit::VisitMutWith;
|
||||||
|
|
||||||
fn print_bundles(cm: Lrc<SourceMap>, modules: Vec<Bundle>) {
|
fn print_bundles(cm: Lrc<SourceMap>, modules: Vec<Bundle>, minify: bool) {
|
||||||
for bundled in modules {
|
for bundled in modules {
|
||||||
let code = {
|
let code = {
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
|
|
||||||
{
|
{
|
||||||
|
let wr = JsWriter::new(cm.clone(), "\n", &mut buf, None);
|
||||||
let mut emitter = Emitter {
|
let mut emitter = Emitter {
|
||||||
cfg: swc_ecma_codegen::Config {
|
cfg: swc_ecma_codegen::Config {
|
||||||
|
minify,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
cm: cm.clone(),
|
cm: cm.clone(),
|
||||||
comments: None,
|
comments: None,
|
||||||
wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
|
wr: if minify {
|
||||||
|
Box::new(omit_trailing_semi(wr)) as Box<dyn WriteJs>
|
||||||
|
} else {
|
||||||
|
Box::new(wr) as Box<dyn WriteJs>
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
emitter.emit_module(&bundled.module).unwrap();
|
emitter.emit_module(&bundled.module).unwrap();
|
||||||
@ -48,11 +62,12 @@ fn print_bundles(cm: Lrc<SourceMap>, modules: Vec<Bundle>) {
|
|||||||
#[cfg(feature = "concurrent")]
|
#[cfg(feature = "concurrent")]
|
||||||
rayon::spawn(move || drop(bundled));
|
rayon::spawn(move || drop(bundled));
|
||||||
|
|
||||||
|
println!("Created output.js ({}kb)", code.len() / 1024);
|
||||||
fs::write("output.js", &code).unwrap();
|
fs::write("output.js", &code).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_test(_entry: &Path, entries: HashMap<String, FileName>, inline: bool) {
|
fn do_test(_entry: &Path, entries: HashMap<String, FileName>, inline: bool, minify: bool) {
|
||||||
testing::run_test2(false, |cm, _| {
|
testing::run_test2(false, |cm, _| {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
@ -69,12 +84,14 @@ fn do_test(_entry: &Path, entries: HashMap<String, FileName>, inline: bool) {
|
|||||||
require: true,
|
require: true,
|
||||||
disable_inliner: !inline,
|
disable_inliner: !inline,
|
||||||
external_modules: Default::default(),
|
external_modules: Default::default(),
|
||||||
|
disable_fixer: minify,
|
||||||
|
disable_hygiene: minify,
|
||||||
module: Default::default(),
|
module: Default::default(),
|
||||||
},
|
},
|
||||||
Box::new(Hook),
|
Box::new(Hook),
|
||||||
);
|
);
|
||||||
|
|
||||||
let modules = bundler
|
let mut modules = bundler
|
||||||
.bundle(entries)
|
.bundle(entries)
|
||||||
.map_err(|err| println!("{:?}", err))?;
|
.map_err(|err| println!("{:?}", err))?;
|
||||||
println!("Bundled as {} modules", modules.len());
|
println!("Bundled as {} modules", modules.len());
|
||||||
@ -86,14 +103,50 @@ fn do_test(_entry: &Path, entries: HashMap<String, FileName>, inline: bool) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let dur = start.elapsed();
|
let dur = start.elapsed();
|
||||||
println!("Bundler.bundle() took {:?}", dur);
|
println!("Bundler.bundle() took {}", to_ms(dur));
|
||||||
}
|
}
|
||||||
|
|
||||||
let error = false;
|
let error = false;
|
||||||
|
if minify {
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
modules = modules
|
||||||
|
.into_iter()
|
||||||
|
.map(|mut b| {
|
||||||
|
GLOBALS.set(&globals, || {
|
||||||
|
b.module = swc_ecma_minifier::optimize(
|
||||||
|
b.module,
|
||||||
|
cm.clone(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
&MinifyOptions {
|
||||||
|
compress: Some(CompressOptions {
|
||||||
|
top_level: Some(TopLevelOptions { functions: true }),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
mangle: Some(MangleOptions {
|
||||||
|
top_level: true,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&ExtraOptions {
|
||||||
|
top_level_mark: Mark::fresh(Mark::root()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
b.module.visit_mut_with(&mut fixer(None));
|
||||||
|
b
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let dur = start.elapsed();
|
||||||
|
println!("Minification took {}", to_ms(dur));
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let cm = cm.clone();
|
let cm = cm.clone();
|
||||||
print_bundles(cm, modules);
|
print_bundles(cm, modules, minify);
|
||||||
}
|
}
|
||||||
|
|
||||||
if error {
|
if error {
|
||||||
@ -105,15 +158,21 @@ fn do_test(_entry: &Path, entries: HashMap<String, FileName>, inline: bool) {
|
|||||||
.expect("failed to process a module");
|
.expect("failed to process a module");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_ms(dur: Duration) -> String {
|
||||||
|
format!("{}ms", dur.as_millis())
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
|
let minify = env::var("MINIFY").unwrap_or_else(|_| "0".to_string()) == "1";
|
||||||
|
|
||||||
let main_file = env::args().nth(1).unwrap();
|
let main_file = env::args().nth(1).unwrap();
|
||||||
let mut entries = HashMap::default();
|
let mut entries = HashMap::default();
|
||||||
entries.insert("main".to_string(), FileName::Real(main_file.clone().into()));
|
entries.insert("main".to_string(), FileName::Real(main_file.clone().into()));
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
do_test(Path::new(&main_file), entries, false);
|
do_test(Path::new(&main_file), entries, false, minify);
|
||||||
let dur = start.elapsed();
|
let dur = start.elapsed();
|
||||||
println!("Took {:?}", dur);
|
println!("Took {}", to_ms(dur));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,15 @@ where
|
|||||||
for mut bundle in bundles {
|
for mut bundle in bundles {
|
||||||
bundle.module = self.optimize(bundle.module);
|
bundle.module = self.optimize(bundle.module);
|
||||||
|
|
||||||
bundle.module = bundle.module.fold_with(&mut hygiene());
|
if !self.config.disable_hygiene {
|
||||||
|
bundle.module = bundle.module.fold_with(&mut hygiene());
|
||||||
|
}
|
||||||
|
|
||||||
bundle.module = self.may_wrap_with_iife(bundle.module);
|
bundle.module = self.may_wrap_with_iife(bundle.module);
|
||||||
|
|
||||||
bundle.module = bundle.module.fold_with(&mut fixer(None));
|
if !self.config.disable_fixer {
|
||||||
|
bundle.module = bundle.module.fold_with(&mut fixer(None));
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Inject swc helpers
|
// Inject swc helpers
|
||||||
|
@ -31,6 +31,11 @@ pub struct Config {
|
|||||||
/// temporary variables, it's really hard to see what's going on.
|
/// temporary variables, it's really hard to see what's going on.
|
||||||
pub disable_inliner: bool,
|
pub disable_inliner: bool,
|
||||||
|
|
||||||
|
/// Useful if you are going to minify the code.
|
||||||
|
pub disable_hygiene: bool,
|
||||||
|
|
||||||
|
pub disable_fixer: bool,
|
||||||
|
|
||||||
/// List of modules which should be preserved.
|
/// List of modules which should be preserved.
|
||||||
pub external_modules: Vec<JsWord>,
|
pub external_modules: Vec<JsWord>,
|
||||||
|
|
||||||
|
@ -132,6 +132,8 @@ impl TestBuilder {
|
|||||||
Config {
|
Config {
|
||||||
require: true,
|
require: true,
|
||||||
disable_inliner: true,
|
disable_inliner: true,
|
||||||
|
disable_hygiene: false,
|
||||||
|
disable_fixer: false,
|
||||||
external_modules: vec![],
|
external_modules: vec![],
|
||||||
module: Default::default(),
|
module: Default::default(),
|
||||||
},
|
},
|
||||||
|
@ -35,6 +35,7 @@ fn do_test(entry: &Path, entries: HashMap<String, FileName>, inline: bool) {
|
|||||||
disable_inliner: !inline,
|
disable_inliner: !inline,
|
||||||
external_modules: NODE_BUILTINS.to_vec().into_iter().map(From::from).collect(),
|
external_modules: NODE_BUILTINS.to_vec().into_iter().map(From::from).collect(),
|
||||||
module: Default::default(),
|
module: Default::default(),
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
Box::new(Hook),
|
Box::new(Hook),
|
||||||
);
|
);
|
||||||
|
@ -37,6 +37,7 @@ swc_ecma_transforms = {version = "0.103.0", path = "../swc_ecma_transforms/", fe
|
|||||||
swc_ecma_transforms_base = {version = "0.49.0", path = "../swc_ecma_transforms_base"}
|
swc_ecma_transforms_base = {version = "0.49.0", path = "../swc_ecma_transforms_base"}
|
||||||
swc_ecma_utils = {version = "0.56.0", path = "../swc_ecma_utils"}
|
swc_ecma_utils = {version = "0.56.0", path = "../swc_ecma_utils"}
|
||||||
swc_ecma_visit = {version = "0.46.0", path = "../swc_ecma_visit"}
|
swc_ecma_visit = {version = "0.46.0", path = "../swc_ecma_visit"}
|
||||||
|
swc_timer = {version = "0.2.0", path = "../swc_timer"}
|
||||||
tracing = "0.1.28"
|
tracing = "0.1.28"
|
||||||
unicode-xid = "0.2.2"
|
unicode-xid = "0.2.2"
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ## `debug`
|
//! ## `debug`
|
||||||
//!
|
//!
|
||||||
//! If you enable this cargo feature and set the environemnt variable named
|
//! If you enable this cargo feature and set the environment variable named
|
||||||
//! `SWC_RUN` to `1`, the minifier will validate the code using node before each
|
//! `SWC_RUN` to `1`, the minifier will validate the code using node before each
|
||||||
//! step.
|
//! step.
|
||||||
//!
|
//!
|
||||||
@ -23,14 +23,13 @@ use crate::{
|
|||||||
mangle_names::name_mangler, mangle_props::mangle_properties,
|
mangle_names::name_mangler, mangle_props::mangle_properties,
|
||||||
precompress::precompress_optimizer,
|
precompress::precompress_optimizer,
|
||||||
},
|
},
|
||||||
util::now,
|
|
||||||
};
|
};
|
||||||
use mode::Minification;
|
use mode::Minification;
|
||||||
use pass::postcompress::postcompress_optimizer;
|
use pass::postcompress::postcompress_optimizer;
|
||||||
use std::time::Instant;
|
|
||||||
use swc_common::{comments::Comments, sync::Lrc, SourceMap, GLOBALS};
|
use swc_common::{comments::Comments, sync::Lrc, SourceMap, GLOBALS};
|
||||||
use swc_ecma_ast::Module;
|
use swc_ecma_ast::Module;
|
||||||
use swc_ecma_visit::{FoldWith, VisitMutWith};
|
use swc_ecma_visit::{FoldWith, VisitMutWith};
|
||||||
|
use swc_timer::timer;
|
||||||
use timing::Timings;
|
use timing::Timings;
|
||||||
|
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
@ -57,14 +56,16 @@ pub fn optimize(
|
|||||||
options: &MinifyOptions,
|
options: &MinifyOptions,
|
||||||
extra: &ExtraOptions,
|
extra: &ExtraOptions,
|
||||||
) -> Module {
|
) -> Module {
|
||||||
|
let _timer = timer!("minify");
|
||||||
|
|
||||||
let marks = Marks::new();
|
let marks = Marks::new();
|
||||||
|
|
||||||
let start = now();
|
|
||||||
if let Some(defs) = options.compress.as_ref().map(|c| &c.global_defs) {
|
if let Some(defs) = options.compress.as_ref().map(|c| &c.global_defs) {
|
||||||
|
let _timer = timer!("inline global defs");
|
||||||
// Apply global defs.
|
// Apply global defs.
|
||||||
//
|
//
|
||||||
// As terser treats `CONFIG['VALUE']` and `CONFIG.VALUE` differently, we don't
|
// As terser treats `CONFIG['VALUE']` and `CONFIG.VALUE` differently, we don't
|
||||||
// have to see if optimized code matches global definition and wecan run
|
// have to see if optimized code matches global definition and we can run
|
||||||
// this at startup.
|
// this at startup.
|
||||||
|
|
||||||
if !defs.is_empty() {
|
if !defs.is_empty() {
|
||||||
@ -72,16 +73,11 @@ pub fn optimize(
|
|||||||
m.visit_mut_with(&mut global_defs::globals_defs(defs, extra.top_level_mark));
|
m.visit_mut_with(&mut global_defs::globals_defs(defs, extra.top_level_mark));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(start) = start {
|
|
||||||
tracing::info!("global_defs took {:?}", Instant::now() - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(options) = &options.compress {
|
if let Some(options) = &options.compress {
|
||||||
let start = now();
|
let _timer = timer!("precompress");
|
||||||
|
|
||||||
m.visit_mut_with(&mut precompress_optimizer(options, marks));
|
m.visit_mut_with(&mut precompress_optimizer(options, marks));
|
||||||
if let Some(start) = start {
|
|
||||||
tracing::info!("precompress took {:?}", Instant::now() - start);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.visit_mut_with(&mut info_marker(comments, marks, extra.top_level_mark));
|
m.visit_mut_with(&mut info_marker(comments, marks, extra.top_level_mark));
|
||||||
@ -115,19 +111,19 @@ pub fn optimize(
|
|||||||
t.section("compress");
|
t.section("compress");
|
||||||
}
|
}
|
||||||
if let Some(options) = &options.compress {
|
if let Some(options) = &options.compress {
|
||||||
let start = now();
|
{
|
||||||
m = GLOBALS
|
let _timer = timer!("compress ast");
|
||||||
.with(|globals| m.fold_with(&mut compressor(globals, marks, &options, &Minification)));
|
|
||||||
if let Some(start) = start {
|
m = GLOBALS.with(|globals| {
|
||||||
tracing::info!("compressor took {:?}", Instant::now() - start);
|
m.fold_with(&mut compressor(globals, marks, &options, &Minification))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Again, we don't need to validate ast
|
// Again, we don't need to validate ast
|
||||||
|
|
||||||
let start = now();
|
let _timer = timer!("postcompress");
|
||||||
|
|
||||||
m.visit_mut_with(&mut postcompress_optimizer(options));
|
m.visit_mut_with(&mut postcompress_optimizer(options));
|
||||||
if let Some(start) = start {
|
|
||||||
tracing::info!("postcompressor took {:?}", Instant::now() - start);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut _t) = timings {
|
if let Some(ref mut _t) = timings {
|
||||||
@ -142,6 +138,7 @@ pub fn optimize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mangle) = &options.mangle {
|
if let Some(mangle) = &options.mangle {
|
||||||
|
let _timer = timer!("mangle names");
|
||||||
// TODO: base54.reset();
|
// TODO: base54.reset();
|
||||||
|
|
||||||
let char_freq_info = compute_char_freq(&m);
|
let char_freq_info = compute_char_freq(&m);
|
||||||
|
@ -76,6 +76,7 @@ fn pass(input_dir: PathBuf) {
|
|||||||
disable_inliner: true,
|
disable_inliner: true,
|
||||||
module: Default::default(),
|
module: Default::default(),
|
||||||
external_modules: NODE_BUILTINS.to_vec().into_iter().map(From::from).collect(),
|
external_modules: NODE_BUILTINS.to_vec().into_iter().map(From::from).collect(),
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
Box::new(Hook),
|
Box::new(Hook),
|
||||||
);
|
);
|
||||||
|
@ -35,6 +35,10 @@ impl Drop for Timer {
|
|||||||
|
|
||||||
/// Creates a timer. For input arguments, see [tracing::span].
|
/// Creates a timer. For input arguments, see [tracing::span].
|
||||||
///
|
///
|
||||||
|
/// # Convention
|
||||||
|
///
|
||||||
|
/// The string passed to `timer!` should start with a verb.
|
||||||
|
///
|
||||||
/// # Example usage
|
/// # Example usage
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -56,6 +60,7 @@ macro_rules! timer {
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
let span = $crate::tracing::span!($crate::tracing::Level::INFO, $($args)*).entered();
|
let span = $crate::tracing::span!($crate::tracing::Level::INFO, $($args)*).entered();
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
$crate::Timer::new(span)
|
$crate::Timer::new(span)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user