Skip to content

Commit

Permalink
perf: parallelize swc js minify (#4213)
Browse files Browse the repository at this point in the history
perf: parallelize minify
  • Loading branch information
h-a-n-a authored Sep 19, 2023
1 parent fbf9217 commit d729541
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 71 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/rspack_plugin_swc_js_minimizer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rayon = { workspace = true }
rspack_core = { path = "../rspack_core" }
rspack_error = { path = "../rspack_error" }
rspack_plugin_javascript = { path = "../rspack_plugin_javascript" }
Expand Down
128 changes: 63 additions & 65 deletions crates/rspack_plugin_swc_js_minimizer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
mod minify;

use std::{
collections::HashMap,
hash::Hash,
sync::{mpsc, Mutex},
};
use std::{collections::HashMap, hash::Hash, sync::Mutex};

use async_recursion::async_recursion;
use async_trait::async_trait;
use minify::{match_object, minify};
use rayon::prelude::*;
use rspack_core::{
rspack_sources::{
MapOptions, RawSource, SourceExt, SourceMap, SourceMapSource, SourceMapSourceOptions,
},
AssetInfo, CompilationAsset, JsChunkHashArgs, Plugin, PluginContext, PluginJsChunkHashHookOutput,
PluginProcessAssetsOutput, ProcessAssetsArgs,
};
use rspack_error::{internal_error, Diagnostic};
use rspack_error::{internal_error, Result};
use rspack_regex::RspackRegex;
use rspack_util::try_any;
use rspack_util::try_any_sync;
use swc_config::config_types::BoolOrDataConfig;
use swc_ecma_minifier::option::{
terser::{TerserCompressorOptions, TerserEcmaVersion},
Expand Down Expand Up @@ -47,8 +43,7 @@ pub enum SwcJsMinimizerRule {
}

impl SwcJsMinimizerRule {
#[async_recursion]
pub async fn try_match(&self, data: &str) -> rspack_error::Result<bool> {
pub fn try_match(&self, data: &str) -> rspack_error::Result<bool> {
match self {
Self::String(s) => Ok(data.starts_with(s)),
Self::Regexp(r) => Ok(r.test(data)),
Expand All @@ -64,12 +59,11 @@ pub enum SwcJsMinimizerRules {
}

impl SwcJsMinimizerRules {
#[async_recursion]
pub async fn try_match(&self, data: &str) -> rspack_error::Result<bool> {
pub fn try_match(&self, data: &str) -> rspack_error::Result<bool> {
match self {
Self::String(s) => Ok(data.starts_with(s)),
Self::Regexp(r) => Ok(r.test(data)),
Self::Array(l) => try_any(l, |i| async { i.try_match(data).await }).await,
Self::Array(l) => try_any_sync(l, |i| i.try_match(data)),
}
}
}
Expand Down Expand Up @@ -105,7 +99,6 @@ impl Plugin for SwcJsMinimizerRspackPlugin {
.as_ref()
.is_some_and(|library| library.library_type == "module");

let (tx, rx) = mpsc::channel::<Vec<Diagnostic>>();
// collect all extracted comments info
let all_extracted_comments = Mutex::new(HashMap::new());
let extract_comments_option = &minify_options.extract_comments.clone();
Expand Down Expand Up @@ -136,68 +129,73 @@ impl Plugin for SwcJsMinimizerRspackPlugin {
..Default::default()
};

for (filename, original) in compilation.assets_mut() {
let result = compilation
.assets_mut()
.par_iter_mut()
.filter(|(filename, original)| {
if !(filename.ends_with(".js") || filename.ends_with(".cjs") || filename.ends_with(".mjs")) {
continue;
return false
}

let is_matched = match_object(minify_options, filename)
.await
.unwrap_or(false);

if !is_matched || original.get_info().minimized {
continue;
return false
}

if let Some(original_source) = original.get_source() {
let input = original_source.source().to_string();
let input_source_map = original_source.map(&MapOptions::default());
let js_minify_options = JsMinifyOptions {
compress: BoolOrDataConfig::from_obj(compress.clone()),
mangle: BoolOrDataConfig::from_obj(mangle.clone()),
format: format.clone(),
source_map: BoolOrDataConfig::from_bool(input_source_map.is_some()),
inline_sources_content: true, /* Using true so original_source can be None in SourceMapSource */
emit_source_map_columns,
module: is_module,
..Default::default()
};
true
})
.try_for_each(|(filename, original)| -> Result<()> {
if let Some(original_source) = original.get_source() {
let input = original_source.source().to_string();
let input_source_map = original_source.map(&MapOptions::default());
let js_minify_options = JsMinifyOptions {
compress: BoolOrDataConfig::from_obj(compress.clone()),
mangle: BoolOrDataConfig::from_obj(mangle.clone()),
format: format.clone(),
source_map: BoolOrDataConfig::from_bool(input_source_map.is_some()),
inline_sources_content: true, /* Using true so original_source can be None in SourceMapSource */
emit_source_map_columns,
module: is_module,
..Default::default()
};
let output = match minify(
&js_minify_options,
input,
filename,
&all_extracted_comments,
extract_comments_option,
) {
Ok(r) => r,
Err(e) => {
return Err(e)
}
};
let source = if let Some(map) = &output.map {
SourceMapSource::new(SourceMapSourceOptions {
value: output.code,
name: filename,
source_map: SourceMap::from_json(map).map_err(|e| internal_error!(e.to_string()))?,
original_source: None,
inner_source_map: input_source_map,
remove_original_source: true,
})
.boxed()
} else {
RawSource::from(output.code).boxed()
};
original.set_source(Some(source));
original.get_info_mut().minimized = true;
}

let output = match minify(
&js_minify_options,
input,
filename,
&all_extracted_comments,
extract_comments_option,
) {
Ok(r) => r,
Err(e) => {
tx.send(e.into())
.map_err(|e| internal_error!(e.to_string()))?;
continue;
}
};
let source = if let Some(map) = &output.map {
SourceMapSource::new(SourceMapSourceOptions {
value: output.code,
name: filename,
source_map: SourceMap::from_json(map).map_err(|e| internal_error!(e.to_string()))?,
original_source: None,
inner_source_map: input_source_map,
remove_original_source: true,
})
.boxed()
} else {
RawSource::from(output.code).boxed()
};
original.set_source(Some(source));
original.get_info_mut().minimized = true;
}
}
Ok(())
});

drop(tx);

compilation.push_batch_diagnostic(rx.into_iter().flatten().collect::<Vec<_>>());
match result {
Ok(_) => {}
Err(e) => compilation.push_batch_diagnostic(e.into()),
};

// write all extracted comments to assets
all_extracted_comments
Expand Down
10 changes: 4 additions & 6 deletions crates/rspack_plugin_swc_js_minimizer/src/minify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::{
sync::{mpsc, Arc, Mutex},
};

use async_recursion::async_recursion;
use regex::Regex;
use rspack_core::{
rspack_sources::{RawSource, SourceExt},
Expand Down Expand Up @@ -43,20 +42,19 @@ use swc_ecma_minifier::{

use crate::{JsMinifyCommentOption, JsMinifyOptions, SwcJsMinimizerRspackPluginOptions};

#[async_recursion]
pub async fn match_object(obj: &SwcJsMinimizerRspackPluginOptions, str: &str) -> Result<bool> {
pub fn match_object(obj: &SwcJsMinimizerRspackPluginOptions, str: &str) -> Result<bool> {
if let Some(condition) = &obj.test {
if !condition.try_match(str).await? {
if !condition.try_match(str)? {
return Ok(false);
}
}
if let Some(condition) = &obj.include {
if !condition.try_match(str).await? {
if !condition.try_match(str)? {
return Ok(false);
}
}
if let Some(condition) = &obj.exclude {
if condition.try_match(str).await? {
if condition.try_match(str)? {
return Ok(false);
}
}
Expand Down
13 changes: 13 additions & 0 deletions crates/rspack_util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ where
Ok(false)
}

pub fn try_any_sync<T, F, E>(it: impl IntoIterator<Item = T>, f: F) -> Result<bool, E>
where
F: Fn(T) -> Result<bool, E>,
{
let it = it.into_iter();
for i in it {
if f(i)? {
return Ok(true);
}
}
Ok(false)
}

pub async fn try_all<T, Fut, F, E>(it: impl IntoIterator<Item = T>, f: F) -> Result<bool, E>
where
Fut: Future<Output = Result<bool, E>>,
Expand Down

0 comments on commit d729541

Please sign in to comment.