Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Module Federation, part 1, ContainerPlugin #4642

Merged
merged 1 commit into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const enum BuiltinPluginName {
LimitChunkCountPlugin = 'LimitChunkCountPlugin',
WebWorkerTemplatePlugin = 'WebWorkerTemplatePlugin',
MergeDuplicateChunksPlugin = 'MergeDuplicateChunksPlugin',
ContainerPlugin = 'ContainerPlugin',
HttpExternalsRspackPlugin = 'HttpExternalsRspackPlugin',
CopyRspackPlugin = 'CopyRspackPlugin',
HtmlRspackPlugin = 'HtmlRspackPlugin',
Expand Down Expand Up @@ -611,6 +612,15 @@ export interface RawChunkOptionNameCtx {
module: JsModule
}

export interface RawContainerPluginOptions {
name: string
shareScope: string
library: RawLibraryOptions
runtime?: string
filename?: string
exposes: Array<RawExposeOptions>
}

export interface RawCopyGlobOptions {
caseSensitiveMatch?: boolean
dot?: boolean
Expand Down Expand Up @@ -685,6 +695,12 @@ export interface RawExperiments {
rspackFuture: RawRspackFuture
}

export interface RawExposeOptions {
key: string
name?: string
import: Array<string>
}

export interface RawExternalItemFnCtx {
request: string
context: string
Expand Down
14 changes: 12 additions & 2 deletions crates/rspack_binding_options/src/options/raw_builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod raw_banner;
mod raw_copy;
mod raw_html;
mod raw_limit_chunk_count;
mod raw_mf;
mod raw_progress;
mod raw_swc_js_minimizer;
mod raw_to_be_deprecated;
Expand All @@ -11,7 +12,9 @@ use napi::{
JsUnknown,
};
use napi_derive::napi;
use rspack_core::{BoxPlugin, Define, DefinePlugin, PluginExt, Provide, ProvidePlugin};
use rspack_core::{
mf::ContainerPlugin, BoxPlugin, Define, DefinePlugin, PluginExt, Provide, ProvidePlugin,
};
use rspack_error::Result;
use rspack_napi_shared::NapiResultExt;
use rspack_plugin_banner::BannerPlugin;
Expand All @@ -38,7 +41,7 @@ use rspack_plugin_web_worker_template::web_worker_template_plugin;
pub use self::{
raw_banner::RawBannerPluginOptions, raw_copy::RawCopyRspackPluginOptions,
raw_html::RawHtmlRspackPluginOptions, raw_limit_chunk_count::RawLimitChunkCountPluginOptions,
raw_progress::RawProgressPluginOptions,
raw_mf::RawContainerPluginOptions, raw_progress::RawProgressPluginOptions,
raw_swc_js_minimizer::RawSwcJsMinimizerRspackPluginOptions,
};
use crate::{
Expand Down Expand Up @@ -68,6 +71,7 @@ pub enum BuiltinPluginName {
LimitChunkCountPlugin,
WebWorkerTemplatePlugin,
MergeDuplicateChunksPlugin,
ContainerPlugin,

// rspack specific plugins
HttpExternalsRspackPlugin,
Expand Down Expand Up @@ -174,6 +178,12 @@ impl RawOptionsApply for BuiltinPlugin {
BuiltinPluginName::MergeDuplicateChunksPlugin => {
plugins.push(MergeDuplicateChunksPlugin.boxed());
}
BuiltinPluginName::ContainerPlugin => {
plugins.push(
ContainerPlugin::new(downcast_into::<RawContainerPluginOptions>(self.options)?.into())
.boxed(),
);
}

// rspack specific plugins
BuiltinPluginName::HttpExternalsRspackPlugin => {
Expand Down
48 changes: 48 additions & 0 deletions crates/rspack_binding_options/src/options/raw_builtins/raw_mf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use napi_derive::napi;
use rspack_core::mf::{ContainerPluginOptions, ExposeOptions};

use crate::RawLibraryOptions;

#[derive(Debug)]
#[napi(object)]
pub struct RawContainerPluginOptions {
pub name: String,
pub share_scope: String,
pub library: RawLibraryOptions,
pub runtime: Option<String>,
pub filename: Option<String>,
pub exposes: Vec<RawExposeOptions>,
}

impl From<RawContainerPluginOptions> for ContainerPluginOptions {
fn from(value: RawContainerPluginOptions) -> Self {
Self {
name: value.name,
share_scope: value.share_scope,
library: value.library.into(),
runtime: value.runtime,
filename: value.filename.map(|f| f.into()),
exposes: value.exposes.into_iter().map(|e| e.into()).collect(),
}
}
}

#[derive(Debug, Clone)]
#[napi(object)]
pub struct RawExposeOptions {
pub key: String,
pub name: Option<String>,
pub import: Vec<String>,
}

impl From<RawExposeOptions> for (String, ExposeOptions) {
fn from(value: RawExposeOptions) -> Self {
(
value.key,
ExposeOptions {
name: value.name,
import: value.import,
},
)
}
}
13 changes: 5 additions & 8 deletions crates/rspack_core/src/build_chunk_graph/code_splitter.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::sync::Arc;

use anyhow::anyhow;
use rspack_error::{internal_error, Result};
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};

use super::remove_parent_modules::RemoveParentModulesContext;
use crate::{
AsyncDependenciesBlockIdentifier, BoxDependency, ChunkGroup, ChunkGroupInfo, ChunkGroupKind,
ChunkGroupOptions, ChunkGroupUkey, ChunkLoading, ChunkUkey, Compilation, DependenciesBlock,
GroupOptions, Logger, ModuleGraphConnection, ModuleIdentifier, RuntimeSpec, IS_NEW_TREESHAKING,
get_entry_runtime, AsyncDependenciesBlockIdentifier, BoxDependency, ChunkGroup, ChunkGroupInfo,
ChunkGroupKind, ChunkGroupOptions, ChunkGroupUkey, ChunkLoading, ChunkUkey, Compilation,
DependenciesBlock, GroupOptions, Logger, ModuleGraphConnection, ModuleIdentifier, RuntimeSpec,
IS_NEW_TREESHAKING,
};

pub(super) struct CodeSplitter<'me> {
Expand Down Expand Up @@ -95,9 +94,7 @@ impl<'me> CodeSplitter<'me> {
let mut entrypoint = ChunkGroup::new(
ChunkGroupKind::new_entrypoint(true, Box::new(options.clone())),
ChunkGroupInfo {
runtime: HashSet::from_iter([Arc::from(
options.runtime.clone().unwrap_or_else(|| name.to_string()),
)]),
runtime: get_entry_runtime(name, options),
chunk_loading: !matches!(
options
.chunk_loading
Expand Down
4 changes: 3 additions & 1 deletion crates/rspack_core/src/chunk_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,12 @@ impl ChunkGroupKind {
}
}

pub type EntryRuntime = String;

#[derive(Debug, Default, Clone)]
pub struct EntryOptions {
pub name: Option<String>,
pub runtime: Option<String>,
pub runtime: Option<EntryRuntime>,
pub chunk_loading: Option<ChunkLoading>,
pub async_chunks: Option<bool>,
pub public_path: Option<PublicPath>,
Expand Down
33 changes: 21 additions & 12 deletions crates/rspack_core/src/compiler/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ use crate::{
ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkHashArgs, ChunkKind, ChunkUkey, CleanQueue,
CleanTask, CleanTaskResult, CodeGenerationResult, CodeGenerationResults, CompilationLogger,
CompilationLogging, CompilerOptions, ContentHashArgs, ContextDependency, DependencyId,
DependencyParents, Entry, EntryData, EntryOptions, Entrypoint, FactorizeQueue, FactorizeTask,
FactorizeTaskResult, Filename, Logger, Module, ModuleGraph, ModuleIdentifier, ModuleProfile,
ModuleType, PathData, ProcessAssetsArgs, ProcessDependenciesQueue, ProcessDependenciesResult,
ProcessDependenciesTask, RenderManifestArgs, Resolve, ResolverFactory, RuntimeGlobals,
RuntimeModule, RuntimeSpec, SharedPluginDriver, SourceType, Stats, TaskResult, WorkerTask,
DependencyParents, DependencyType, Entry, EntryData, EntryOptions, Entrypoint, FactorizeQueue,
FactorizeTask, FactorizeTaskResult, Filename, Logger, Module, ModuleGraph, ModuleIdentifier,
ModuleProfile, ModuleType, PathData, ProcessAssetsArgs, ProcessDependenciesQueue,
ProcessDependenciesResult, ProcessDependenciesTask, RenderManifestArgs, Resolve, ResolverFactory,
RuntimeGlobals, RuntimeModule, RuntimeSpec, SharedPluginDriver, SourceType, Stats, TaskResult,
WorkerTask,
};
use crate::{tree_shaking::visitor::OptimizeAnalyzeResult, Context};

Expand Down Expand Up @@ -172,19 +173,21 @@ impl Compilation {
}
}

pub fn add_entry(&mut self, entry: DependencyId, options: EntryOptions) {
pub fn add_entry(&mut self, entry: BoxDependency, options: EntryOptions) {
let entry_id = *entry.id();
self.module_graph.add_dependency(entry);
if let Some(name) = options.name.clone() {
if let Some(data) = self.entries.get_mut(&name) {
data.dependencies.push(entry);
data.dependencies.push(entry_id);
} else {
let data = EntryData {
dependencies: vec![entry],
dependencies: vec![entry_id],
options,
};
self.entries.insert(name, data);
}
} else {
self.global_entry.dependencies.push(entry);
self.global_entry.dependencies.push(entry_id);
}
}

Expand Down Expand Up @@ -939,11 +942,13 @@ impl Compilation {
if self.options.builtins.tree_shaking.enable() {
self.bailout_module_identifiers = self
.module_graph
.modules()
.dependencies()
.values()
.par_bridge()
.filter_map(|module| {
if module.as_context_module().is_some() {
.filter_map(|dep| {
if dep.as_context_dependency().is_some()
&& let Some(module) = self.module_graph.get_module(dep.id())
{
let mut values = vec![(module.identifier(), BailoutFlag::CONTEXT_MODULE)];
if let Some(dependencies) = self
.module_graph
Expand All @@ -960,6 +965,10 @@ impl Compilation {
}

Some(values)
} else if matches!(dep.dependency_type(), DependencyType::ContainerExposed)
&& let Some(module) = self.module_graph.get_module(dep.id())
{
Some(vec![(module.identifier(), BailoutFlag::CONTAINER_EXPOSED)])
} else {
None
}
Expand Down
11 changes: 11 additions & 0 deletions crates/rspack_core/src/compiler/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ impl WorkerTask for FactorizeTask {
.await?
.split_into_parts()
}
DependencyType::ContainerEntry => {
let factory = crate::mf::ContainerEntryModuleFactory;
factory
.create(ModuleFactoryCreateData {
resolve_options: self.resolve_options,
context,
dependency,
})
.await?
.split_into_parts()
}
_ => {
assert!(dependency.as_context_dependency().is_none());
let factory = NormalModuleFactory::new(
Expand Down
11 changes: 11 additions & 0 deletions crates/rspack_core/src/dependencies_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ impl AsyncDependenciesBlockIdentifier {
pub fn new(from: ModuleIdentifier, modifier: Ustr) -> Self {
Self { from, modifier }
}

pub fn get<'a>(&self, compilation: &'a Compilation) -> Option<&'a AsyncDependenciesBlock> {
compilation.module_graph.block_by_id(self)
}

pub fn expect_get<'a>(&self, compilation: &'a Compilation) -> &'a AsyncDependenciesBlock {
compilation
.module_graph
.block_by_id(self)
.expect("should have block")
}
}

#[derive(Debug, Clone)]
Expand Down
6 changes: 6 additions & 0 deletions crates/rspack_core/src/dependency/dependency_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ pub enum DependencyType {
WasmExportImported,
/// static exports
StaticExports,
/// container exposed
ContainerExposed,
/// container entry,
ContainerEntry,
Custom(Box<str>), // TODO it will increase large layout size
}

Expand Down Expand Up @@ -101,6 +105,8 @@ impl DependencyType {
DependencyType::ExportInfoApi => Cow::Borrowed("export info api"),
// TODO: mode
DependencyType::ImportMetaContext => Cow::Borrowed("import.meta context"),
DependencyType::ContainerExposed => Cow::Borrowed("container exposed"),
DependencyType::ContainerEntry => Cow::Borrowed("container entry"),
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions crates/rspack_core/src/dependency/runtime_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,11 @@ pub fn weak_error(request: &str) -> String {
let msg = format!("Module is not available (weak dependency), request is {request}.");
format!("var e = new Error('{msg}'); e.code = 'MODULE_NOT_FOUND'; throw e;")
}

pub fn returning_function(return_value: &str, args: &str) -> String {
format!("function({args}) {{ return {return_value}; }}")
}

pub fn basic_function(args: &str, body: &str) -> String {
format!("function({args}) {{\n{body}\n}}")
}
1 change: 1 addition & 0 deletions crates/rspack_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::sync::atomic::AtomicBool;
use std::{fmt, sync::Arc};
mod dependencies_block;
pub mod mf;
pub use dependencies_block::{
AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, DependenciesBlock,
};
Expand Down
64 changes: 64 additions & 0 deletions crates/rspack_core/src/mf/container/container_entry_dependency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use super::ExposeOptions;
use crate::{
AsContextDependency, AsDependencyTemplate, Dependency, DependencyCategory, DependencyId,
DependencyType, ModuleDependency,
};

#[derive(Debug, Clone)]
pub struct ContainerEntryDependency {
id: DependencyId,
pub name: String,
pub exposes: Vec<(String, ExposeOptions)>,
pub share_scope: String,
resource_identifier: String,
}

impl ContainerEntryDependency {
pub fn new(name: String, exposes: Vec<(String, ExposeOptions)>, share_scope: String) -> Self {
let resource_identifier = format!("container-entry-{}", &name);
Self {
id: DependencyId::new(),
name,
exposes,
share_scope,
resource_identifier,
}
}
}

impl Dependency for ContainerEntryDependency {
fn dependency_debug_name(&self) -> &'static str {
"ContainerEntryDependency"
}

fn id(&self) -> &DependencyId {
&self.id
}

fn category(&self) -> &DependencyCategory {
&DependencyCategory::Esm
}

fn dependency_type(&self) -> &DependencyType {
&DependencyType::ContainerEntry
}

fn resource_identifier(&self) -> Option<&str> {
Some(&self.resource_identifier)
}
}

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 {}
impl AsDependencyTemplate for ContainerEntryDependency {}
Loading
Loading