Skip to content

Commit

Permalink
feat: add banner output option (#634)
Browse files Browse the repository at this point in the history
Co-authored-by: underfin <likui.underfin@gmail.com>
  • Loading branch information
PengBoUESTC and underfin committed Mar 28, 2024
1 parent 29a8abe commit 60331fb
Show file tree
Hide file tree
Showing 17 changed files with 170 additions and 43 deletions.
14 changes: 10 additions & 4 deletions crates/rolldown/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use rolldown_common::ChunkId;
pub type ChunksVec = IndexVec<ChunkId, Chunk>;

use rolldown_common::{
ChunkKind, ExternalModuleId, NamedImport, NormalModuleId, RenderedModule, Specifier, SymbolRef,
ChunkKind, ExternalModuleId, NamedImport, NormalModuleId, RenderedChunk, RenderedModule,
Specifier, SymbolRef,
};
use rolldown_error::BuildError;
use rolldown_rstr::Rstr;
Expand Down Expand Up @@ -55,7 +56,7 @@ pub struct Chunk {
pub struct ChunkRenderReturn {
pub code: String,
pub map: Option<SourceMap>,
pub rendered_modules: FxHashMap<String, RenderedModule>,
pub rendered_chunk: RenderedChunk,
}

impl Chunk {
Expand All @@ -80,7 +81,7 @@ impl Chunk {
}

#[allow(clippy::unnecessary_wraps, clippy::cast_possible_truncation)]
pub fn render(
pub async fn render(
&self,
input_options: &NormalizedInputOptions,
graph: &LinkStageOutput,
Expand Down Expand Up @@ -155,13 +156,18 @@ impl Chunk {
Ok(())
},
)?;
let rendered_chunk = self.get_rendered_chunk_info(graph, output_options, rendered_modules);
// add banner
if let Some(banner_txt) = output_options.banner.call(rendered_chunk.clone()).await? {
concat_source.add_prepend_source(Box::new(RawSource::new(banner_txt)));
}

if let Some(exports) = self.render_exports(graph, output_options) {
concat_source.add_source(Box::new(RawSource::new(exports)));
}

let (content, map) = concat_source.content_and_sourcemap();

Ok(ChunkRenderReturn { code: content, map, rendered_modules })
Ok(ChunkRenderReturn { code: content, map, rendered_chunk })
}
}
1 change: 1 addition & 0 deletions crates/rolldown/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub use crate::{
input_options::{resolve_options::ResolveOptions, External, InputOptions},
output_options::{OutputFormat, OutputOptions, SourceMapType},
types::input_item::InputItem,
types::output_option::Banner,
},
types::rolldown_output::RolldownOutput,
};
8 changes: 4 additions & 4 deletions crates/rolldown/src/options/normalized_output_options/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use derivative::Derivative;

use crate::{FileNameTemplate, OutputFormat};

use super::output_options::SourceMapType;
use crate::Banner;
use crate::{FileNameTemplate, OutputFormat};
use derivative::Derivative;

#[derive(Derivative)]
#[derivative(Debug)]
Expand All @@ -12,4 +11,5 @@ pub struct NormalizedOutputOptions {
pub dir: String,
pub format: OutputFormat,
pub sourcemap: SourceMapType,
pub banner: Banner,
}
2 changes: 2 additions & 0 deletions crates/rolldown/src/options/output_options/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::Banner;
use derivative::Derivative;

#[derive(Debug)]
Expand Down Expand Up @@ -38,6 +39,7 @@ pub struct OutputOptions {
pub dir: Option<String>,
pub format: Option<OutputFormat>,
pub sourcemap: Option<SourceMapType>,
pub banner: Option<Banner>,
}

// impl Default for OutputOptions {
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown/src/options/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod input_item;
pub mod output_option;
40 changes: 40 additions & 0 deletions crates/rolldown/src/options/types/output_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use futures::Future;
use rolldown_common::RenderedChunk;
use rolldown_error::BuildError;
use std::fmt::Debug;
use std::pin::Pin;

pub type BannerFn = dyn Fn(
RenderedChunk,
) -> Pin<Box<(dyn Future<Output = Result<Option<String>, BuildError>> + Send + 'static)>>
+ Send
+ Sync;

pub enum Banner {
String(Option<String>),
Fn(Box<BannerFn>),
}

impl Debug for Banner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(value) => write!(f, "Banner::String({value:?})"),
Self::Fn(_) => write!(f, "Banner::Fn(...)"),
}
}
}

