Skip to content

Commit

Permalink
feat: render chunk hook binding (#402)
Browse files Browse the repository at this point in the history
  • Loading branch information
underfin authored Jan 23, 2024
1 parent b7ea28b commit 25fca9d
Showing 21 changed files with 383 additions and 49 deletions.
2 changes: 1 addition & 1 deletion crates/rolldown/src/bundler/bundle/output.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_hash::FxHashMap;

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct RenderedModule {
// The code of the module is omit at now.
pub original_length: u32,
5 changes: 3 additions & 2 deletions crates/rolldown/src/bundler/bundler.rs
Original file line number Diff line number Diff line change
@@ -176,8 +176,9 @@ impl<T: FileSystem + Default + 'static> Bundler<T> {
tracing::trace!("InputOptions {:#?}", self.input_options);
tracing::trace!("OutputOptions: {output_options:#?}",);
let graph = self.build_result.as_mut().expect("Build should success");
let mut bundle_stage = BundleStage::new(graph, &self.input_options, &output_options);
let assets = bundle_stage.bundle();
let mut bundle_stage =
BundleStage::new(graph, &self.input_options, &output_options, &self.plugin_driver);
let assets = bundle_stage.bundle().await?;

Ok(RolldownOutput { warnings: std::mem::take(&mut graph.warnings), assets })
}
2 changes: 1 addition & 1 deletion crates/rolldown/src/bundler/chunk/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#[allow(clippy::module_inception)]
pub mod chunk;
mod de_conflict;
pub mod render_chunk;
mod render_chunk_exports;
mod render_chunk_imports;

use index_vec::IndexVec;

use self::chunk::Chunk;
70 changes: 70 additions & 0 deletions crates/rolldown/src/bundler/chunk/render_chunk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use rolldown_common::EntryPointKind;
use rustc_hash::FxHashMap;

use crate::{bundler::stages::link_stage::LinkStageOutput, OutputOptions, RenderedModule};

use super::chunk::Chunk;

#[derive(Debug, Clone)]
pub struct PreRenderedChunk {
// pub name: String,
pub is_entry: bool,
pub is_dynamic_entry: bool,
pub facade_module_id: Option<String>,
pub module_ids: Vec<String>,
pub exports: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct RenderedChunk {
// PreRenderedChunk
pub is_entry: bool,
pub is_dynamic_entry: bool,
pub facade_module_id: Option<String>,
pub module_ids: Vec<String>,
pub exports: Vec<String>,
// RenderedChunk
pub file_name: String,
pub modules: FxHashMap<String, RenderedModule>,
}

impl Chunk {
pub fn get_pre_rendered_chunk_info(
&self,
graph: &LinkStageOutput,
output_options: &OutputOptions,
) -> PreRenderedChunk {
PreRenderedChunk {
is_entry: matches!(&self.entry_point, Some(e) if e.kind == EntryPointKind::UserDefined),
is_dynamic_entry: matches!(&self.entry_point, Some(e) if e.kind == EntryPointKind::DynamicImport),
facade_module_id: self
.entry_point
.as_ref()
.map(|entry_point| graph.modules[entry_point.id].resource_id().expect_file().to_string()),
module_ids: self
.modules
.iter()
.map(|id| graph.modules[*id].resource_id().expect_file().to_string())
.collect(),
exports: self.get_export_names(graph, output_options),
}
}

pub fn get_rendered_chunk_info(
&self,
graph: &LinkStageOutput,
output_options: &OutputOptions,
render_modules: FxHashMap<String, RenderedModule>,
) -> RenderedChunk {
let pre_rendered_chunk = self.get_pre_rendered_chunk_info(graph, output_options);
RenderedChunk {
is_entry: pre_rendered_chunk.is_entry,
is_dynamic_entry: pre_rendered_chunk.is_dynamic_entry,
facade_module_id: pre_rendered_chunk.facade_module_id,
module_ids: pre_rendered_chunk.module_ids,
exports: pre_rendered_chunk.exports,
file_name: self.file_name.clone().expect("should have file name"),
modules: render_modules,
}
}
}
2 changes: 1 addition & 1 deletion crates/rolldown/src/bundler/mod.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ mod ast_scanner;
pub mod bundle;
#[allow(clippy::module_inception)]
pub mod bundler;
mod chunk;
pub mod chunk;
mod chunk_graph;
mod linker;
mod module;
2 changes: 1 addition & 1 deletion crates/rolldown/src/bundler/module/mod.rs
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ impl Module {
}
}

pub fn expect_normal(&self) -> &NormalModule {
pub fn _expect_normal(&self) -> &NormalModule {
match self {
Self::Normal(m) => m,
Self::External(_) => unreachable!(),
3 changes: 1 addition & 2 deletions crates/rolldown/src/bundler/plugin_driver/mod.rs
Original file line number Diff line number Diff line change
@@ -63,8 +63,7 @@ impl PluginDriver {
Ok(())
}

#[allow(dead_code)]
pub async fn render_chunk(&self, mut args: RenderChunkArgs) -> Result<String, BuildError> {
pub async fn render_chunk(&self, mut args: RenderChunkArgs<'_>) -> Result<String, BuildError> {
for plugin in &self.plugins {
if let Some(r) = plugin.render_chunk(&PluginContext::new(), &args).await? {
args.code = r.code;
43 changes: 23 additions & 20 deletions crates/rolldown/src/bundler/stages/bundle_stage.rs
Original file line number Diff line number Diff line change
@@ -10,9 +10,11 @@ use crate::{
chunk_graph::ChunkGraph,
module::Module,
options::{file_name_template::FileNameRenderOptions, output_options::OutputOptions},
plugin_driver::SharedPluginDriver,
stages::link_stage::LinkStageOutput,
utils::{bitset::BitSet, finalizer::FinalizerContext},
utils::{bitset::BitSet, finalizer::FinalizerContext, render_chunks::render_chunks},
},
error::BatchedResult,
InputOptions, Output, OutputFormat,
};
use index_vec::{index_vec, IndexVec};
@@ -23,19 +25,21 @@ pub struct BundleStage<'a> {
link_output: &'a mut LinkStageOutput,
output_options: &'a OutputOptions,
input_options: &'a InputOptions,
plugin_driver: &'a SharedPluginDriver,
}

impl<'a> BundleStage<'a> {
pub fn new(
link_output: &'a mut LinkStageOutput,
input_options: &'a InputOptions,
output_options: &'a OutputOptions,
plugin_driver: &'a SharedPluginDriver,
) -> Self {
Self { link_output, output_options, input_options }
Self { link_output, output_options, input_options, plugin_driver }
}

#[tracing::instrument(skip_all)]
pub fn bundle(&mut self) -> Vec<Output> {
pub async fn bundle(&mut self) -> BatchedResult<Vec<Output>> {
use rayon::prelude::*;
let mut chunk_graph = self.generate_chunks();

@@ -62,30 +66,29 @@ impl<'a> BundleStage<'a> {
Module::External(_) => {}
});

let assets = chunk_graph
.chunks
.iter()
.enumerate()
.map(|(_chunk_id, c)| {
let (content, rendered_modules) = c
.render(self.input_options, self.link_output, &chunk_graph, self.output_options)
.unwrap();
let chunks = chunk_graph.chunks.iter().map(|c| {
let (content, rendered_modules) =
c.render(self.input_options, self.link_output, &chunk_graph, self.output_options).unwrap();
(content, c.get_rendered_chunk_info(self.link_output, self.output_options, rendered_modules))
});

let assets = render_chunks(self.plugin_driver, chunks)
.await?
.into_iter()
.map(|(content, rendered_chunk)| {
Output::Chunk(Box::new(OutputChunk {
file_name: c.file_name.clone().unwrap(),
file_name: rendered_chunk.file_name,
code: content,
is_entry: matches!(&c.entry_point, Some(e) if e.kind == EntryPointKind::UserDefined),
is_dynamic_entry: matches!(&c.entry_point, Some(e) if e.kind == EntryPointKind::DynamicImport),
facade_module_id: c.entry_point.as_ref().map(|entry_point| {
self.link_output.modules[entry_point.id].expect_normal().pretty_path.to_string()
}),
modules: rendered_modules,
exports: c.get_export_names(self.link_output, self.output_options),
is_entry: rendered_chunk.is_entry,
is_dynamic_entry: rendered_chunk.is_dynamic_entry,
facade_module_id: rendered_chunk.facade_module_id,
modules: rendered_chunk.modules,
exports: rendered_chunk.exports,
}))
})
.collect::<Vec<_>>();

assets
Ok(assets)
}

fn determine_reachable_modules_for_entry(
4 changes: 2 additions & 2 deletions crates/rolldown/src/bundler/stages/scan_stage.rs
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ use crate::{
symbols::Symbols,
},
},
error::{collect_errors, BatchedResult},
error::{into_batched_result, BatchedResult},
HookResolveIdArgsOptions, SharedResolver,
};

@@ -101,6 +101,6 @@ impl<Fs: FileSystem + Default + 'static> ScanStage<Fs> {
}
}));

collect_errors(resolved_ids)
into_batched_result(resolved_ids)
}
}
28 changes: 17 additions & 11 deletions crates/rolldown/src/bundler/utils/render_chunks.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
use rolldown_utils::block_on_spawn_all;

use crate::{
bundler::{chunk::ChunkId, plugin_driver::PluginDriver},
error::{collect_errors, BatchedErrors},
bundler::{chunk::render_chunk::RenderedChunk, plugin_driver::SharedPluginDriver},
error::{into_batched_result, BatchedErrors},
plugin::args::RenderChunkArgs,
};

#[allow(clippy::unused_async)]
pub async fn _render_chunks<'a>(
plugin_driver: &PluginDriver,
chunks: Vec<(ChunkId, String)>,
) -> Result<Vec<(ChunkId, String)>, BatchedErrors> {
let result = block_on_spawn_all(chunks.iter().map(|(chunk, content)| async move {
match plugin_driver.render_chunk(RenderChunkArgs { code: content.to_string() }).await {
Ok(value) => Ok((*chunk, value)),
#[allow(clippy::future_not_send)]
pub async fn render_chunks<'a>(
plugin_driver: &SharedPluginDriver,
chunks: impl Iterator<Item = (String, RenderedChunk)>,
) -> Result<Vec<(String, RenderedChunk)>, BatchedErrors> {
let result = block_on_spawn_all(chunks.map(|(content, rendered_chunk)| async move {
match plugin_driver
.render_chunk(RenderChunkArgs {
code: content,
chunk: unsafe { std::mem::transmute(&rendered_chunk) },
})
.await
{
Ok(value) => Ok((value, rendered_chunk)),
Err(e) => Err(e),
}
}));

collect_errors(result)
into_batched_result(result)
}
2 changes: 1 addition & 1 deletion crates/rolldown/src/error.rs
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ impl BatchedErrors {
}
}

pub fn collect_errors<T>(value: Vec<Result<T, BuildError>>) -> BatchedResult<Vec<T>> {
pub fn into_batched_result<T>(value: Vec<Result<T, BuildError>>) -> BatchedResult<Vec<T>> {
let mut errors = BatchedErrors::default();

let collected = value.into_iter().filter_map(|item| errors.take_err_from(item)).collect();
3 changes: 2 additions & 1 deletion crates/rolldown/src/lib.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ pub use crate::{
bundler::{
bundle::output::{Output, OutputAsset, OutputChunk, RenderedModule},
bundler::{Bundler, RolldownOutput},
chunk::render_chunk::{PreRenderedChunk, RenderedChunk},
options::{
file_name_template::FileNameTemplate,
input_options::{External, InputItem, InputOptions},
@@ -21,7 +22,7 @@ pub use crate::{
plugin::{
args::{
HookBuildEndArgs, HookLoadArgs, HookResolveIdArgs, HookResolveIdArgsOptions,
HookTransformArgs,
HookTransformArgs, RenderChunkArgs,
},
context::PluginContext,
output::{HookLoadOutput, HookRenderChunkOutput, HookResolveIdOutput},
4 changes: 3 additions & 1 deletion crates/rolldown/src/plugin/args.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::bundler::chunk::render_chunk::RenderedChunk;
use rolldown_common::ImportKind;

#[derive(Debug)]
@@ -31,6 +32,7 @@ pub struct HookBuildEndArgs {
}

#[derive(Debug)]
pub struct RenderChunkArgs {
pub struct RenderChunkArgs<'a> {
pub code: String,
pub chunk: &'a RenderedChunk,
}
5 changes: 4 additions & 1 deletion crates/rolldown/tests/common/case.rs
Original file line number Diff line number Diff line change
@@ -95,7 +95,10 @@ impl Case {
chunk.file_name,
chunk.is_entry,
chunk.is_dynamic_entry,
chunk.facade_module_id,
chunk
.facade_module_id
.clone()
.map(|v| v.replace(self.fixture.dir_path().to_str().unwrap(), "$DIR$")),
chunk.exports
))]
}
6 changes: 3 additions & 3 deletions crates/rolldown/tests/fixtures/code_splitting/artifacts.snap
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ console.log('shared');
## Output Stats

- _rolldown_runtime.mjs, is_entry false, is_dynamic_entry false, facade_module_id None, exports []
- dynamic_js.mjs, is_entry false, is_dynamic_entry true, facade_module_id Some("dynamic.js"), exports []
- main1.mjs, is_entry true, is_dynamic_entry false, facade_module_id Some("main1.js"), exports []
- main2.mjs, is_entry true, is_dynamic_entry false, facade_module_id Some("main2.js"), exports []
- dynamic_js.mjs, is_entry false, is_dynamic_entry true, facade_module_id Some("$DIR$/dynamic.js"), exports []
- main1.mjs, is_entry true, is_dynamic_entry false, facade_module_id Some("$DIR$/main1.js"), exports []
- main2.mjs, is_entry true, is_dynamic_entry false, facade_module_id Some("$DIR$/main2.js"), exports []
- share_js.mjs, is_entry false, is_dynamic_entry false, facade_module_id None, exports []
23 changes: 23 additions & 0 deletions crates/rolldown_binding/index.d.ts
Original file line number Diff line number Diff line change
@@ -14,6 +14,10 @@ export interface PluginOptions {
load?: (id: string) => Promise<undefined | SourceResult>
transform?: (id: string, code: string) => Promise<undefined | SourceResult>
buildEnd?: (error: string) => Promise<void>
renderChunk?: (
code: string,
chunk: RenderedChunk,
) => Promise<undefined | HookRenderChunkOutput>
}
export interface HookResolveIdArgsOptions {
isEntry: boolean
@@ -26,6 +30,25 @@ export interface ResolveIdResult {
export interface SourceResult {
code: string
}
export interface HookRenderChunkOutput {
code: string
}
export interface PreRenderedChunk {
isEntry: boolean
isDynamicEntry: boolean
facadeModuleId?: string
moduleIds: Array<string>
exports: Array<string>
}
export interface RenderedChunk {
isEntry: boolean
isDynamicEntry: boolean
facadeModuleId?: string
moduleIds: Array<string>
exports: Array<string>
fileName: string
modules: Record<string, RenderedModule>
}
export interface InputItem {
name?: string
import: string
Loading

0 comments on commit 25fca9d

Please sign in to comment.