Skip to content

Commit

Permalink
feat(modern-module): make dynamic import runtime-less
Browse files Browse the repository at this point in the history
  • Loading branch information
fi3ework committed Sep 2, 2024
1 parent 01a460d commit cdb0cb7
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions crates/rspack_core/src/dependencies_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ impl DependenciesBlock for AsyncDependenciesBlock {
}
}

impl AsyncDependenciesBlock {
pub fn remove_dependency_id(&mut self, dependency: DependencyId) {
self.dependency_ids.retain(|dep| dep != &dependency);
}
}

#[derive(Debug, Error, Diagnostic)]
#[diagnostic(code(AsyncDependencyToInitialChunkError))]
#[error("It's not allowed to load an initial chunk on demand. The chunk name \"{0}\" is already used by an entrypoint.")]
Expand Down
6 changes: 3 additions & 3 deletions crates/rspack_core/src/external_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub enum ExternalRequest {

#[derive(Debug, Clone)]
pub struct ExternalRequestValue {
primary: String,
pub primary: String,
rest: Option<Vec<String>>,
}

Expand Down Expand Up @@ -121,9 +121,9 @@ pub struct ExternalModule {
blocks: Vec<AsyncDependenciesBlockIdentifier>,
id: Identifier,
pub request: ExternalRequest,
external_type: ExternalType,
pub external_type: ExternalType,
/// Request intended by user (without loaders from config)
user_request: String,
pub user_request: String,
factory_meta: Option<FactoryMeta>,
build_info: Option<BuildInfo>,
build_meta: Option<BuildMeta>,
Expand Down
16 changes: 16 additions & 0 deletions crates/rspack_core/src/module_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,22 @@ impl<'a> ModuleGraph<'a> {
.map(|b| &**b)
}

pub fn block_by_id_mut(
&mut self,
block_id: &AsyncDependenciesBlockIdentifier,
) -> Option<&mut Box<AsyncDependenciesBlock>> {
self
.loop_partials_mut(
|p| p.blocks.contains_key(block_id),
|p, search_result| {
p.blocks.insert(*block_id, search_result);
},
|p| p.blocks.get(block_id).cloned(),
|p| p.blocks.get_mut(block_id),
)?
.as_mut()
}

pub fn block_by_id_expect(
&self,
block_id: &AsyncDependenciesBlockIdentifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ pub fn create_import_dependency_referenced_exports(
#[derive(Debug, Clone)]
pub struct ImportDependency {
id: DependencyId,
request: Atom,
range: RealDependencyLocation,
pub request: Atom,
pub range: RealDependencyLocation,
referenced_exports: Option<Vec<Atom>>,
attributes: Option<ImportAttributes>,
resource_identifier: String,
Expand Down
12 changes: 12 additions & 0 deletions crates/rspack_plugin_library/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ rspack_util = { version = "0.1.0", path = "../rspack_util" }
rustc-hash = { workspace = true }
serde_json = { workspace = true }
tracing = { workspace = true }
swc_core = { workspace = true, features = [
"__parser",
"__utils",
"common_sourcemap",
"ecma_preset_env",
"ecma_transforms_optimization",
"ecma_transforms_module",
"ecma_transforms_compat",
"ecma_transforms_typescript",
"base",
"ecma_quote",
] }

[package.metadata.cargo-shear]
ignored = ["tracing"]
1 change: 1 addition & 0 deletions crates/rspack_plugin_library/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod amd_library_plugin;
mod assign_library_plugin;
mod export_property_library_plugin;
mod modern_module;
mod modern_module_library_plugin;
mod module_library_plugin;
mod system_library_plugin;
Expand Down
127 changes: 127 additions & 0 deletions crates/rspack_plugin_library/src/modern_module/dependency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use rspack_core::{AsContextDependency, Dependency};
use rspack_core::{
Compilation, DependencyType, ErrorSpan, ExternalRequest, ExternalType, ImportAttributes,
RealDependencyLocation, RuntimeSpec,
};
use rspack_core::{DependencyCategory, DependencyId, DependencyTemplate};
use rspack_core::{ModuleDependency, TemplateContext, TemplateReplaceSource};
use rspack_plugin_javascript::dependency::create_resource_identifier_for_esm_dependency;
use swc_core::ecma::atoms::Atom;

#[derive(Debug, Clone)]
pub struct ModernModuleImportDependency {
id: DependencyId,
request: Atom,
target_request: ExternalRequest,
external_type: ExternalType,
range: RealDependencyLocation,
attributes: Option<ImportAttributes>,
resource_identifier: String,
}

impl ModernModuleImportDependency {
pub fn new(
request: Atom,
target_request: ExternalRequest,
external_type: ExternalType,
range: RealDependencyLocation,
attributes: Option<ImportAttributes>,
) -> Self {
let resource_identifier =
create_resource_identifier_for_esm_dependency(request.as_str(), attributes.as_ref());
Self {
request,
target_request,
external_type,
range,
id: DependencyId::new(),
attributes,
resource_identifier,
}
}
}

impl Dependency for ModernModuleImportDependency {
fn id(&self) -> &DependencyId {
&self.id
}

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

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

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

fn get_attributes(&self) -> Option<&ImportAttributes> {
self.attributes.as_ref()
}

fn span(&self) -> Option<ErrorSpan> {
Some(ErrorSpan::new(self.range.start, self.range.end))
}

fn could_affect_referencing_module(&self) -> rspack_core::AffectType {
rspack_core::AffectType::True
}
}

impl ModuleDependency for ModernModuleImportDependency {
fn request(&self) -> &str {
&self.request
}

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

fn set_request(&mut self, request: String) {
self.request = request.into();
}
}

impl DependencyTemplate for ModernModuleImportDependency {
fn apply(
&self,
source: &mut TemplateReplaceSource,
_code_generatable_context: &mut TemplateContext,
) {
let request_and_external_type = match &self.target_request {
ExternalRequest::Single(request) => (Some(request), &self.external_type),
ExternalRequest::Map(map) => (map.get(&self.external_type), &self.external_type),
};

if let Some(request_and_external_type) = request_and_external_type.0 {
source.replace(
self.range.start,
self.range.end,
format!(
"import({})",
serde_json::to_string(request_and_external_type.primary())
.expect("invalid json to_string")
)
.as_str(),
None,
);
}
}

fn dependency_id(&self) -> Option<DependencyId> {
Some(self.id)
}

fn update_hash(
&self,
_hasher: &mut dyn std::hash::Hasher,
_compilation: &Compilation,
_runtime: Option<&RuntimeSpec>,
) {
}
}

impl AsContextDependency for ModernModuleImportDependency {}
3 changes: 3 additions & 0 deletions crates/rspack_plugin_library/src/modern_module/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod dependency;

pub use dependency::*;
88 changes: 85 additions & 3 deletions crates/rspack_plugin_library/src/modern_module_library_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ use std::hash::Hash;
use rspack_core::rspack_sources::{ConcatSource, RawSource, SourceExt};
use rspack_core::{
merge_runtime, to_identifier, ApplyContext, ChunkUkey, CodeGenerationExportsFinalNames,
Compilation, CompilationOptimizeChunkModules, CompilationParams, CompilerCompilation,
CompilerOptions, ConcatenatedModule, ConcatenatedModuleExportsDefinitions, LibraryOptions,
ModuleIdentifier, Plugin, PluginContext,
Compilation, CompilationFinishModules, CompilationOptimizeChunkModules, CompilationParams,
CompilerCompilation, CompilerOptions, ConcatenatedModule, ConcatenatedModuleExportsDefinitions,
DependenciesBlock, Dependency, ExternalModule, LibraryOptions, ModuleIdentifier, Plugin,
PluginContext,
};
use rspack_error::{error_bail, Result};
use rspack_hash::RspackHash;
use rspack_hook::{plugin, plugin_hook};
use rspack_plugin_javascript::dependency::ImportDependency;
use rspack_plugin_javascript::ModuleConcatenationPlugin;
use rspack_plugin_javascript::{
ConcatConfiguration, JavascriptModulesChunkHash, JavascriptModulesRenderStartup, JsPlugin,
RenderSource,
};
use rustc_hash::FxHashSet as HashSet;

use super::modern_module::ModernModuleImportDependency;
use crate::utils::{get_options_for_chunk, COMMON_LIBRARY_NAME_MESSAGE};

const PLUGIN_NAME: &str = "rspack.ModernModuleLibraryPlugin";
Expand Down Expand Up @@ -189,6 +192,80 @@ fn render_startup(
Ok(())
}

#[plugin_hook(CompilationFinishModules for ModernModuleLibraryPlugin)]
async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> {
let mut mg = compilation.get_module_graph_mut();
let modules = mg.modules();
let module_ids = modules.keys().cloned().collect::<Vec<_>>();

for module_id in module_ids {
let mut deps_to_replace = Vec::new();
let module = mg
.module_by_identifier(&module_id)
.expect("should have mgm");
let connections = mg.get_outgoing_connections(&module_id);
let block_ids = module.get_blocks();

for block_id in block_ids {
let block = mg.block_by_id(block_id).expect("should have block");
for block_dep_id in block.get_dependencies() {
let block_dep = mg.dependency_by_id(block_dep_id);
if let Some(block_dep) = block_dep {
if let Some(import_dependency) = block_dep.as_any().downcast_ref::<ImportDependency>() {
// TODO: do not use find
// Try find the connection that marks import dependency as an external module.
let connection_to_external = connections.iter().find(|c| {
let module_id = c.module_identifier();
let module = mg.module_by_identifier(module_id).expect("should have mgm");
if let Some(external_module) = module.as_any().downcast_ref::<ExternalModule>() {
import_dependency.request == external_module.user_request
} else {
false
}
});

if let Some(connection_to_external) = connection_to_external {
let external_module_id = connection_to_external.module_identifier();
let external_module = mg
.module_by_identifier(external_module_id)
.expect("should have mgm");

if let Some(external_module) = external_module.as_external_module() {
let new_dep = ModernModuleImportDependency::new(
import_dependency.request.as_str().into(),
external_module.request.clone(),
external_module.external_type.clone(),
import_dependency.range.clone(),
None,
);

deps_to_replace.push((
*block_id,
block_dep.clone(),
new_dep.clone(),
connection_to_external.id,
));
}
}
}
}
}
}

for (block_id, dep, new_dep, connection_id) in deps_to_replace.iter() {
let block = mg.block_by_id_mut(block_id).expect("should have block");
let dep_id = dep.id();
block.remove_dependency_id(*dep_id);
let boxed_dep = Box::new(new_dep.clone()) as Box<dyn rspack_core::Dependency>;
block.add_dependency_id(*new_dep.id());
mg.add_dependency(boxed_dep);
mg.revoke_connection(connection_id, true);
}
}

Ok(())
}

#[plugin_hook(JavascriptModulesChunkHash for ModernModuleLibraryPlugin)]
async fn js_chunk_hash(
&self,
Expand Down Expand Up @@ -256,6 +333,11 @@ impl Plugin for ModernModuleLibraryPlugin {
.compilation_hooks
.optimize_chunk_modules
.tap(optimize_chunk_modules::new(self));
ctx
.context
.compilation_hooks
.finish_modules
.tap(finish_modules::new(self));

Ok(())
}
Expand Down

0 comments on commit cdb0cb7

Please sign in to comment.