impl Default for Banner {
fn default() -> Self {
Self::String(None)
}
}

impl Banner {
pub async fn call(&self, chunk: RenderedChunk) -> Result<Option<String>, BuildError> {
match self {
Self::String(value) => Ok(value.clone()),
Self::Fn(value) => value(chunk).await,
}
}
}
27 changes: 13 additions & 14 deletions crates/rolldown/src/stages/bundle_stage/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
chunk::ChunkRenderReturn,
chunk_graph::ChunkGraph,
error::BatchedResult,
finalizer::FinalizerContext,
Expand All @@ -9,11 +10,12 @@ use crate::{
stages::link_stage::LinkStageOutput,
utils::{finalize_normal_module, is_in_rust_test_mode, render_chunks::render_chunks},
};
use rolldown_utils::block_on_spawn_all;

use rolldown_common::{ChunkKind, Output, OutputAsset, OutputChunk};
use rolldown_error::BuildError;
use rolldown_plugin::SharedPluginDriver;
use rustc_hash::FxHashSet;

mod code_splitting;
mod compute_cross_chunk_links;

Expand Down Expand Up @@ -79,20 +81,17 @@ impl<'a> BundleStage<'a> {
});
tracing::info!("finalizing modules");

let chunks = chunk_graph.chunks.iter().map(|c| {
let ret =
c.render(self.input_options, self.link_output, &chunk_graph, self.output_options).unwrap();
(
ret.code,
ret.map,
c.get_rendered_chunk_info(self.link_output, self.output_options, ret.rendered_modules),
)
});
let chunks = block_on_spawn_all(chunk_graph.chunks.iter().map(|c| async {
c.render(self.input_options, self.link_output, &chunk_graph, self.output_options).await
}))
.into_iter()
.collect::<Result<Vec<_>, _>>()?;

let mut assets = vec![];

