diff --git a/crates/rspack_core/src/context_module.rs b/crates/rspack_core/src/context_module.rs index 58915ce8a98..487f8595ebc 100644 --- a/crates/rspack_core/src/context_module.rs +++ b/crates/rspack_core/src/context_module.rs @@ -20,6 +20,7 @@ use rspack_sources::{BoxSource, ConcatSource, RawSource, SourceExt}; use rspack_util::{fx_hash::FxIndexMap, json_stringify, source_map::SourceMapKind}; use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashSet as HashSet; +use swc_core::atoms::Atom; use crate::{ block_promise, contextify, get_exports_type_with_strict, impl_module_meta_info, @@ -141,6 +142,7 @@ pub struct ContextOptions { pub replaces: Vec<(String, u32, u32)>, pub start: u32, pub end: u32, + pub referenced_exports: Option>, } #[derive(Debug, PartialEq, Clone)] @@ -1074,7 +1076,7 @@ impl ContextModule { context: options.resource.clone().into(), options: options.context_options.clone(), resource_identifier: format!("context{}|{}", &options.resource, path.to_string_lossy()), - referenced_exports: None, + referenced_exports: options.context_options.referenced_exports.clone(), dependency_type: DependencyType::ContextElement(options.type_prefix), }); }) @@ -1217,6 +1219,14 @@ fn create_identifier(options: &ContextModuleOptions) -> Identifier { id += "|exclude: "; id += &exclude.to_source_string(); } + if let Some(exports) = &options.context_options.referenced_exports { + id += "|referencedExports: "; + id += &format!( + "[{}]", + exports.iter().map(|x| format!(r#""{x}""#)).join(",") + ); + } + if let Some(GroupOptions::ChunkGroup(group)) = &options.context_options.group_options { if let Some(chunk_name) = &group.name { id += "|chunkName: "; diff --git a/crates/rspack_core/src/dependency/context_element_dependency.rs b/crates/rspack_core/src/dependency/context_element_dependency.rs index b7614e4cd14..a83651de4ed 100644 --- a/crates/rspack_core/src/dependency/context_element_dependency.rs +++ b/crates/rspack_core/src/dependency/context_element_dependency.rs @@ -1,6 +1,7 @@ +use itertools::Itertools; use swc_core::ecma::atoms::Atom; -use crate::{AsContextDependency, AsDependencyTemplate, Context}; +use crate::{create_exports_object_referenced, AsContextDependency, AsDependencyTemplate, Context}; use crate::{ContextMode, ContextOptions, Dependency}; use crate::{DependencyCategory, DependencyId, DependencyType}; use crate::{ExtendedReferencedExport, ModuleDependency}; @@ -47,9 +48,14 @@ impl Dependency for ContextElementDependency { _runtime: Option<&RuntimeSpec>, ) -> Vec { if let Some(referenced_exports) = &self.referenced_exports { - vec![ReferencedExport::new(referenced_exports.clone(), false).into()] + referenced_exports + .iter() + .map(|export| { + ExtendedReferencedExport::Export(ReferencedExport::new(vec![export.clone()], false)) + }) + .collect_vec() } else { - vec![ExtendedReferencedExport::Array(vec![])] + create_exports_object_referenced() } } } diff --git a/crates/rspack_core/src/dependency/import_dependency_trait.rs b/crates/rspack_core/src/dependency/import_dependency_trait.rs deleted file mode 100644 index 0ae3b1d658c..00000000000 --- a/crates/rspack_core/src/dependency/import_dependency_trait.rs +++ /dev/null @@ -1,20 +0,0 @@ -use swc_core::ecma::atoms::Atom; - -use crate::{ExtendedReferencedExport, ModuleDependency}; -use crate::{ModuleGraph, ReferencedExport, RuntimeSpec}; - -pub trait ImportDependencyTrait: ModuleDependency { - fn referenced_exports(&self) -> Option<&Vec>; - - fn get_referenced_exports( - &self, - _module_graph: &ModuleGraph, - _runtime: Option<&RuntimeSpec>, - ) -> Vec { - if let Some(referenced_exports) = self.referenced_exports() { - vec![ReferencedExport::new(referenced_exports.clone(), false).into()] - } else { - vec![ExtendedReferencedExport::Array(vec![])] - } - } -} diff --git a/crates/rspack_core/src/dependency/mod.rs b/crates/rspack_core/src/dependency/mod.rs index 6373af41ea2..0daa6cfaf58 100644 --- a/crates/rspack_core/src/dependency/mod.rs +++ b/crates/rspack_core/src/dependency/mod.rs @@ -9,7 +9,6 @@ mod dependency_template; mod dependency_trait; mod dependency_type; mod entry; -mod import_dependency_trait; mod loader_import; mod module_dependency; mod runtime_requirements_dependency; @@ -29,7 +28,6 @@ pub use dependency_template::*; pub use dependency_trait::*; pub use dependency_type::DependencyType; pub use entry::*; -pub use import_dependency_trait::ImportDependencyTrait; pub use loader_import::*; pub use module_dependency::*; pub use runtime_requirements_dependency::RuntimeRequirementsDependency; diff --git a/crates/rspack_core/src/exports_info.rs b/crates/rspack_core/src/exports_info.rs index 13096172e81..29a435a0532 100644 --- a/crates/rspack_core/src/exports_info.rs +++ b/crates/rspack_core/src/exports_info.rs @@ -740,11 +740,17 @@ impl ExportsInfo { } UsedName::Vec(value) => { if value.is_empty() { - let other_export_info = module_graph.get_export_info_by_id(&self.other_exports_info); - return other_export_info.get_used(runtime); + return self + .other_exports_info + .get_export_info(module_graph) + .get_used(runtime); } let info = self.id.get_read_only_export_info(&value[0], module_graph); - if let Some(exports_info) = info.get_exports_info(module_graph) { + if let Some(exports_info) = info + .exports_info + .map(|id| id.get_exports_info(module_graph)) + && value.len() > 1 + { return exports_info.get_used( UsedName::Vec(value.iter().skip(1).cloned().collect::>()), runtime, diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs index 06118f21ecd..a13f6476b89 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs @@ -1,9 +1,54 @@ -use rspack_core::{module_namespace_promise, DependencyType, ErrorSpan, ImportDependencyTrait}; +use rspack_core::{ + create_exports_object_referenced, module_namespace_promise, DependencyType, ErrorSpan, + ExportsType, ExtendedReferencedExport, ModuleGraph, ReferencedExport, +}; use rspack_core::{AsContextDependency, Dependency}; use rspack_core::{DependencyCategory, DependencyId, DependencyTemplate}; use rspack_core::{ModuleDependency, TemplateContext, TemplateReplaceSource}; use swc_core::ecma::atoms::Atom; +pub fn create_import_dependency_referenced_exports( + dependency_id: &DependencyId, + referenced_exports: &Option>, + mg: &ModuleGraph, +) -> Vec { + if let Some(referenced_exports) = referenced_exports { + let mut refs = vec![]; + for referenced_export in referenced_exports { + if referenced_export == "default" { + let Some(strict) = mg + .get_parent_module(dependency_id) + .and_then(|id| mg.module_by_identifier(id)) + .and_then(|m| m.build_meta()) + .map(|bm| bm.strict_harmony_module) + else { + return create_exports_object_referenced(); + }; + let Some(imported_module) = mg + .module_identifier_by_dependency_id(dependency_id) + .and_then(|id| mg.module_by_identifier(id)) + else { + return create_exports_object_referenced(); + }; + let exports_type = imported_module.get_exports_type_readonly(mg, strict); + if matches!( + exports_type, + ExportsType::DefaultOnly | ExportsType::DefaultWithNamed + ) { + return create_exports_object_referenced(); + } + } + refs.push(ExtendedReferencedExport::Export(ReferencedExport::new( + vec![referenced_export.clone()], + false, + ))); + } + refs + } else { + create_exports_object_referenced() + } +} + #[derive(Debug, Clone)] pub struct ImportDependency { start: u32, @@ -49,6 +94,14 @@ impl Dependency for ImportDependency { fn span(&self) -> Option { self.span } + + fn get_referenced_exports( + &self, + module_graph: &rspack_core::ModuleGraph, + _runtime: Option<&rspack_core::RuntimeSpec>, + ) -> Vec { + create_import_dependency_referenced_exports(&self.id, &self.referenced_exports, module_graph) + } } impl ModuleDependency for ImportDependency { @@ -65,12 +118,6 @@ impl ModuleDependency for ImportDependency { } } -impl ImportDependencyTrait for ImportDependency { - fn referenced_exports(&self) -> Option<&Vec> { - self.referenced_exports.as_ref() - } -} - impl DependencyTemplate for ImportDependency { fn apply( &self, diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/import_eager_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/import_eager_dependency.rs index b2d394b27a0..32cd17a0f4b 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/import_eager_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/import_eager_dependency.rs @@ -1,11 +1,12 @@ use rspack_core::{ module_namespace_promise, AsContextDependency, Dependency, DependencyCategory, DependencyId, - DependencyTemplate, DependencyType, ErrorSpan, ExtendedReferencedExport, ImportDependencyTrait, - ModuleDependency, ModuleGraph, ReferencedExport, RuntimeSpec, TemplateContext, + DependencyTemplate, DependencyType, ErrorSpan, ModuleDependency, TemplateContext, TemplateReplaceSource, }; use swc_core::ecma::atoms::Atom; +use super::import_dependency::create_import_dependency_referenced_exports; + #[derive(Debug, Clone)] pub struct ImportEagerDependency { start: u32, @@ -54,14 +55,10 @@ impl Dependency for ImportEagerDependency { fn get_referenced_exports( &self, - _module_graph: &ModuleGraph, - _runtime: Option<&RuntimeSpec>, - ) -> Vec { - if let Some(referenced_exports) = &self.referenced_exports { - vec![ReferencedExport::new(referenced_exports.clone(), false).into()] - } else { - vec![ExtendedReferencedExport::Array(vec![])] - } + module_graph: &rspack_core::ModuleGraph, + _runtime: Option<&rspack_core::RuntimeSpec>, + ) -> Vec { + create_import_dependency_referenced_exports(&self.id, &self.referenced_exports, module_graph) } } @@ -79,12 +76,6 @@ impl ModuleDependency for ImportEagerDependency { } } -impl ImportDependencyTrait for ImportEagerDependency { - fn referenced_exports(&self) -> Option<&Vec> { - self.referenced_exports.as_ref() - } -} - impl DependencyTemplate for ImportEagerDependency { fn apply( &self, diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/provide_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/provide_dependency.rs index 8e91d99e7c8..d2663361d81 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/provide_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/provide_dependency.rs @@ -1,5 +1,8 @@ use itertools::Itertools; -use rspack_core::{module_raw, NormalInitFragment, UsedName}; +use rspack_core::{ + create_exports_object_referenced, module_raw, ExtendedReferencedExport, ModuleGraph, + NormalInitFragment, RuntimeSpec, UsedName, +}; use rspack_core::{AsContextDependency, Dependency, InitFragmentKey, InitFragmentStage}; use rspack_core::{DependencyCategory, DependencyId, DependencyTemplate}; use rspack_core::{DependencyType, ErrorSpan}; @@ -45,6 +48,18 @@ impl Dependency for ProvideDependency { fn span(&self) -> Option { None } + + fn get_referenced_exports( + &self, + _module_graph: &ModuleGraph, + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + if self.ids.is_empty() { + create_exports_object_referenced() + } else { + vec![ExtendedReferencedExport::Array(self.ids.clone())] + } + } } impl ModuleDependency for ProvideDependency { diff --git a/crates/rspack_plugin_javascript/src/dependency/export_info_api_dep.rs b/crates/rspack_plugin_javascript/src/dependency/export_info_api_dep.rs deleted file mode 100644 index 63463d65bfd..00000000000 --- a/crates/rspack_plugin_javascript/src/dependency/export_info_api_dep.rs +++ /dev/null @@ -1,81 +0,0 @@ -use rspack_core::{ - AsDependency, DependencyTemplate, TemplateContext, TemplateReplaceSource, UsageState, -}; -use swc_core::ecma::atoms::Atom; - -#[derive(Debug, Clone)] -pub struct ExportInfoApiDependency { - start: u32, - end: u32, - // id: DependencyId, - export_name: Vec, - property: Atom, - // TODO: runtime_requirements -} - -impl ExportInfoApiDependency { - pub fn new(start: u32, end: u32, export_name: Vec, property: Atom) -> Self { - Self { - start, - end, - // id: DependencyId::new(), - export_name, - property, - } - } -} - -impl DependencyTemplate for ExportInfoApiDependency { - fn apply( - &self, - source: &mut TemplateReplaceSource, - code_generatable_context: &mut TemplateContext, - ) { - let usage = matches!( - self.get_property(code_generatable_context), - Some(UsageState::Used) - ); - source.replace(self.start, self.end, usage.to_string().as_ref(), None); - } - - fn dependency_id(&self) -> Option { - None - } -} - -impl ExportInfoApiDependency { - fn get_property(&self, context: &TemplateContext) -> Option { - let TemplateContext { - compilation, - module, - runtime, - .. - } = context; - let export_name = &self.export_name; - let prop = &self.property; - let module_graph = compilation.get_module_graph(); - // TODO: nested export_name, one level is enough for test - if export_name.len() == 1 { - let export_name = &export_name[0]; - match prop.to_string().as_str() { - "used" => { - let id = module.identifier(); - let mgm = module_graph.module_graph_module_by_identifier(&id)?; - let exports_info = module_graph.get_exports_info_by_id(&mgm.exports); - Some(exports_info.get_used( - rspack_core::UsedName::Str(export_name.clone()), - *runtime, - &module_graph, - )) - } - _ => { - // TODO: support other prop - None - } - } - } else { - None - } - } -} -impl AsDependency for ExportInfoApiDependency {} diff --git a/crates/rspack_plugin_javascript/src/dependency/export_info_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/export_info_dependency.rs new file mode 100644 index 00000000000..1e8645c028e --- /dev/null +++ b/crates/rspack_plugin_javascript/src/dependency/export_info_dependency.rs @@ -0,0 +1,127 @@ +use itertools::Itertools; +use rspack_core::{ + AsDependency, DependencyTemplate, ExportProvided, TemplateContext, TemplateReplaceSource, + UsageState, UsedExports, UsedName, +}; +use swc_core::ecma::atoms::Atom; + +#[derive(Debug, Clone)] +pub struct ExportInfoDependency { + start: u32, + end: u32, + export_name: Vec, + property: Atom, +} + +impl ExportInfoDependency { + pub fn new(start: u32, end: u32, export_name: Vec, property: Atom) -> Self { + Self { + start, + end, + export_name, + property, + } + } +} + +impl DependencyTemplate for ExportInfoDependency { + fn apply(&self, source: &mut TemplateReplaceSource, context: &mut TemplateContext) { + let value = self.get_property(context); + source.replace( + self.start, + self.end, + value.unwrap_or("undefined".to_owned()).as_str(), + None, + ); + } + + fn dependency_id(&self) -> Option { + None + } +} + +impl ExportInfoDependency { + fn get_property(&self, context: &TemplateContext) -> Option { + let TemplateContext { + compilation, + module, + runtime, + .. + } = context; + let export_name = &self.export_name; + let prop = &self.property; + let module_graph = compilation.get_module_graph(); + let module_identifier = module.identifier(); + + if export_name.is_empty() && prop == "usedExports" { + let used_exports = module_graph + .get_exports_info(&module_identifier) + .get_used_exports(&module_graph, *runtime); + return Some(match used_exports { + UsedExports::Null => "null".to_owned(), + UsedExports::Bool(value) => value.to_string(), + UsedExports::Vec(exports) => { + format!( + r#"[{}]"#, + exports + .iter() + .map(|x| format!(r#""{x}""#)) + .collect_vec() + .join(",") + ) + } + }); + } + + let exports_info = module_graph.get_exports_info(&module_identifier); + + match prop.to_string().as_str() { + "canMangle" => { + let can_mangle = if let Some(export_info) = exports_info + .id + .get_read_only_export_info_recursive(export_name, &module_graph) + { + export_info.can_mangle() + } else { + exports_info + .other_exports_info + .get_export_info(&module_graph) + .can_mangle() + }; + can_mangle.map(|v| v.to_string()) + } + "used" => { + let used = + exports_info.get_used(UsedName::Vec(export_name.clone()), *runtime, &module_graph); + Some((!matches!(used, UsageState::Unused)).to_string()) + } + "useInfo" => { + let used_state = + exports_info.get_used(UsedName::Vec(export_name.clone()), *runtime, &module_graph); + Some( + (match used_state { + UsageState::Used => "true", + UsageState::OnlyPropertiesUsed => "true", + UsageState::Unused => "false", + UsageState::NoInfo => "undefined", + UsageState::Unknown => "null", + }) + .to_owned(), + ) + } + "provideInfo" => exports_info + .id + .is_export_provided(export_name, &module_graph) + .map(|provided| { + (match provided { + ExportProvided::True => "true", + ExportProvided::False => "false", + ExportProvided::Null => "null", + }) + .to_owned() + }), + _ => None, + } + } +} +impl AsDependency for ExportInfoDependency {} diff --git a/crates/rspack_plugin_javascript/src/dependency/mod.rs b/crates/rspack_plugin_javascript/src/dependency/mod.rs index 1965012fc75..d4b68d94d08 100644 --- a/crates/rspack_plugin_javascript/src/dependency/mod.rs +++ b/crates/rspack_plugin_javascript/src/dependency/mod.rs @@ -1,7 +1,7 @@ mod commonjs; mod context; mod esm; -mod export_info_api_dep; +mod export_info_dependency; mod hmr; mod is_included_dependency; mod module_argument_dependency; @@ -12,7 +12,7 @@ mod worker; pub use self::commonjs::*; pub use self::context::*; pub use self::esm::*; -pub use self::export_info_api_dep::*; +pub use self::export_info_dependency::*; pub use self::hmr::*; pub use self::is_included_dependency::*; pub use self::module_argument_dependency::*; diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs index 2f0f5ed8d49..ed42aa4a374 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs @@ -40,6 +40,7 @@ fn create_commonjs_require_context_dependency( replaces: result.replaces, start: callee_start, end: callee_end, + referenced_exports: None, }; CommonJsRequireContextDependency::new( callee_start, @@ -228,6 +229,7 @@ impl CommonJsImportsParserPlugin { replaces: Vec::new(), start: ident.span().real_lo(), end: ident.span().real_hi(), + referenced_exports: None, }, Some(ident.span().into()), parser.in_try, diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/exports_info_api_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/exports_info_api_plugin.rs index e2e1503aa91..48b6f02e1de 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/exports_info_api_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/exports_info_api_plugin.rs @@ -1,7 +1,7 @@ use rspack_core::{extract_member_expression_chain, ConstDependency, SpanExt}; use super::JavascriptParserPlugin; -use crate::{dependency::ExportInfoApiDependency, visitors::JavascriptParser}; +use crate::{dependency::ExportInfoDependency, visitors::JavascriptParser}; const WEBPACK_EXPORTS_INFO: &str = "__webpack_exports_info__"; @@ -21,9 +21,9 @@ impl JavascriptParserPlugin for ExportsInfoApiPlugin { && parser.is_unresolved_ident(WEBPACK_EXPORTS_INFO) { let len = member_chain.len(); - if len >= 3 { + if len >= 2 { let prop = member_chain[len - 1].0.clone(); - let dep = Box::new(ExportInfoApiDependency::new( + let dep = Box::new(ExportInfoDependency::new( member_expr.span.real_lo(), member_expr.span.real_hi(), member_chain @@ -37,7 +37,6 @@ impl JavascriptParserPlugin for ExportsInfoApiPlugin { parser.presentational_dependencies.push(dep); Some(true) } else { - // TODO: support other __webpack_exports_info__ None } } else { diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_context_dependency_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_context_dependency_parser_plugin.rs index 62b9740479b..cf82412943f 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_context_dependency_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_context_dependency_parser_plugin.rs @@ -73,6 +73,7 @@ fn create_import_meta_context_dependency( replaces: Vec::new(), start: node.span().real_lo(), end: node.span().real_hi(), + referenced_exports: None, } } else { ContextOptions { @@ -89,6 +90,7 @@ fn create_import_meta_context_dependency( replaces: Vec::new(), start: node.span().real_lo(), end: node.span().real_hi(), + referenced_exports: None, } }; Some(ImportMetaContextDependency::new( diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs index 1d28d93316f..5f2bfa3e41d 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs @@ -156,6 +156,7 @@ impl JavascriptParserPlugin for ImportParserPlugin { replaces, start: node.span().real_lo(), end: node.span().real_hi(), + referenced_exports: exports, }, Some(node.span.into()), parser.in_try, diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/require_context_dependency_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/require_context_dependency_parser_plugin.rs index 93d9269abf6..0a08a828822 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/require_context_dependency_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/require_context_dependency_parser_plugin.rs @@ -92,6 +92,7 @@ impl JavascriptParserPlugin for RequireContextDependencyParserPlugin { replaces: Vec::new(), start: expr.span().real_lo(), end: expr.span().real_hi(), + referenced_exports: None, }, Some(expr.span.into()), parser.in_try, diff --git a/crates/rspack_plugin_javascript/src/webpack_comment.rs b/crates/rspack_plugin_javascript/src/webpack_comment.rs index 0d661e39ad4..ec86bb963d4 100644 --- a/crates/rspack_plugin_javascript/src/webpack_comment.rs +++ b/crates/rspack_plugin_javascript/src/webpack_comment.rs @@ -99,10 +99,18 @@ impl WebpackCommentMap { } pub fn get_webpack_exports(&self) -> Option> { - self - .0 - .get(&WebpackComment::Exports) - .map(|expr| expr.split(',').map(|x| x.to_owned()).collect_vec()) + self.0.get(&WebpackComment::Exports).map(|expr| { + expr + .split(',') + .filter_map(|x| { + if x.is_empty() { + None + } else { + Some(x.to_owned()) + } + }) + .collect_vec() + }) } } diff --git a/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir12/a.js b/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir12/a.js new file mode 100644 index 00000000000..880c38a1988 --- /dev/null +++ b/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir12/a.js @@ -0,0 +1,9 @@ +export const c = "c"; + +export const d = "d"; + +export const longnameforexport = "longnameforexport"; + +export default "default2"; + +export const usedExports = __webpack_exports_info__.usedExports; diff --git a/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir13/a.js b/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir13/a.js new file mode 100644 index 00000000000..fbeecbd2065 --- /dev/null +++ b/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir13/a.js @@ -0,0 +1,7 @@ +export const c = "c"; + +export const d = "d"; + +export default "default2"; + +export const usedExports = __webpack_exports_info__.usedExports; diff --git a/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir13/b.js b/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir13/b.js new file mode 100644 index 00000000000..b73c5a615da --- /dev/null +++ b/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/dir13/b.js @@ -0,0 +1,7 @@ +export const a = "a"; + +export const b = "b"; + +export default "default"; + +export const usedExports = __webpack_exports_info__.usedExports; diff --git a/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/index.js b/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/index.js new file mode 100644 index 00000000000..163a234ac2b --- /dev/null +++ b/packages/rspack-test-tools/tests/normalCases/parsing/webpack-exports/index.js @@ -0,0 +1,63 @@ +it("should contain only one export from webpackExports from module", function () { + return import(/* webpackExports: "usedExports" */ "./dir12/a?1").then( + module => { + expect(module.usedExports).toEqual(["usedExports"]); + } + ); +}); + +it("should contain only webpackExports from module", function () { + return import( + /* webpackExports: ["a", "usedExports", "b"] */ "./dir12/a?2" + ).then(module => { + expect(module.usedExports).toEqual(["a", "b", "usedExports"]); + }); +}); + +it("should contain only webpackExports from module in eager mode", function () { + return import( + /* + webpackMode: "eager", + webpackExports: ["a", "usedExports", "b"] + */ "./dir12/a?3" + ).then(module => { + expect(module.usedExports).toEqual(["a", "b", "usedExports"]); + }); +}); + +it("should contain webpackExports from module in weak mode", function () { + require.resolve("./dir12/a?4"); + return import( + /* + webpackMode: "weak", + webpackExports: ["a", "usedExports", "b"] + */ "./dir12/a?4" + ).then(module => { + expect(module.usedExports).toEqual(["a", "b", "usedExports"]); + }); +}); + +it("should not mangle webpackExports from module", function () { + return import(/* webpackExports: "longnameforexport" */ "./dir12/a?5").then( + module => { + expect(module).toHaveProperty("longnameforexport"); + } + ); +}); + +it("should not mangle default webpackExports from module", function () { + return import(/* webpackExports: "default" */ "./dir12/a?6").then( + module => { + expect(module).toHaveProperty("default"); + } + ); +}); + +it("should contain only webpackExports from module in context mode", function () { + const x = "b"; + return import(/* webpackExports: "usedExports" */ `./dir13/${x}`).then( + module => { + expect(module.usedExports).toEqual(["usedExports"]); + } + ); +}); \ No newline at end of file diff --git a/tests/webpack-test/cases/inner-graph/extend-class2/test.filter.js b/tests/webpack-test/cases/inner-graph/extend-class2/test.filter.js deleted file mode 100644 index ad8d560b5ac..00000000000 --- a/tests/webpack-test/cases/inner-graph/extend-class2/test.filter.js +++ /dev/null @@ -1,4 +0,0 @@ - -module.exports = () => {return true} - - diff --git a/tests/webpack-test/cases/parsing/harmony-deep-exports/test.filter.js b/tests/webpack-test/cases/parsing/harmony-deep-exports/test.filter.js deleted file mode 100644 index e86046352ae..00000000000 --- a/tests/webpack-test/cases/parsing/harmony-deep-exports/test.filter.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => {return "https://github.com/web-infra-dev/rspack/issues/4426"} diff --git a/tests/webpack-test/cases/parsing/harmony-export-import-specifier/test.filter.js b/tests/webpack-test/cases/parsing/harmony-export-import-specifier/test.filter.js deleted file mode 100644 index eec05b0526b..00000000000 --- a/tests/webpack-test/cases/parsing/harmony-export-import-specifier/test.filter.js +++ /dev/null @@ -1,4 +0,0 @@ - -module.exports = () => {return "https://github.com/web-infra-dev/rspack/issues/4426"} - - \ No newline at end of file diff --git a/tests/webpack-test/cases/parsing/harmony-reexport/test.filter.js b/tests/webpack-test/cases/parsing/harmony-reexport/test.filter.js deleted file mode 100644 index 94a7b84d727..00000000000 --- a/tests/webpack-test/cases/parsing/harmony-reexport/test.filter.js +++ /dev/null @@ -1,4 +0,0 @@ - -module.exports = () => {return "new treeshaking re-implementation"} - - \ No newline at end of file diff --git a/tests/webpack-test/configCases/optimization/runtime-specific-used-exports/test.filter.js b/tests/webpack-test/configCases/optimization/runtime-specific-used-exports/test.filter.js deleted file mode 100644 index 8e5c51af343..00000000000 --- a/tests/webpack-test/configCases/optimization/runtime-specific-used-exports/test.filter.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => {return true} diff --git a/website/docs/en/api/modules/module-variables.mdx b/website/docs/en/api/modules/module-variables.mdx index f3d4c20f8fc..01795a0970c 100644 --- a/website/docs/en/api/modules/module-variables.mdx +++ b/website/docs/en/api/modules/module-variables.mdx @@ -501,6 +501,42 @@ __resourceQuery +### \_\_webpack_exports_info\_\_ + + + +In modules, `__webpack_exports_info__` is available to allow exports introspection: + +- `__webpack_exports_info__` is always `true` +- `__webpack_exports_info__..used` is `false` when the export is known to be unused, `true` otherwise +- `__webpack_exports_info__..useInfo` is + - `false` when the export is known to be unused + - `true` when the export is known to be used + - `null` when the export usage could depend on runtime conditions + - `undefined` when no info is available +- `__webpack_exports_info__..provideInfo` is + - `false` when the export is known to be not provided + - `true` when the export is known to be provided + - `null` when the export provision could depend on runtime conditions + - `undefined` when no info is available +- Accessing the info from nested exports is possible: i. e. `__webpack_exports_info__....used` +- Check whether exports can be mangled with `__webpack_exports_info__..canMangle` + + +```js title=Source +if (__webpack_exports_info__.someUsedExport.used) { } +if (__webpack_exports_info__.someUnusedExport.used) { } +``` + +```js title=Compiled +if (true) { +} +if (false) { +} +``` + + + ## Chunks ### \_\_webpack_chunkname\_\_ diff --git a/website/docs/zh/api/modules/module-variables.mdx b/website/docs/zh/api/modules/module-variables.mdx index 9f831b1911f..3f79312e265 100644 --- a/website/docs/zh/api/modules/module-variables.mdx +++ b/website/docs/zh/api/modules/module-variables.mdx @@ -494,6 +494,42 @@ __resourceQuery +### \_\_webpack_exports_info\_\_ + + + +在模块中通过 `__webpack_exports_info__` 可以读取如下导出信息: + +- `__webpack_exports_info__` 始终为 `true` +- 当导出未被引用时 `__webpack_exports_info__..used` 为 `false`,否则为 `true` +- `__webpack_exports_info__..useInfo` 值如下: + - `false`:导出未被引用 + - `true`:导出被引用 + - `null`:导出是否被引用依赖运行时条件判断 + - `undefined`:无法判断导出的引用情况 +- `__webpack_exports_info__..provideInfo` 值如下: + - `false`:导出未被提供 + - `true`:导出被提供 + - `null`:导出是否被提供依赖运行时条件判断 + - `undefined`:无法判断导出的提供情况 +- 支持访问导出的多级子属性:如 `__webpack_exports_info__....used` +- 通过 `__webpack_exports_info__..canMangle` 判断导出是否可被重命名 + + +```js title=源码 +if (__webpack_exports_info__.someUsedExport.used) { } +if (__webpack_exports_info__.someUnusedExport.used) { } +``` + +```js title=产物 +if (true) { +} +if (false) { +} +``` + + + ## Chunks ### \_\_webpack_chunkname\_\_