From 6b6110b01e9cdf8635f2168ef1fba529126d75ad Mon Sep 17 00:00:00 2001 From: Hana Date: Thu, 9 Jan 2025 19:16:39 +0800 Subject: [PATCH] feat: support `optimization` in compiler options builder (#8979) feat: init --- Cargo.lock | 4 + crates/rspack/Cargo.toml | 9 +- .../src/{options => builder}/devtool.rs | 0 .../src/{options => builder}/externals.rs | 0 .../{options/builder.rs => builder/mod.rs} | 432 ++++++++++++++++-- .../rspack/src/{options => builder}/target.rs | 0 crates/rspack/src/lib.rs | 2 +- crates/rspack/src/options/mod.rs | 10 - .../rspack_core/src/options/optimizations.rs | 2 +- .../src/occurrence_chunk_ids_plugin.rs | 1 + .../src/electron_target_plugin.rs | 1 + .../src/parser_plugin/define_plugin/mod.rs | 2 +- 12 files changed, 420 insertions(+), 43 deletions(-) rename crates/rspack/src/{options => builder}/devtool.rs (100%) rename crates/rspack/src/{options => builder}/externals.rs (100%) rename crates/rspack/src/{options/builder.rs => builder/mod.rs} (82%) rename crates/rspack/src/{options => builder}/target.rs (100%) delete mode 100644 crates/rspack/src/options/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a4035fecd13..82f8a87a69a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3820,9 +3820,13 @@ dependencies = [ "regex", "rspack_core", "rspack_hash", + "rspack_ids", "rspack_paths", "rspack_plugin_devtool", "rspack_plugin_externals", + "rspack_plugin_javascript", + "rspack_plugin_runtime_chunk", + "rspack_plugin_swc_js_minimizer", "rspack_regex", "rustc-hash 2.1.0", "serde_json", diff --git a/crates/rspack/Cargo.toml b/crates/rspack/Cargo.toml index daad3bf45f8..cbabbc8969c 100644 --- a/crates/rspack/Cargo.toml +++ b/crates/rspack/Cargo.toml @@ -17,8 +17,13 @@ rspack_regex = { workspace = true } rustc-hash = { workspace = true } serde_json = { workspace = true } -rspack_plugin_devtool = { workspace = true } -rspack_plugin_externals = { workspace = true } +rspack_ids = { workspace = true } +rspack_plugin_devtool = { workspace = true } +rspack_plugin_externals = { workspace = true } +rspack_plugin_javascript = { workspace = true } +# rspack_plugin_lightning_css_minimizer = { workspace = true } +rspack_plugin_runtime_chunk = { workspace = true } +rspack_plugin_swc_js_minimizer = { workspace = true } [lints] workspace = true diff --git a/crates/rspack/src/options/devtool.rs b/crates/rspack/src/builder/devtool.rs similarity index 100% rename from crates/rspack/src/options/devtool.rs rename to crates/rspack/src/builder/devtool.rs diff --git a/crates/rspack/src/options/externals.rs b/crates/rspack/src/builder/externals.rs similarity index 100% rename from crates/rspack/src/options/externals.rs rename to crates/rspack/src/builder/externals.rs diff --git a/crates/rspack/src/options/builder.rs b/crates/rspack/src/builder/mod.rs similarity index 82% rename from crates/rspack/src/options/builder.rs rename to crates/rspack/src/builder/mod.rs index 00a41ea8e10..cf54e3b5425 100644 --- a/crates/rspack/src/options/builder.rs +++ b/crates/rspack/src/builder/mod.rs @@ -1,26 +1,10 @@ -use indexmap::IndexMap; -use rspack_core::{incremental::IncrementalPasses, ModuleType}; -use rspack_core::{ - AssetParserDataUrl, AssetParserDataUrlOptions, AssetParserOptions, ByDependency, CacheOptions, - ChunkLoading, ChunkLoadingType, CleanOptions, CompilerOptions, Context, CrossOriginLoading, - CssAutoGeneratorOptions, CssAutoParserOptions, CssExportsConvention, CssGeneratorOptions, - CssModuleGeneratorOptions, CssModuleParserOptions, CssParserOptions, DynamicImportMode, - EntryDescription, Environment, ExperimentCacheOptions, Experiments, ExternalItem, ExternalType, - Filename, FilenameTemplate, GeneratorOptions, GeneratorOptionsMap, JavascriptParserOptions, - JavascriptParserOrder, JavascriptParserUrl, JsonParserOptions, LibraryName, LibraryNonUmdObject, - LibraryOptions, LibraryType, Mode, ModuleNoParseRules, ModuleOptions, ModuleRule, - ModuleRuleEffect, OutputOptions, ParserOptions, ParserOptionsMap, PathInfo, PublicPath, Resolve, - RspackFuture, RuleSetCondition, RuleSetLogicalConditions, TrustedTypes, WasmLoading, - WasmLoadingType, -}; -use rspack_hash::{HashDigest, HashFunction, HashSalt}; -use rspack_paths::{AssertUtf8, Utf8PathBuf}; -use rspack_regex::RspackRegex; -use rustc_hash::FxHashMap as HashMap; +mod devtool; +mod externals; +mod target; + +pub use devtool::Devtool; +pub use target::Target; -use super::externals::ExternalsPresets; -use super::target::{get_targets_properties, TargetProperties}; -use super::{Devtool, DevtoolFlags, Target}; macro_rules! d { ($o:expr, $v:expr) => {{ $o.unwrap_or($v) @@ -39,6 +23,30 @@ macro_rules! f { }}; } +use devtool::DevtoolFlags; +use externals::ExternalsPresets; +use indexmap::IndexMap; +use rspack_core::{incremental::IncrementalPasses, ModuleType}; +use rspack_core::{ + AssetParserDataUrl, AssetParserDataUrlOptions, AssetParserOptions, BoxPlugin, ByDependency, + CacheOptions, ChunkLoading, ChunkLoadingType, CleanOptions, CompilerOptions, Context, + CrossOriginLoading, CssAutoGeneratorOptions, CssAutoParserOptions, CssExportsConvention, + CssGeneratorOptions, CssModuleGeneratorOptions, CssModuleParserOptions, CssParserOptions, + DynamicImportMode, EntryDescription, Environment, ExperimentCacheOptions, Experiments, + ExternalItem, ExternalType, Filename, FilenameTemplate, GeneratorOptions, GeneratorOptionsMap, + JavascriptParserOptions, JavascriptParserOrder, JavascriptParserUrl, JsonParserOptions, + LibraryName, LibraryNonUmdObject, LibraryOptions, LibraryType, MangleExportsOption, Mode, + ModuleNoParseRules, ModuleOptions, ModuleRule, ModuleRuleEffect, Optimization, OutputOptions, + ParserOptions, ParserOptionsMap, PathInfo, PublicPath, Resolve, RspackFuture, RuleSetCondition, + RuleSetLogicalConditions, SideEffectOption, TrustedTypes, UsedExportsOption, WasmLoading, + WasmLoadingType, +}; +use rspack_hash::{HashDigest, HashFunction, HashSalt}; +use rspack_paths::{AssertUtf8, Utf8PathBuf}; +use rspack_regex::RspackRegex; +use rustc_hash::FxHashMap as HashMap; +use target::{get_targets_properties, TargetProperties}; + pub trait Builder { type Item; fn builder() -> Self::Item; @@ -58,6 +66,13 @@ impl Builder for OutputOptions { } } +impl Builder for Optimization { + type Item = OptimizationOptionsBuilder; + fn builder() -> Self::Item { + OptimizationOptionsBuilder::default() + } +} + impl Builder for ModuleOptions { type Item = ModuleOptionsBuilder; fn builder() -> Self::Item { @@ -77,8 +92,9 @@ impl Builder for Experiments { /// Plugin options applied to compiler is ordered. /// Plugin options are created ahead of apply and used for ordered plugin application later. #[allow(unused, clippy::enum_variant_names)] +#[derive(Debug)] pub(crate) enum BuiltinPluginOptions { - DefinePlugin, + DefinePlugin(rspack_plugin_javascript::define_plugin::DefineValue), ProvidePlugin, BannerPlugin, IgnorePlugin, @@ -115,7 +131,7 @@ pub(crate) enum BuiltinPluginOptions { NaturalChunkIdsPlugin, NamedChunkIdsPlugin, DeterministicChunkIdsPlugin, - OccurrenceChunkIdsPlugin, + OccurrenceChunkIdsPlugin(rspack_ids::OccurrenceChunkIdsPluginOptions), RealContentHashPlugin, RemoveEmptyChunksPlugin, EnsureChunkConditionsPlugin, @@ -133,12 +149,12 @@ pub(crate) enum BuiltinPluginOptions { EvalDevToolModulePlugin(rspack_plugin_devtool::EvalDevToolModulePluginOptions), SideEffectsFlagPlugin, FlagDependencyExportsPlugin, - FlagDependencyUsagePlugin, - MangleExportsPlugin, + FlagDependencyUsagePlugin(bool), + MangleExportsPlugin(bool), ModuleConcatenationPlugin, CssModulesPlugin, APIPlugin, - RuntimeChunkPlugin, + RuntimeChunkPlugin(rspack_plugin_runtime_chunk::RuntimeChunkOptions), SizeLimitsPlugin, NoEmitOnErrorsPlugin, ContextReplacementPlugin, @@ -156,6 +172,8 @@ pub(crate) enum BuiltinPluginOptions { HtmlRspackPlugin, SwcJsMinimizerRspackPlugin, LightningCssMinimizerRspackPlugin, + // minimizer plugins + AnyMinimizerRspackPlugin(BoxPlugin), BundlerInfoRspackPlugin, CssExtractRspackPlugin, } @@ -182,6 +200,7 @@ pub struct CompilerOptionsBuilder { experiments: Option, module: Option, output: Option, + optimization: Option, } impl CompilerOptionsBuilder { @@ -267,6 +286,14 @@ impl CompilerOptionsBuilder { self } + pub fn optimization(&mut self, optimization: V) -> &mut Self + where + V: Into, + { + self.optimization = Some(optimization.into()); + self + } + pub fn experiments(&mut self, experiments: V) -> &mut Self where V: Into, @@ -526,6 +553,14 @@ impl CompilerOptionsBuilder { ))); } + // apply optimization defaults + let optimization = f!(self.optimization.take(), Optimization::builder).build( + builder_context, + production, + development, + css, + ); + CompilerOptions { name, context, @@ -538,7 +573,7 @@ impl CompilerOptionsBuilder { cache, experiments, node: Default::default(), - optimization: Default::default(), + optimization, profile, amd: None, bail, @@ -1733,6 +1768,347 @@ impl OutputOptionsBuilder { } } +/// Builder used to build options for optimization plugins +#[derive(Debug, Default)] +pub struct OptimizationOptionsBuilder { + remove_available_modules: Option, + remove_empty_chunks: Option, + merge_duplicate_chunks: Option, + module_ids: Option, + chunk_ids: Option, + minimize: Option, + minimizer: Option>, + side_effects: Option, + provided_exports: Option, + used_exports: Option, + inner_graph: Option, + mangle_exports: Option, + concatenate_modules: Option, + real_content_hash: Option, + avoid_entry_iife: Option, + node_env: Option, + emit_on_errors: Option, + runtime_chunk: Option, +} + +impl OptimizationOptionsBuilder { + pub fn remove_available_modules(&mut self, value: bool) -> &mut Self { + self.remove_available_modules = Some(value); + self + } + + pub fn remove_empty_chunks(&mut self, value: bool) -> &mut Self { + self.remove_empty_chunks = Some(value); + self + } + + pub fn merge_duplicate_chunks(&mut self, value: bool) -> &mut Self { + self.merge_duplicate_chunks = Some(value); + self + } + + pub fn module_ids(&mut self, value: String) -> &mut Self { + self.module_ids = Some(value); + self + } + + pub fn chunk_ids(&mut self, value: String) -> &mut Self { + self.chunk_ids = Some(value); + self + } + + pub fn minimize(&mut self, value: bool) -> &mut Self { + self.minimize = Some(value); + self + } + + pub fn minimizer(&mut self, value: Vec) -> &mut Self { + self.minimizer = Some( + value + .into_iter() + .map(BuiltinPluginOptions::AnyMinimizerRspackPlugin) + .collect(), + ); + self + } + + pub fn side_effects(&mut self, value: SideEffectOption) -> &mut Self { + self.side_effects = Some(value); + self + } + + pub fn provided_exports(&mut self, value: bool) -> &mut Self { + self.provided_exports = Some(value); + self + } + + pub fn used_exports(&mut self, value: UsedExportsOption) -> &mut Self { + self.used_exports = Some(value); + self + } + + pub fn inner_graph(&mut self, value: bool) -> &mut Self { + self.inner_graph = Some(value); + self + } + + pub fn mangle_exports(&mut self, value: MangleExportsOption) -> &mut Self { + self.mangle_exports = Some(value); + self + } + + pub fn concatenate_modules(&mut self, value: bool) -> &mut Self { + self.concatenate_modules = Some(value); + self + } + + pub fn real_content_hash(&mut self, value: bool) -> &mut Self { + self.real_content_hash = Some(value); + self + } + + pub fn avoid_entry_iife(&mut self, value: bool) -> &mut Self { + self.avoid_entry_iife = Some(value); + self + } + + pub fn node_env(&mut self, value: String) -> &mut Self { + self.node_env = Some(value); + self + } + + pub fn emit_on_errors(&mut self, value: bool) -> &mut Self { + self.emit_on_errors = Some(value); + self + } + + pub fn runtime_chunk( + &mut self, + value: rspack_plugin_runtime_chunk::RuntimeChunkOptions, + ) -> &mut Self { + self.runtime_chunk = Some(value); + self + } + + pub fn build( + &mut self, + builder_context: &mut BuilderContext, + development: bool, + production: bool, + _css: bool, + ) -> Optimization { + let remove_available_modules = d!(self.remove_available_modules, false); + let remove_empty_chunks = d!(self.remove_empty_chunks, true); + if remove_empty_chunks { + builder_context + .plugins + .push(BuiltinPluginOptions::RemoveEmptyChunksPlugin); + } + let real_content_hash = d!(self.real_content_hash, production); + if real_content_hash { + builder_context + .plugins + .push(BuiltinPluginOptions::RealContentHashPlugin); + } + let merge_duplicate_chunks = d!(self.merge_duplicate_chunks, true); + if merge_duplicate_chunks { + builder_context + .plugins + .push(BuiltinPluginOptions::MergeDuplicateChunksPlugin); + } + let module_ids = w!(self.module_ids, { + if production { + "deterministic".to_string() + } else if development { + "named".to_string() + } else { + "natural".to_string() + } + }); + + match module_ids.as_str() { + "deterministic" => { + builder_context + .plugins + .push(BuiltinPluginOptions::DeterministicModuleIdsPlugin); + } + "named" => { + builder_context + .plugins + .push(BuiltinPluginOptions::NamedModuleIdsPlugin); + } + "natural" => { + builder_context + .plugins + .push(BuiltinPluginOptions::NaturalModuleIdsPlugin); + } + _ => { + panic!("moduleIds: {module_ids} is not implemented"); + } + } + + let chunk_ids = w!(self.chunk_ids, { + if production { + "deterministic".to_string() + } else if development { + "named".to_string() + } else { + "natural".to_string() + } + }); + + match chunk_ids.as_str() { + "deterministic" => { + builder_context + .plugins + .push(BuiltinPluginOptions::DeterministicChunkIdsPlugin); + } + "named" => { + builder_context + .plugins + .push(BuiltinPluginOptions::NamedChunkIdsPlugin); + } + "natural" => { + builder_context + .plugins + .push(BuiltinPluginOptions::NaturalChunkIdsPlugin); + } + "size" => { + builder_context + .plugins + .push(BuiltinPluginOptions::OccurrenceChunkIdsPlugin( + rspack_ids::OccurrenceChunkIdsPluginOptions { + prioritise_initial: true, + }, + )); + } + "total-size" => { + builder_context + .plugins + .push(BuiltinPluginOptions::OccurrenceChunkIdsPlugin( + rspack_ids::OccurrenceChunkIdsPluginOptions { + prioritise_initial: false, + }, + )); + } + + _ => { + panic!("chunkIds: {chunk_ids} is not implemented"); + } + } + + let side_effects = f!(self.side_effects.take(), || { + if production { + SideEffectOption::True + } else { + SideEffectOption::Flag + } + }); + if side_effects.is_enable() { + builder_context + .plugins + .push(BuiltinPluginOptions::SideEffectsFlagPlugin); + } + + let mangle_exports = f!(self.mangle_exports.take(), || { + if production { + MangleExportsOption::Deterministic + } else { + MangleExportsOption::False + } + }); + if mangle_exports.is_enable() { + builder_context + .plugins + .push(BuiltinPluginOptions::MangleExportsPlugin( + mangle_exports != MangleExportsOption::Size, + )); + } + let provided_exports = d!(self.provided_exports, true); + if provided_exports { + builder_context + .plugins + .push(BuiltinPluginOptions::FlagDependencyExportsPlugin); + } + let used_exports = f!(self.used_exports.take(), || { + if production { + UsedExportsOption::True + } else { + UsedExportsOption::False + } + }); + if used_exports.is_enable() { + builder_context + .plugins + .push(BuiltinPluginOptions::FlagDependencyUsagePlugin( + used_exports.is_global(), + )); + } + let inner_graph = d!(self.inner_graph, production); + if !d!(self.emit_on_errors, !production) { + builder_context + .plugins + .push(BuiltinPluginOptions::NoEmitOnErrorsPlugin); + } + + if let Some(runtime_chunk) = self.runtime_chunk.take() { + builder_context + .plugins + .push(BuiltinPluginOptions::RuntimeChunkPlugin(runtime_chunk)); + } + + let concatenate_modules = d!(self.concatenate_modules, production); + if concatenate_modules { + builder_context + .plugins + .push(BuiltinPluginOptions::ModuleConcatenationPlugin); + } + + let avoid_entry_iife = d!(self.avoid_entry_iife, false); + let minimize = d!(self.minimize, production); + let minimizer = f!(self.minimizer.take(), || { + if minimize { + vec![ + BuiltinPluginOptions::AnyMinimizerRspackPlugin(Box::new( + rspack_plugin_swc_js_minimizer::SwcJsMinimizerRspackPlugin::new( + rspack_plugin_swc_js_minimizer::PluginOptions { + test: None, + include: None, + exclude: None, + extract_comments: None, + minimizer_options: Default::default(), + }, + ), + )), + // TODO: add lightning css + ] + } else { + vec![] + } + }); + builder_context.plugins.extend(minimizer); + + if let Some(node_env) = self.node_env.take() { + builder_context + .plugins + .push(BuiltinPluginOptions::DefinePlugin( + [("process.env.NODE_ENV".to_string(), node_env.into())].into(), + )); + } + + Optimization { + remove_available_modules, + side_effects, + provided_exports, + used_exports, + inner_graph, + mangle_exports, + concatenate_modules, + avoid_entry_iife, + } + } +} + /// Builder used to build [`Experiments`] #[derive(Debug, Default)] pub struct ExperimentsBuilder { diff --git a/crates/rspack/src/options/target.rs b/crates/rspack/src/builder/target.rs similarity index 100% rename from crates/rspack/src/options/target.rs rename to crates/rspack/src/builder/target.rs diff --git a/crates/rspack/src/lib.rs b/crates/rspack/src/lib.rs index 66dd7795f50..5575a85eb34 100644 --- a/crates/rspack/src/lib.rs +++ b/crates/rspack/src/lib.rs @@ -1 +1 @@ -pub mod options; +pub mod builder; diff --git a/crates/rspack/src/options/mod.rs b/crates/rspack/src/options/mod.rs deleted file mode 100644 index c8dacfcdd37..00000000000 --- a/crates/rspack/src/options/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod builder; -mod devtool; -mod externals; -mod target; - -pub use builder::{ - Builder, CompilerOptionsBuilder, ExperimentsBuilder, ModuleOptionsBuilder, OutputOptionsBuilder, -}; -pub use devtool::{Devtool, DevtoolFlags}; -pub use target::Target; diff --git a/crates/rspack_core/src/options/optimizations.rs b/crates/rspack_core/src/options/optimizations.rs index 7c149ae6fc7..0ec505e9394 100644 --- a/crates/rspack_core/src/options/optimizations.rs +++ b/crates/rspack_core/src/options/optimizations.rs @@ -93,7 +93,7 @@ impl UsedExportsOption { } } -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub enum MangleExportsOption { #[default] False, diff --git a/crates/rspack_ids/src/occurrence_chunk_ids_plugin.rs b/crates/rspack_ids/src/occurrence_chunk_ids_plugin.rs index 5653f29114e..108206b9ef8 100644 --- a/crates/rspack_ids/src/occurrence_chunk_ids_plugin.rs +++ b/crates/rspack_ids/src/occurrence_chunk_ids_plugin.rs @@ -10,6 +10,7 @@ use rspack_hook::{plugin, plugin_hook}; use crate::id_helpers::{assign_ascending_chunk_ids, compare_chunks_natural}; +#[derive(Debug)] pub struct OccurrenceChunkIdsPluginOptions { pub prioritise_initial: bool, } diff --git a/crates/rspack_plugin_externals/src/electron_target_plugin.rs b/crates/rspack_plugin_externals/src/electron_target_plugin.rs index 50fa8ae758e..7120df56bff 100644 --- a/crates/rspack_plugin_externals/src/electron_target_plugin.rs +++ b/crates/rspack_plugin_externals/src/electron_target_plugin.rs @@ -2,6 +2,7 @@ use rspack_core::{BoxPlugin, ExternalItem, PluginExt}; use crate::ExternalsPlugin; +#[derive(Debug)] pub enum ElectronTargetContext { Main, Preload, diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/mod.rs b/crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/mod.rs index d00bf32aed2..721cefd6a02 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/mod.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/mod.rs @@ -19,7 +19,7 @@ use serde_json::Value; use crate::parser_and_generator::JavaScriptParserAndGenerator; -type DefineValue = HashMap; +pub type DefineValue = HashMap; const VALUE_DEP_PREFIX: &str = "webpack/DefinePlugin ";