Skip to content

Commit

Permalink
feat: Module Federation, part 2, ContainerReferencePlugin (#4735)
Browse files Browse the repository at this point in the history
* wip

* fix

* fix

* simplify

* fix
  • Loading branch information
ahabhgk authored and JSerFeng committed Nov 30, 2023
1 parent e034b65 commit cc38fdb
Show file tree
Hide file tree
Showing 41 changed files with 1,187 additions and 71 deletions.
16 changes: 15 additions & 1 deletion crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,11 @@ export const enum BuiltinPluginName {
LimitChunkCountPlugin = 'LimitChunkCountPlugin',
WebWorkerTemplatePlugin = 'WebWorkerTemplatePlugin',
MergeDuplicateChunksPlugin = 'MergeDuplicateChunksPlugin',
ContainerPlugin = 'ContainerPlugin',
SplitChunksPlugin = 'SplitChunksPlugin',
OldSplitChunksPlugin = 'OldSplitChunksPlugin',
ContainerPlugin = 'ContainerPlugin',
ContainerReferencePlugin = 'ContainerReferencePlugin',
ModuleFederationRuntimePlugin = 'ModuleFederationRuntimePlugin',
HttpExternalsRspackPlugin = 'HttpExternalsRspackPlugin',
CopyRspackPlugin = 'CopyRspackPlugin',
HtmlRspackPlugin = 'HtmlRspackPlugin',
Expand Down Expand Up @@ -624,6 +626,12 @@ export interface RawContainerPluginOptions {
exposes: Array<RawExposeOptions>
}

export interface RawContainerReferencePluginOptions {
remoteType: string
remotes: Array<RawRemoteOptions>
shareScope?: string
}

export interface RawCopyGlobOptions {
caseSensitiveMatch?: boolean
dot?: boolean
Expand Down Expand Up @@ -1022,6 +1030,12 @@ export interface RawRelayConfig {
language: 'javascript' | 'typescript' | 'flow'
}

export interface RawRemoteOptions {
key: string
external: Array<string>
shareScope: string
}

export interface RawResolveOptions {
preferRelative?: boolean
extensions?: Array<string>
Expand Down
34 changes: 26 additions & 8 deletions crates/rspack_binding_options/src/options/raw_builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ use napi::{
};
use napi_derive::napi;
use rspack_core::{
mf::ContainerPlugin, BoxPlugin, Define, DefinePlugin, PluginExt, Provide, ProvidePlugin,
mf::{
container_plugin::ContainerPlugin, container_reference_plugin::ContainerReferencePlugin,
module_federation_runtime_plugin::ModuleFederationRuntimePlugin,
},
BoxPlugin, Define, DefinePlugin, PluginExt, Provide, ProvidePlugin,
};
use rspack_error::Result;
use rspack_napi_shared::NapiResultExt;
Expand All @@ -38,6 +42,7 @@ use rspack_plugin_swc_js_minimizer::SwcJsMinimizerRspackPlugin;
use rspack_plugin_wasm::enable_wasm_loading_plugin;
use rspack_plugin_web_worker_template::web_worker_template_plugin;

use self::raw_mf::RawContainerReferencePluginOptions;
pub use self::{
raw_banner::RawBannerPluginOptions, raw_copy::RawCopyRspackPluginOptions,
raw_html::RawHtmlRspackPluginOptions, raw_limit_chunk_count::RawLimitChunkCountPluginOptions,
Expand Down Expand Up @@ -71,9 +76,11 @@ pub enum BuiltinPluginName {
LimitChunkCountPlugin,
WebWorkerTemplatePlugin,
MergeDuplicateChunksPlugin,
ContainerPlugin,
SplitChunksPlugin,
OldSplitChunksPlugin,
ContainerPlugin,
ContainerReferencePlugin,
ModuleFederationRuntimePlugin,

// rspack specific plugins
HttpExternalsRspackPlugin,
Expand Down Expand Up @@ -180,12 +187,6 @@ impl RawOptionsApply for BuiltinPlugin {
BuiltinPluginName::MergeDuplicateChunksPlugin => {
plugins.push(MergeDuplicateChunksPlugin.boxed());
}
BuiltinPluginName::ContainerPlugin => {
plugins.push(
ContainerPlugin::new(downcast_into::<RawContainerPluginOptions>(self.options)?.into())
.boxed(),
);
}
BuiltinPluginName::SplitChunksPlugin => {
use rspack_plugin_split_chunks_new::SplitChunksPlugin;
let options = downcast_into::<RawSplitChunksOptions>(self.options)?.into();
Expand All @@ -196,6 +197,23 @@ impl RawOptionsApply for BuiltinPlugin {
let options = downcast_into::<RawSplitChunksOptions>(self.options)?.into();
plugins.push(SplitChunksPlugin::new(options).boxed());
}
BuiltinPluginName::ContainerPlugin => {
plugins.push(
ContainerPlugin::new(downcast_into::<RawContainerPluginOptions>(self.options)?.into())
.boxed(),
);
}
BuiltinPluginName::ContainerReferencePlugin => {
plugins.push(
ContainerReferencePlugin::new(
downcast_into::<RawContainerReferencePluginOptions>(self.options)?.into(),
)
.boxed(),
);
}
BuiltinPluginName::ModuleFederationRuntimePlugin => {
plugins.push(ModuleFederationRuntimePlugin::default().boxed())
}

// rspack specific plugins
BuiltinPluginName::HttpExternalsRspackPlugin => {
Expand Down
43 changes: 42 additions & 1 deletion crates/rspack_binding_options/src/options/raw_builtins/raw_mf.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use napi_derive::napi;
use rspack_core::mf::{ContainerPluginOptions, ExposeOptions};
use rspack_core::mf::{
container_plugin::{ContainerPluginOptions, ExposeOptions},
container_reference_plugin::{ContainerReferencePluginOptions, RemoteOptions},
};

use crate::RawLibraryOptions;

Expand Down Expand Up @@ -46,3 +49,41 @@ impl From<RawExposeOptions> for (String, ExposeOptions) {
)
}
}

#[derive(Debug)]
#[napi(object)]
pub struct RawContainerReferencePluginOptions {
pub remote_type: String,
pub remotes: Vec<RawRemoteOptions>,
pub share_scope: Option<String>,
}

impl From<RawContainerReferencePluginOptions> for ContainerReferencePluginOptions {
fn from(value: RawContainerReferencePluginOptions) -> Self {
Self {
remote_type: value.remote_type,
remotes: value.remotes.into_iter().map(|e| e.into()).collect(),
share_scope: value.share_scope,
}
}
}

#[derive(Debug)]
#[napi(object)]
pub struct RawRemoteOptions {
pub key: String,
pub external: Vec<String>,
pub share_scope: String,
}

impl From<RawRemoteOptions> for (String, RemoteOptions) {
fn from(value: RawRemoteOptions) -> Self {
(
value.key,
RemoteOptions {
external: value.external,
share_scope: value.share_scope,
},
)
}
}
2 changes: 1 addition & 1 deletion crates/rspack_core/src/compiler/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl WorkerTask for FactorizeTask {
.split_into_parts()
}
DependencyType::ContainerEntry => {
let factory = crate::mf::ContainerEntryModuleFactory;
let factory = crate::mf::container_entry_module_factory::ContainerEntryModuleFactory;
factory
.create(ModuleFactoryCreateData {
resolve_options: self.resolve_options,
Expand Down
3 changes: 3 additions & 0 deletions crates/rspack_core/src/dependency/dependency_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub enum DependencyType {
ContainerExposed,
/// container entry,
ContainerEntry,
/// remote to external,
RemoteToExternal,
Custom(Box<str>), // TODO it will increase large layout size
}

Expand Down Expand Up @@ -107,6 +109,7 @@ impl DependencyType {
DependencyType::ImportMetaContext => Cow::Borrowed("import.meta context"),
DependencyType::ContainerExposed => Cow::Borrowed("container exposed"),
DependencyType::ContainerEntry => Cow::Borrowed("container entry"),
DependencyType::RemoteToExternal => Cow::Borrowed("remote to external"),
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion crates/rspack_core/src/dependency/runtime_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,12 @@ pub fn module_namespace_promise(
}
runtime_requirements.insert(RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT);
if matches!(
compilation.module_graph.is_async(&module.identifier()),
compilation.module_graph.is_async(
compilation
.module_graph
.module_identifier_by_dependency_id(dep_id)
.expect("should have module")
),
Some(true)
) {
if let Some(header) = header {
Expand Down
47 changes: 41 additions & 6 deletions crates/rspack_core/src/external_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_hash::FxHashMap as HashMap;
use serde::Serialize;

use crate::{
property_access,
extract_url_and_global, property_access,
rspack_sources::{BoxSource, RawSource, Source, SourceExt},
to_identifier, AsyncDependenciesBlockIdentifier, BuildContext, BuildInfo, BuildMetaExportsType,
BuildResult, ChunkInitFragments, ChunkUkey, CodeGenerationDataUrl, CodeGenerationResult,
Expand Down Expand Up @@ -134,12 +134,45 @@ impl ExternalModule {
)
}

fn get_source_for_script_external(
&self,
url_and_global: &ExternalRequestValue,
runtime_requirements: &mut RuntimeGlobals,
) -> Result<String> {
let url_and_global = extract_url_and_global(url_and_global.primary())?;
runtime_requirements.insert(RuntimeGlobals::LOAD_SCRIPT);
Ok(format!(
r#"
var __webpack_error__ = new Error();
module.exports = new Promise(function(resolve, reject) {{
if(typeof {global} !== "undefined") return resolve();
{load_script}({url_str}, function(event) {{
if(typeof {global} !== "undefined") return resolve();
var errorType = event && (event.type === 'load' ? 'missing' : event.type);
var realSrc = event && event.target && event.target.src;
__webpack_error__.message = 'Loading script failed.\n(' + errorType + ': ' + realSrc + ')';
__webpack_error__.name = 'ScriptExternalLoadError';
__webpack_error__.type = errorType;
__webpack_error__.request = realSrc;
reject(__webpack_error__);
}}, {global_str});
}}).then(function() {{ return {global}; }});
"#,
global = url_and_global.global,
global_str =
serde_json::to_string(url_and_global.global).map_err(|e| internal_error!(e.to_string()))?,
url_str =
serde_json::to_string(url_and_global.url).map_err(|e| internal_error!(e.to_string()))?,
load_script = RuntimeGlobals::LOAD_SCRIPT.name()
))
}

fn get_source(
&self,
compilation: &Compilation,
request: Option<&ExternalRequestValue>,
external_type: &ExternalType,
) -> (BoxSource, ChunkInitFragments, RuntimeGlobals) {
) -> Result<(BoxSource, ChunkInitFragments, RuntimeGlobals)> {
let mut chunk_init_fragments: ChunkInitFragments = Default::default();
let mut runtime_requirements: RuntimeGlobals = Default::default();
let source = match self.external_type.as_str() {
Expand Down Expand Up @@ -231,15 +264,17 @@ impl ExternalModule {
self.get_source_for_import(request, compilation)
}
}
// TODO "script"
"script" if let Some(request) = request => {
self.get_source_for_script_external(request, &mut runtime_requirements)?
}
_ => "".to_string(),
};
runtime_requirements.insert(RuntimeGlobals::MODULE);
(
Ok((
RawSource::from(source).boxed(),
chunk_init_fragments,
runtime_requirements,
)
))
}
}

Expand Down Expand Up @@ -379,7 +414,7 @@ impl Module for ExternalModule {
}
_ => {
let (source, chunk_init_fragments, runtime_requirements) =
self.get_source(compilation, request, external_type);
self.get_source(compilation, request, external_type)?;
cgr.add(SourceType::JavaScript, source);
cgr.chunk_init_fragments = chunk_init_fragments;
cgr.runtime_requirements.insert(runtime_requirements);
Expand Down
7 changes: 7 additions & 0 deletions crates/rspack_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![feature(iter_intersperse)]
#![feature(box_patterns)]
#![feature(anonymous_lifetime_in_impl_trait)]
#![feature(hash_raw_entry)]

use std::sync::atomic::AtomicBool;
use std::{fmt, sync::Arc};
Expand Down Expand Up @@ -95,6 +96,8 @@ pub enum SourceType {
Css,
Wasm,
Asset,
Remote,
ShareInit,
#[default]
Unknown,
}
Expand All @@ -106,6 +109,8 @@ impl std::fmt::Display for SourceType {
SourceType::Css => write!(f, "css"),
SourceType::Wasm => write!(f, "wasm"),
SourceType::Asset => write!(f, "asset"),
SourceType::Remote => write!(f, "remote"),
SourceType::ShareInit => write!(f, "share-init"),
SourceType::Unknown => write!(f, "unknown"),
}
}
Expand All @@ -132,6 +137,7 @@ pub enum ModuleType {
AssetSource,
Asset,
Runtime,
Remote,
}

impl ModuleType {
Expand Down Expand Up @@ -216,6 +222,7 @@ impl ModuleType {
ModuleType::AssetResource => "asset/resource",
ModuleType::AssetInline => "asset/inline",
ModuleType::Runtime => "runtime",
ModuleType::Remote => "remote-module",
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::ExposeOptions;
use super::container_plugin::ExposeOptions;
use crate::{
AsContextDependency, AsDependencyTemplate, Dependency, DependencyCategory, DependencyId,
DependencyType, ModuleDependency,
Expand Down Expand Up @@ -52,12 +52,6 @@ impl ModuleDependency for ContainerEntryDependency {
fn request(&self) -> &str {
&self.resource_identifier
}

fn user_request(&self) -> &str {
&self.resource_identifier
}

fn set_request(&mut self, _request: String) {}
}

impl AsContextDependency for ContainerEntryDependency {}
Expand Down
11 changes: 7 additions & 4 deletions crates/rspack_core/src/mf/container/container_entry_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use rspack_hash::RspackHash;
use rspack_identifier::{Identifiable, Identifier};
use rspack_sources::{RawSource, Source, SourceExt};

use super::{container_exposed_dependency::ContainerExposedDependency, ExposeOptions};
use super::{
container_exposed_dependency::ContainerExposedDependency, container_plugin::ExposeOptions,
};
use crate::{
basic_function, block_promise, module_raw, returning_function, throw_missing_module_error_block,
AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BuildContext, BuildInfo, BuildMeta,
Expand All @@ -20,13 +22,14 @@ pub struct ContainerEntryModule {
blocks: Vec<AsyncDependenciesBlockIdentifier>,
dependencies: Vec<DependencyId>,
identifier: ModuleIdentifier,
name: String,
lib_ident: String,
exposes: Vec<(String, ExposeOptions)>,
share_scope: String,
}

impl ContainerEntryModule {
pub fn new(name: String, exposes: Vec<(String, ExposeOptions)>, share_scope: String) -> Self {
let lib_ident = format!("webpack/container/entry/{}", &name);
Self {
blocks: Vec::new(),
dependencies: Vec::new(),
Expand All @@ -35,7 +38,7 @@ impl ContainerEntryModule {
share_scope,
serde_json::to_string(&exposes).expect("should able to json to_string")
)),
name,
lib_ident,
exposes,
share_scope,
}
Expand Down Expand Up @@ -89,7 +92,7 @@ impl Module for ContainerEntryModule {
}

fn lib_ident(&self, _options: LibIdentOptions) -> Option<Cow<str>> {
Some(format!("webpack/container/entry/{}", self.name).into())
Some(self.lib_ident.as_str().into())
}

async fn build(
Expand Down
Loading

0 comments on commit cc38fdb

Please sign in to comment.