diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 36af31a5e85..8c4b6553741 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -1129,6 +1129,8 @@ export interface RawAssetGeneratorOptions { outputPath?: JsFilename publicPath?: "auto" | JsFilename dataUrl?: RawAssetGeneratorDataUrlOptions | ((source: Buffer, context: RawAssetGeneratorDataUrlFnCtx) => string) + experimentalLibReExport?: boolean + experimentalLibPreserveImport?: boolean } export interface RawAssetInlineGeneratorOptions { @@ -1153,6 +1155,8 @@ export interface RawAssetResourceGeneratorOptions { filename?: JsFilename outputPath?: JsFilename publicPath?: "auto" | JsFilename + experimentalLibPreserveImport?: boolean + experimentalLibReExport?: boolean } export interface RawBannerPluginOptions { diff --git a/crates/rspack_binding_options/src/options/raw_module/mod.rs b/crates/rspack_binding_options/src/options/raw_module/mod.rs index 6044dbe6421..b3241a1b702 100644 --- a/crates/rspack_binding_options/src/options/raw_module/mod.rs +++ b/crates/rspack_binding_options/src/options/raw_module/mod.rs @@ -504,6 +504,9 @@ pub struct RawAssetGeneratorOptions { ts_type = "RawAssetGeneratorDataUrlOptions | ((source: Buffer, context: RawAssetGeneratorDataUrlFnCtx) => string)" )] pub data_url: Option, + + pub experimental_lib_re_export: Option, + pub experimental_lib_preserve_import: Option, } impl From for AssetGeneratorOptions { @@ -516,6 +519,8 @@ impl From for AssetGeneratorOptions { data_url: value .data_url .map(|i| RawAssetGeneratorDataUrlWrapper(i).into()), + experimental_lib_re_export: value.experimental_lib_re_export, + experimental_lib_preserve_import: value.experimental_lib_preserve_import, } } } @@ -548,6 +553,9 @@ pub struct RawAssetResourceGeneratorOptions { pub output_path: Option, #[napi(ts_type = "\"auto\" | JsFilename")] pub public_path: Option, + + pub experimental_lib_preserve_import: Option, + pub experimental_lib_re_export: Option, } impl From for AssetResourceGeneratorOptions { @@ -557,6 +565,8 @@ impl From for AssetResourceGeneratorOptions { filename: value.filename.map(|i| i.into()), output_path: value.output_path.map(|i| i.into()), public_path: value.public_path.map(|i| i.into()), + experimental_lib_preserve_import: value.experimental_lib_preserve_import, + experimental_lib_re_export: value.experimental_lib_re_export, } } } diff --git a/crates/rspack_core/src/options/module.rs b/crates/rspack_core/src/options/module.rs index 9579dc6c5b1..81ab26ff1d1 100644 --- a/crates/rspack_core/src/options/module.rs +++ b/crates/rspack_core/src/options/module.rs @@ -383,6 +383,8 @@ pub struct AssetResourceGeneratorOptions { pub filename: Option, pub output_path: Option, pub public_path: Option, + pub experimental_lib_re_export: Option, + pub experimental_lib_preserve_import: Option, } #[cacheable] @@ -393,6 +395,8 @@ pub struct AssetGeneratorOptions { pub output_path: Option, pub public_path: Option, pub data_url: Option, + pub experimental_lib_re_export: Option, + pub experimental_lib_preserve_import: Option, } pub struct AssetGeneratorDataUrlFnCtx<'a> { diff --git a/crates/rspack_plugin_asset/src/lib.rs b/crates/rspack_plugin_asset/src/lib.rs index 6d981fc187c..3624269da9c 100644 --- a/crates/rspack_plugin_asset/src/lib.rs +++ b/crates/rspack_plugin_asset/src/lib.rs @@ -432,7 +432,6 @@ impl ParserAndGenerator for AssetParserAndGenerator { let exported_content = if parsed_asset_config.is_inline() { let resource_data: &ResourceData = normal_module.resource_resolved_data(); let data_url = module_generator_options.and_then(|x| x.asset_data_url()); - let encoded_source: String; if let Some(custom_data_url) = @@ -522,6 +521,59 @@ impl ParserAndGenerator for AssetParserAndGenerator { } else { unreachable!() }; + + let experimental_lib_preserve_import = module_generator_options + .and_then(|x| x.get_asset()) + .and_then(|x| x.experimental_lib_preserve_import) + .or_else(|| { + module_generator_options + .and_then(|x| x.get_asset_resource()) + .and_then(|x| x.experimental_lib_preserve_import) + }) + .unwrap_or(false); + let experimental_lib_re_export = module_generator_options + .and_then(|x| x.get_asset()) + .and_then(|x| x.experimental_lib_re_export) + .or_else(|| { + module_generator_options + .and_then(|x| x.get_asset_resource()) + .and_then(|x| x.experimental_lib_re_export) + }) + .unwrap_or(false); + + if experimental_lib_preserve_import || experimental_lib_re_export { + let Some(PublicPath::Auto) = module_generator_options.and_then(|x| x.asset_public_path()) + else { + return Err(error!( + "`experimentalLibPreserveImport` and `experimentalLibReExport` can only be used with `asset/resource` and `publicPath: 'auto'`" + )); + }; + + if let Some(ref mut scope) = generate_context.concatenation_scope { + scope.register_namespace_export(NAMESPACE_OBJECT_EXPORT); + return Ok(if experimental_lib_re_export { + RawStringSource::from(format!( + r#"import {NAMESPACE_OBJECT_EXPORT} from {exported_content}; +export default {NAMESPACE_OBJECT_EXPORT};"# + )) + .boxed() + } else { + RawStringSource::from(format!( + r#"import {NAMESPACE_OBJECT_EXPORT} from {exported_content};"# + )) + .boxed() + }); + } else { + generate_context + .runtime_requirements + .insert(RuntimeGlobals::MODULE); + return Ok( + RawStringSource::from(format!(r#"module.exports = require({exported_content})"#)) + .boxed(), + ); + } + } + if let Some(ref mut scope) = generate_context.concatenation_scope { scope.register_namespace_export(NAMESPACE_OBJECT_EXPORT); let supports_const = compilation.options.output.environment.supports_const(); diff --git a/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs index ceac4894f52..45651a133f6 100644 --- a/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs @@ -555,6 +555,9 @@ impl ModuleConcatenationPlugin { let box_module = module_graph .module_by_identifier(&root_module_id) .expect("should have module"); + let root_module_source_types = box_module.source_types(); + let is_root_module_asset_module = root_module_source_types.contains(&SourceType::Asset); + let root_module_ctxt = RootModuleContext { id: root_module_id, readable_identifier: box_module @@ -677,8 +680,26 @@ impl ModuleConcatenationPlugin { // .module_identifier_to_module // .remove(&root_module_id); // compilation.chunk_graph.clear + if is_root_module_asset_module { + chunk_graph.replace_module(&root_module_id, &new_module.id()); + chunk_graph.add_module(root_module_id); + for chunk_ukey in chunk_graph.get_module_chunks(new_module.id()).clone() { + let module = module_graph + .module_by_identifier(&root_module_id) + .expect("should exist module"); - chunk_graph.replace_module(&root_module_id, &new_module.id()); + let source_types = chunk_graph.get_chunk_module_source_types(&chunk_ukey, module); + let new_source_types = source_types + .iter() + .filter(|source_type| !matches!(source_type, SourceType::JavaScript)) + .copied() + .collect(); + chunk_graph.set_chunk_modules_source_types(&chunk_ukey, root_module_id, new_source_types); + chunk_graph.connect_chunk_and_module(chunk_ukey, root_module_id); + } + } else { + chunk_graph.replace_module(&root_module_id, &new_module.id()); + } module_graph.move_module_connections(&root_module_id, &new_module.id(), |c, dep| { let other_module = if *c.module_identifier() == root_module_id { diff --git a/packages/rspack/etc/core.api.md b/packages/rspack/etc/core.api.md index 44c0b52eaee..d10d634b613 100644 --- a/packages/rspack/etc/core.api.md +++ b/packages/rspack/etc/core.api.md @@ -203,6 +203,8 @@ export type AssetResourceGeneratorOptions = { filename?: Filename; outputPath?: AssetModuleOutputPath; publicPath?: PublicPath; + experimentalLibReExport?: boolean; + experimentalLibPreserveImport?: boolean; }; // @public (undocumented) diff --git a/packages/rspack/src/config/adapter.ts b/packages/rspack/src/config/adapter.ts index 516a1c48897..2b9004e72d4 100644 --- a/packages/rspack/src/config/adapter.ts +++ b/packages/rspack/src/config/adapter.ts @@ -816,7 +816,9 @@ function getRawAssetResourceGeneratorOptions( emit: options.emit, filename: options.filename, outputPath: options.outputPath, - publicPath: options.publicPath + publicPath: options.publicPath, + experimentalLibReExport: options.experimentalLibReExport, + experimentalLibPreserveImport: options.experimentalLibPreserveImport }; } diff --git a/packages/rspack/src/config/types.ts b/packages/rspack/src/config/types.ts index 879843399e4..4a85ad89842 100644 --- a/packages/rspack/src/config/types.ts +++ b/packages/rspack/src/config/types.ts @@ -1147,6 +1147,11 @@ export type AssetResourceGeneratorOptions = { /** This option determines the URL prefix of the referenced 'asset' or 'asset/resource'*/ publicPath?: PublicPath; + + /** */ + experimentalLibReExport?: boolean; + /** */ + experimentalLibPreserveImport?: boolean; }; /** Generator options for asset modules. */