Skip to content

Commit

Permalink
feat: support experiments.outputModule (#2803)
Browse files Browse the repository at this point in the history
* feat: output module

* fix: format

* fix: test

* fix: test

* fix: test

* fix: avoid add tree_shaking_result

* fix: test

* fix: test

* fix: test

* fix: test
  • Loading branch information
underfin authored and IWANABETHATGUY committed Apr 19, 2023
1 parent 227a0ed commit 62628a0
Show file tree
Hide file tree
Showing 47 changed files with 1,644 additions and 140 deletions.
3 changes: 2 additions & 1 deletion crates/rspack/tests/fixtures/dynamic-import/expected/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ __webpack_require__('./child Lazy recursive ^.*.js$')("./child/".concat(request

};

var __webpack_require__ = require('./runtime.js')
var __webpack_require__ = require('./runtime.js');

__webpack_require__.C(exports)
var __webpack_exports__ = __webpack_require__('./index.js');
9 changes: 9 additions & 0 deletions crates/rspack_binding_options/src/options/raw_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ impl RawOutputOptions {
plugins.push(rspack_plugin_runtime::ArrayPushCallbackChunkFormatPlugin {}.boxed());
}
"commonjs" => plugins.push(rspack_plugin_runtime::CommonJsChunkFormatPlugin {}.boxed()),
"module" => {
plugins.push(rspack_plugin_runtime::ModuleChunkFormatPlugin {}.boxed());
}
_ => {
return Err(internal_error!(
"Unsupported chunk format '{chunk_format}'."
Expand All @@ -202,6 +205,9 @@ impl RawOutputOptions {
"require" => {
plugins.push(rspack_plugin_runtime::CommonJsChunkLoadingPlugin {}.boxed());
}
"import" => {
plugins.push(rspack_plugin_runtime::ModuleChunkLoadingPlugin {}.boxed());
}
"universal" => {
return Err(internal_error!(
"Universal Chunk Loading is not implemented yet.",
Expand Down Expand Up @@ -342,6 +348,9 @@ impl RawOutputOptions {
rspack_plugin_library::AmdLibraryPlugin::new("amd-require".eq(library)).boxed(),
);
}
"module" => {
plugins.push(rspack_plugin_library::ModuleLibraryPlugin::default().boxed());
}
_ => {}
}
}
Expand Down
6 changes: 5 additions & 1 deletion crates/rspack_core/src/compiler/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ use crate::{
build_chunk_graph::build_chunk_graph,
cache::{use_code_splitting_cache, Cache, CodeSplittingCache},
is_source_equal,
tree_shaking::{optimizer, visitor::SymbolRef, BailoutFlag, OptimizeDependencyResult},
tree_shaking::{
optimizer, visitor::SymbolRef, webpack_ext::ExportInfo, BailoutFlag, OptimizeDependencyResult,
},
utils::fast_drop,
AddQueue, AddTask, AddTaskResult, AdditionalChunkRuntimeRequirementsArgs, BoxModuleDependency,
BuildQueue, BuildTask, BuildTaskResult, BundleEntries, Chunk, ChunkByUkey, ChunkGraph,
Expand Down Expand Up @@ -85,6 +87,7 @@ pub struct Compilation {
pub used_symbol_ref: HashSet<SymbolRef>,
/// Collecting all module that need to skip in tree-shaking ast modification phase
pub bailout_module_identifiers: IdentifierMap<BailoutFlag>,
pub exports_info_map: IdentifierMap<Vec<ExportInfo>>,
#[cfg(debug_assertions)]
pub tree_shaking_result: IdentifierMap<TreeShakingResult>,

Expand Down Expand Up @@ -137,6 +140,7 @@ impl Compilation {
named_chunk_groups: Default::default(),
entry_module_identifiers: IdentifierSet::default(),
used_symbol_ref: HashSet::default(),
exports_info_map: IdentifierMap::default(),
#[cfg(debug_assertions)]
tree_shaking_result: IdentifierMap::default(),
bailout_module_identifiers: IdentifierMap::default(),
Expand Down
28 changes: 24 additions & 4 deletions crates/rspack_core/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use tokio::sync::RwLock;
use tracing::instrument;

use crate::{
cache::Cache, fast_set, CompilerOptions, LoaderRunnerRunner, Plugin, PluginDriver,
SharedPluginDriver,
cache::Cache, fast_set, tree_shaking::webpack_ext::ExportInfoExt, CompilerOptions,
LoaderRunnerRunner, Plugin, PluginDriver, SharedPluginDriver,
};

#[derive(Debug)]
Expand Down Expand Up @@ -139,7 +139,20 @@ where
let option = self.options.clone();
self.compilation.make(params).await?;
self.compilation.finish(self.plugin_driver.clone()).await?;
if option.builtins.tree_shaking {
if option.builtins.tree_shaking
|| option
.output
.enabled_library_types
.as_ref()
.and_then(|types| {
if types.contains(&"module".to_string()) {
Some(())
} else {
None
}
})
.is_some()
{
let (analyze_result, diagnostics) = self
.compilation
.optimize_dependency()
Expand All @@ -152,7 +165,14 @@ where
self.compilation.bailout_module_identifiers = analyze_result.bail_out_module_identifiers;
self.compilation.side_effects_free_modules = analyze_result.side_effects_free_modules;
self.compilation.module_item_map = analyze_result.module_item_map;

for entry in &self.compilation.entry_module_identifiers {
if let Some(analyze_results) = analyze_result.analyze_results.get(entry) {
self
.compilation
.exports_info_map
.insert(*entry, analyze_results.ordered_exports());
}
}
// This is only used when testing
#[cfg(debug_assertions)]
{
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_core/src/options/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ pub enum WasmLoading {
pub enum WasmLoadingType {
Fetch,
AsyncNode,
AsyncNodeModule,
}

impl From<&str> for WasmLoadingType {
fn from(value: &str) -> Self {
match value {
"fetch" => Self::Fetch,
"async-node" => Self::AsyncNode,
"async-node-module" => Self::AsyncNodeModule,
_ => todo!(),
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_core/src/plugin/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ pub struct RenderStartupArgs<'a> {
// pub module_source: &'a BoxSource,
pub compilation: &'a Compilation,
pub chunk: &'a ChunkUkey,
pub module: ModuleIdentifier,
}

impl<'me> RenderStartupArgs<'me> {
Expand Down
4 changes: 4 additions & 0 deletions crates/rspack_core/src/runtime_globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ bitflags! {
const BASE_URI = 1 << 32;

const MODULE_LOADED = 1 << 33;

const STARTUP_ENTRYPOINT = 1 << 34;

}
}

Expand Down Expand Up @@ -246,6 +249,7 @@ impl RuntimeGlobals {
R::INSTANTIATE_WASM => "__webpack_require__.v",
R::ASYNC_MODULE => "__webpack_require__.a",
R::BASE_URI => "__webpack_require__.b",
R::STARTUP_ENTRYPOINT => "__webpack_require__.X",
r => panic!(
"Unexpected flag `{r:?}`. RuntimeGlobals should only be printed for one single flag."
),
Expand Down
3 changes: 2 additions & 1 deletion crates/rspack_core/src/tree_shaking/webpack_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ pub trait ExportInfoExt {
fn ordered_exports(&self) -> Vec<ExportInfo>;
}

#[derive(Debug)]
pub struct ExportInfo {
name: JsWord,
pub name: JsWord,
}

impl ExportInfoExt for TreeShakingResult {
Expand Down
7 changes: 7 additions & 0 deletions crates/rspack_plugin_javascript/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ impl JsPlugin {
if runtime_requirements.contains(RuntimeGlobals::RETURN_EXPORTS_FROM_RUNTIME) {
sources.add(RawSource::from("return __webpack_exports__;\n"));
}
let last_entry_module = compilation
.chunk_graph
.get_chunk_entry_modules_with_chunk_group_iterable(&chunk.ukey)
.keys()
.last()
.expect("should have last entry module");
if let Some(source) =
compilation
.plugin_driver
Expand All @@ -194,6 +200,7 @@ impl JsPlugin {
.render_startup(RenderStartupArgs {
compilation,
chunk: &chunk.ukey,
module: *last_entry_module,
})?
{
sources.add(source);
Expand Down
10 changes: 2 additions & 8 deletions crates/rspack_plugin_library/src/assign_library_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use rspack_core::{
RenderStartupArgs, SourceType,
};

use crate::utils::property_access;

#[derive(Debug)]
pub enum Unnamed {
Error,
Expand Down Expand Up @@ -155,14 +157,6 @@ fn property_library(library: &Option<LibraryOptions>) -> String {
String::default()
}

fn property_access(o: &Vec<String>) -> String {
let mut str = String::default();
for property in o {
str.push_str(format!(r#"["{property}"]"#).as_str());
}
str
}

fn access_with_init(accessor: &Vec<String>, existing_length: usize, init_last: bool) -> String {
let base = accessor[0].clone();
if accessor.len() == 1 && !init_last {
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_plugin_library/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ mod umd_library_plugin;
pub use umd_library_plugin::UmdLibraryPlugin;
mod amd_library_plugin;
pub use amd_library_plugin::AmdLibraryPlugin;
mod module_library_plugin;
pub use module_library_plugin::ModuleLibraryPlugin;
mod utils;
62 changes: 62 additions & 0 deletions crates/rspack_plugin_library/src/module_library_plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::hash::Hash;

use rspack_core::{
rspack_sources::{ConcatSource, RawSource, SourceExt},
to_identifier, JsChunkHashArgs, Plugin, PluginContext, PluginJsChunkHashHookOutput,
PluginRenderStartupHookOutput, RenderStartupArgs,
};

use crate::utils::property_access;

#[derive(Debug, Default)]
pub struct ModuleLibraryPlugin {}

impl ModuleLibraryPlugin {}

impl Plugin for ModuleLibraryPlugin {
fn name(&self) -> &'static str {
"ModuleLibraryPlugin"
}

fn render_startup(
&self,
_ctx: PluginContext,
args: &RenderStartupArgs,
) -> PluginRenderStartupHookOutput {
let mut source = ConcatSource::default();
let mut exports = vec![];
if let Some(ordered_exports) = args.compilation.exports_info_map.get(&args.module) {
for info in ordered_exports {
let name = to_identifier(info.name.as_ref());
let var_name = format!("__webpack_exports__{}", name);
source.add(RawSource::from(format!(
"var {var_name} = __webpack_exports__{};\n",
property_access(&vec![info.name.to_string()])
)));
exports.push(format!("{var_name} as {}", info.name));
}
}
if !exports.is_empty() {
source.add(RawSource::from(format!(
"export {{ {} }};\n",
exports.join(", ")
)));
}
Ok(Some(source.boxed()))
}

fn js_chunk_hash(
&self,
_ctx: PluginContext,
args: &mut JsChunkHashArgs,
) -> PluginJsChunkHashHookOutput {
self.name().hash(&mut args.hasher);
args
.compilation
.options
.output
.library
.hash(&mut args.hasher);
Ok(())
}
}
24 changes: 24 additions & 0 deletions crates/rspack_plugin_library/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,27 @@ pub fn external_arguments(modules: &[&ExternalModule], compilation: &Compilation
.collect::<Vec<_>>()
.join(", ")
}

// pub fn normalize_name(o: &Option<LibraryOptions>) -> Result<Option<String>> {
// if let Some(LibraryOptions {
// name: Some(LibraryName {
// root: Some(root), ..
// }),
// ..
// }) = o
// {
// // TODO error "AMD library name must be a simple string or unset."
// if let Some(name) = root.get(0) {
// return Ok(Some(name.to_string()));
// }
// }
// Ok(None)
// }

pub fn property_access(o: &Vec<String>) -> String {
let mut str = String::default();
for property in o {
str.push_str(format!(r#"["{property}"]"#).as_str());
}
str
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,13 @@ impl Plugin for ArrayPushCallbackChunkFormatPlugin {
}
if has_entry {
source.add(generate_chunk_entry_code(args.compilation, args.chunk_ukey));
let runtime_requirements = args
let last_entry_module = args
.compilation
.chunk_graph
.get_tree_runtime_requirements(args.chunk_ukey);
if runtime_requirements.contains(RuntimeGlobals::RETURN_EXPORTS_FROM_RUNTIME) {
source.add(RawSource::from("return __webpack_exports__;\n"));
}
.get_chunk_entry_modules_with_chunk_group_iterable(&chunk.ukey)
.keys()
.last()
.expect("should have last entry module");
if let Some(s) =
args
.compilation
Expand All @@ -164,10 +164,18 @@ impl Plugin for ArrayPushCallbackChunkFormatPlugin {
.render_startup(RenderStartupArgs {
compilation: args.compilation,
chunk: &chunk.ukey,
module: *last_entry_module,
})?
{
source.add(s);
}
let runtime_requirements = args
.compilation
.chunk_graph
.get_tree_runtime_requirements(args.chunk_ukey);
if runtime_requirements.contains(RuntimeGlobals::RETURN_EXPORTS_FROM_RUNTIME) {
source.add(RawSource::from("return __webpack_exports__;\n"));
}
}
source.add(RawSource::from("\n}\n"));
}
Expand Down
Loading

0 comments on commit 62628a0

Please sign in to comment.