diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index c0f8bc9ff3e5..3f3bdb2e1f4e 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -1128,6 +1128,8 @@ export interface RawAssetGeneratorOptions { filename?: JsFilename publicPath?: "auto" | JsFilename dataUrl?: RawAssetGeneratorDataUrlOptions | ((arg: RawAssetGeneratorDataUrlFnArgs) => string) + experimentalLibReExport?: boolean + experimentalLibPreserveImport?: boolean } export interface RawAssetInlineGeneratorOptions { @@ -1151,6 +1153,8 @@ export interface RawAssetResourceGeneratorOptions { emit?: boolean filename?: 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 baf6781e3981..e7907b302020 100644 --- a/crates/rspack_binding_options/src/options/raw_module/mod.rs +++ b/crates/rspack_binding_options/src/options/raw_module/mod.rs @@ -503,6 +503,9 @@ pub struct RawAssetGeneratorOptions { ts_type = "RawAssetGeneratorDataUrlOptions | ((arg: RawAssetGeneratorDataUrlFnArgs) => string)" )] pub data_url: Option, + + pub experimental_lib_re_export: Option, + pub experimental_lib_preserve_import: Option, } impl From for AssetGeneratorOptions { @@ -514,6 +517,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, } } } @@ -545,6 +550,9 @@ pub struct RawAssetResourceGeneratorOptions { pub filename: 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 { @@ -553,6 +561,8 @@ impl From for AssetResourceGeneratorOptions { emit: value.emit, filename: value.filename.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 b60cbfd28375..f609086ac2f4 100644 --- a/crates/rspack_core/src/options/module.rs +++ b/crates/rspack_core/src/options/module.rs @@ -371,6 +371,8 @@ pub struct AssetResourceGeneratorOptions { pub emit: Option, pub filename: Option, pub public_path: Option, + pub experimental_lib_re_export: Option, + pub experimental_lib_preserve_import: Option, } #[cacheable] @@ -380,6 +382,8 @@ pub struct AssetGeneratorOptions { pub filename: Option, pub public_path: Option, pub data_url: Option, + pub experimental_lib_re_export: Option, + pub experimental_lib_preserve_import: Option, } pub struct AssetGeneratorDataUrlFnArgs { diff --git a/crates/rspack_plugin_asset/src/lib.rs b/crates/rspack_plugin_asset/src/lib.rs index 1f5d1198e3f4..757f3d866c37 100644 --- a/crates/rspack_plugin_asset/src/lib.rs +++ b/crates/rspack_plugin_asset/src/lib.rs @@ -409,13 +409,13 @@ impl ParserAndGenerator for AssetParserAndGenerator { .as_normal_module() .expect("module should be a NormalModule in AssetParserAndGenerator"); let module_generator_options = normal_module.get_generator_options(); + dbg!(module_generator_options); let result = match generate_context.requested_source_type { SourceType::JavaScript => { 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) = self.get_data_url(resource_data, data_url, source) { @@ -498,6 +498,61 @@ 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); + + dbg!(experimental_lib_preserve_import, experimental_lib_re_export); + + 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(); @@ -621,6 +676,7 @@ async fn render_manifest( .get::() .expect("should have asset_info") .inner(); + dbg!(&asset_filename, &asset_info); RenderManifestEntry { source: source.clone(), filename: asset_filename.to_owned(), 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 ceac4894f526..0adca2d5762f 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,28 @@ 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()); + dbg!(&module, &new_module); + + 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 2f6de0d724d5..42b033edce52 100644 --- a/packages/rspack/etc/core.api.md +++ b/packages/rspack/etc/core.api.md @@ -199,6 +199,8 @@ export type AssetResourceGeneratorOptions = { emit?: boolean; filename?: Filename; 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 21c615fe0196..b36f22fb894d 100644 --- a/packages/rspack/src/config/adapter.ts +++ b/packages/rspack/src/config/adapter.ts @@ -813,7 +813,9 @@ function getRawAssetResourceGeneratorOptions( return { emit: options.emit, filename: options.filename, - 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 629d18f05eb3..243bf1da0dcb 100644 --- a/packages/rspack/src/config/types.ts +++ b/packages/rspack/src/config/types.ts @@ -1138,6 +1138,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. */