render_chunks(self.plugin_driver, chunks).await?.into_iter().try_for_each(
|(mut content, mut map, rendered_chunk)| -> Result<(), BuildError> {
|chunk| -> Result<(), BuildError> {
let ChunkRenderReturn { mut map, rendered_chunk, mut code } = chunk;
if let Some(map) = map.as_mut() {
map.set_file(Some(rendered_chunk.file_name.clone()));
match self.output_options.sourcemap {
Expand All @@ -107,20 +106,20 @@ impl<'a> BundleStage<'a> {
file_name: map_file_name.clone(),
source: map,
})));
content.push_str(&format!("\n//# sourceMappingURL={map_file_name}"));
code.push_str(&format!("\n//# sourceMappingURL={map_file_name}"));
}
SourceMapType::Inline => {
let data_url =
map.to_data_url().map_err(|e| BuildError::sourcemap_error(e.to_string()))?;
content.push_str(&format!("\n//# sourceMappingURL={data_url}"));
code.push_str(&format!("\n//# sourceMappingURL={data_url}"));
}
SourceMapType::Hidden => {}
}
}
let sourcemap_file_name = map.as_ref().map(|_| format!("{}.map", rendered_chunk.file_name));
assets.push(Output::Chunk(Box::new(OutputChunk {
file_name: rendered_chunk.file_name,
code: content,
code,
is_entry: rendered_chunk.is_entry,
is_dynamic_entry: rendered_chunk.is_dynamic_entry,
facade_module_id: rendered_chunk.facade_module_id,
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown/src/utils/normalize_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub fn normalize_options(
.chunk_file_names
.unwrap_or_else(|| "[name]-[hash].js".to_string())
.into(),
banner: raw_output.banner.unwrap_or_default(),
dir: raw_output.dir.unwrap_or_else(|| "dist".to_string()),
format: raw_output.format.unwrap_or(crate::OutputFormat::Esm),
sourcemap: raw_output.sourcemap.unwrap_or(SourceMapType::Hidden),
Expand Down
17 changes: 9 additions & 8 deletions crates/rolldown/src/utils/render_chunks.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
use rolldown_common::{IntoBatchedResult, RenderedChunk};
use rolldown_common::IntoBatchedResult;
use rolldown_plugin::{HookRenderChunkArgs, SharedPluginDriver};
use rolldown_sourcemap::SourceMap;
use rolldown_utils::block_on_spawn_all;

use crate::error::BatchedErrors;
use crate::{chunk::ChunkRenderReturn, error::BatchedErrors};

pub async fn render_chunks<'a>(
plugin_driver: &SharedPluginDriver,
chunks: impl Iterator<Item = (String, Option<SourceMap>, RenderedChunk)>,
) -> Result<Vec<(String, Option<SourceMap>, RenderedChunk)>, BatchedErrors> {
chunks: Vec<ChunkRenderReturn>,
) -> Result<Vec<ChunkRenderReturn>, BatchedErrors> {
// TODO support `render_chunk` hook return map
let result = block_on_spawn_all(chunks.map(|(content, map, rendered_chunk)| async move {
let result = block_on_spawn_all(chunks.into_iter().map(|chunk| async move {
tracing::info!("render_chunks");
match plugin_driver
.render_chunk(HookRenderChunkArgs { code: content, chunk: &rendered_chunk })
.render_chunk(HookRenderChunkArgs { code: chunk.code, chunk: &chunk.rendered_chunk })
.await
{
Ok(value) => Ok((value, map, rendered_chunk)),
Ok(code) => {
Ok(ChunkRenderReturn { code, map: chunk.map, rendered_chunk: chunk.rendered_chunk })
}
Err(e) => Err(e),
}
}));
Expand Down
14 changes: 10 additions & 4 deletions crates/rolldown_binding/src/options/binding_output_options/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use super::super::types::binding_rendered_chunk::RenderedChunk;
use super::plugin::BindingPluginOptions;
use crate::types::js_async_callback::JsAsyncCallback;
use derivative::Derivative;
use napi_derive::napi;
use serde::Deserialize;

use super::plugin::BindingPluginOptions;

#[napi(object, object_to_js = false)]
#[derive(Deserialize, Debug)]
#[derive(Deserialize, Derivative)]
#[serde(rename_all = "camelCase")]
#[derivative(Debug)]
pub struct BindingOutputOptions {
// --- Options Rolldown doesn't need to be supported
// /** @deprecated Use the "renderDynamicImport" plugin hook instead. */
Expand All @@ -15,7 +18,10 @@ pub struct BindingOutputOptions {

// amd: NormalizedAmdOptions;
// assetFileNames: string | ((chunkInfo: PreRenderedAsset) => string);
// banner: () => string | Promise<string>;
#[derivative(Debug = "ignore")]
#[serde(skip_deserializing)]
#[napi(ts_type = "undefined | string | ((chunk: RenderedChunk) => string | Promise<String>)")]
pub banner: Option<JsAsyncCallback<RenderedChunk, Option<String>>>,
// chunkFileNames: string | ((chunkInfo: PreRenderedChunk) => string);
// compact: boolean;
pub dir: Option<String>,
Expand Down
14 changes: 11 additions & 3 deletions crates/rolldown_binding/src/utils/normalize_binding_options.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::utils::js_async_callback_ext::JsAsyncCallbackExt;
use std::path::PathBuf;

use rolldown::{InputOptions, OutputOptions};
use crate::{options::plugin::JsPlugin, types::binding_rendered_chunk::RenderedChunk};
use rolldown::{Banner, InputOptions, OutputOptions};
use rolldown_error::BuildError;
use rolldown_plugin::BoxPlugin;

use crate::options::plugin::JsPlugin;

pub struct NormalizeBindingOptionsReturn {
pub input_options: InputOptions,
pub output_options: OutputOptions,
Expand Down Expand Up @@ -48,6 +48,14 @@ pub fn normalize_binding_options(
chunk_file_names: output_options.chunk_file_names,
dir: output_options.dir,
sourcemap: output_options.sourcemap.map(Into::into),
banner: output_options.banner.map(move |value| {
Banner::Fn(Box::new(move |chunk| {
let fn_js = value.clone();
Box::pin(async move {
fn_js.call_async_normalized(RenderedChunk::from(chunk)).await.map_err(BuildError::from)
})
}))
}),
..Default::default()
};

Expand Down
20 changes: 15 additions & 5 deletions crates/rolldown_sourcemap/src/concat_sourcemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,33 @@ impl Source for SourceMapSource {

#[derive(Default)]
pub struct ConcatSource {
inner: Vec<Box<dyn Source>>,
inner: Vec<Box<dyn Source + Send>>,
prepend_source: Vec<Box<dyn Source + Send>>,
enabel_sourcemap: bool,
}

impl ConcatSource {
pub fn add_source(&mut self, source: Box<dyn Source>) {
pub fn add_source(&mut self, source: Box<dyn Source + Send>) {
if source.sourcemap().is_some() {
self.enabel_sourcemap = true;
}
self.inner.push(source);
}

pub fn add_prepend_source(&mut self, source: Box<dyn Source + Send>) {
if source.sourcemap().is_some() {
self.enabel_sourcemap = true;
}
self.prepend_source.push(source);
}

#[allow(clippy::cast_possible_truncation)]
pub fn content_and_sourcemap(self) -> (String, Option<SourceMap>) {
let mut final_source = String::new();
let mut sourcemap_builder = self.enabel_sourcemap.then_some(SourceMapBuilder::new(None));
let mut line_offset = 0;

for (index, source) in self.inner.iter().enumerate() {
for (index, source) in self.prepend_source.iter().chain(self.inner.iter()).enumerate() {
source.into_concat_source(&mut final_source, &mut sourcemap_builder, line_offset);
if index < self.inner.len() - 1 {
final_source.push('\n');
Expand All @@ -132,6 +140,8 @@ mod tests {
fn concat_sourcemaps_works() {
let mut concat_source = ConcatSource::default();
concat_source.add_source(Box::new(RawSource::new("\nconsole.log()".to_string())));
concat_source.add_prepend_source(Box::new(RawSource::new("// banner".to_string())));

concat_source.add_source(Box::new(SourceMapSource::new(
"function sayHello(name: string) {\n console.log(`Hello, ${name}`);\n}\n".to_string(),
SourceMap::from_slice(
Expand All @@ -156,9 +166,9 @@ mod tests {

assert_eq!(
content,
"\nconsole.log()\nfunction sayHello(name: string) {\n console.log(`Hello, ${name}`);\n}\n"
"// banner\n\nconsole.log()\nfunction sayHello(name: string) {\n console.log(`Hello, ${name}`);\n}\n"
);
let expected = "{\"version\":3,\"sources\":[\"index.ts\"],\"sourcesContent\":[\"function sayHello(name: string) {\\n console.log(`Hello, ${name}`);\\n}\\n\"],\"names\":[],\"mappings\":\";;AAAA,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,CAAC,GAAG,CAAC,iBAAU,IAAI,CAAE,CAAC,CAAC;AAChC,CAAC\"}";
let expected = "{\"version\":3,\"sources\":[\"index.ts\"],\"sourcesContent\":[\"function sayHello(name: string) {\\n console.log(`Hello, ${name}`);\\n}\\n\"],\"names\":[],\"mappings\":\";;;AAAA,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,CAAC,GAAG,CAAC,iBAAU,IAAI,CAAE,CAAC,CAAC;AAChC,CAAC\"}";
assert_eq!(map, expected);
}
}
4 changes: 4 additions & 0 deletions packages/rolldown/src/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export interface BindingOutputChunk {
export interface BindingOutputOptions {
entryFileNames?: string
chunkFileNames?: string
banner?:
| undefined
| string
| ((chunk: RenderedChunk) => string | Promise<String>)
dir?: string
exports?: 'default' | 'named' | 'none' | 'auto'
format?: 'es' | 'cjs'
Expand Down
3 changes: 2 additions & 1 deletion packages/rolldown/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RolldownOutput } from './types/rolldown-output'
import { RolldownOutput, RolldownOutputChunk } from './types/rolldown-output'
import type { InputOptions } from './options/input-options'
import type { OutputOptions } from './options/output-options'
import type { RolldownOptions } from './types/rolldown-options'
Expand All @@ -9,6 +9,7 @@ import { rolldown, experimental_scan } from './rolldown'
export { defineConfig, rolldown, experimental_scan }

export type {
RolldownOutputChunk,
RolldownOptions,
RolldownOutput,
InputOptions,
Expand Down
Loading

0 comments on commit 60331fb

Please sign in to comment.