Skip to content

Commit

Permalink
feat: add footer output option (#693)
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 29, 2024
1 parent 15cecef commit ca51a6b
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 25 deletions.
7 changes: 7 additions & 0 deletions crates/rolldown/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ impl Chunk {
},
)?;
let rendered_chunk = self.get_rendered_chunk_info(graph, output_options, rendered_modules);

// TODO avoid rendered_chunk clone
// 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)));
Expand All @@ -166,6 +168,11 @@ impl Chunk {
concat_source.add_source(Box::new(RawSource::new(exports)));
}

// add footer
if let Some(footer_txt) = output_options.footer.call(rendered_chunk.clone()).await? {
concat_source.add_source(Box::new(RawSource::new(footer_txt)));
}

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

Ok(ChunkRenderReturn { code: content, map, rendered_chunk })
Expand Down
2 changes: 1 addition & 1 deletion crates/rolldown/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +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::output_option::AddonOutputOption,
},
types::rolldown_output::RolldownOutput,
};
5 changes: 3 additions & 2 deletions crates/rolldown/src/options/normalized_output_options/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::output_options::SourceMapType;
use crate::Banner;
use crate::AddonOutputOption;
use crate::{FileNameTemplate, OutputFormat};
use derivative::Derivative;

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

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

// impl Default for OutputOptions {
Expand Down
16 changes: 8 additions & 8 deletions crates/rolldown/src/options/types/output_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,33 @@ use rolldown_error::BuildError;
use std::fmt::Debug;
use std::pin::Pin;

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

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

impl Debug for Banner {
impl Debug for AddonOutputOption {
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(...)"),
Self::String(value) => write!(f, "AddonFunction::String({value:?})"),
Self::Fn(_) => write!(f, "AddonFunction::Fn(...)"),
}
}
}

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

impl Banner {
impl AddonOutputOption {
pub async fn call(&self, chunk: RenderedChunk) -> Result<Option<String>, BuildError> {
match self {
Self::String(value) => Ok(value.clone()),
Expand Down
3 changes: 2 additions & 1 deletion crates/rolldown/src/utils/normalize_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,14 @@ pub fn normalize_options(

// Normalize output options

let output_options = NormalizedOutputOptions {
let output_options: NormalizedOutputOptions = NormalizedOutputOptions {
entry_file_names: raw_output.entry_file_names.unwrap_or_else(|| "[name].js".to_string()).into(),
chunk_file_names: raw_output
.chunk_file_names
.unwrap_or_else(|| "[name]-[hash].js".to_string())
.into(),
banner: raw_output.banner.unwrap_or_default(),
footer: raw_output.footer.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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use derivative::Derivative;
use napi_derive::napi;
use serde::Deserialize;

pub type AddonOutputOption = MaybeAsyncJsCallback<RenderedChunk, Option<String>>;

#[napi(object, object_to_js = false)]
#[derive(Deserialize, Derivative)]
#[serde(rename_all = "camelCase")]
Expand All @@ -23,7 +25,7 @@ pub struct BindingOutputOptions {
#[napi(
ts_type = "Nullable<string> | ((chunk: RenderedChunk) => MaybePromise<VoidNullable<string>>)"
)]
pub banner: Option<MaybeAsyncJsCallback<RenderedChunk, Option<String>>>,
pub banner: Option<AddonOutputOption>,
// chunkFileNames: string | ((chunkInfo: PreRenderedChunk) => string);
// compact: boolean;
pub dir: Option<String>,
Expand All @@ -34,6 +36,12 @@ pub struct BindingOutputOptions {
// extend: boolean;
// externalLiveBindings: boolean;
// footer: () => string | Promise<string>;
#[derivative(Debug = "ignore")]
#[serde(skip_deserializing)]
#[napi(
ts_type = "Nullable<string> | ((chunk: RenderedChunk) => MaybePromise<VoidNullable<string>>)"
)]
pub footer: Option<AddonOutputOption>,
#[napi(ts_type = "'es' | 'cjs'")]
pub format: Option<String>,
// freeze: boolean;
Expand Down
25 changes: 16 additions & 9 deletions crates/rolldown_binding/src/utils/normalize_binding_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
options::plugin::JsPlugin,
types::{binding_rendered_chunk::RenderedChunk, js_callback::MaybeAsyncJsCallbackExt},
};
use rolldown::{Banner, InputOptions, OutputOptions};
use rolldown::{AddonOutputOption, InputOptions, OutputOptions};
use rolldown_error::BuildError;
use rolldown_plugin::BoxPlugin;

Expand All @@ -14,6 +14,19 @@ pub struct NormalizeBindingOptionsReturn {
pub plugins: Vec<BoxPlugin>,
}

fn normalize_addon_option(
addon_option: Option<crate::options::AddonOutputOption>,
) -> Option<AddonOutputOption> {
addon_option.map(move |value| {
AddonOutputOption::Fn(Box::new(move |chunk| {
let fn_js = value.clone();
Box::pin(async move {
fn_js.await_call(RenderedChunk::from(chunk)).await.map_err(BuildError::from)
})
}))
})
}

pub fn normalize_binding_options(
input_options: crate::options::BindingInputOptions,
output_options: crate::options::BindingOutputOptions,
Expand Down Expand Up @@ -50,14 +63,8 @@ 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.await_call(RenderedChunk::from(chunk)).await.map_err(BuildError::from)
})
}))
}),
banner: normalize_addon_option(output_options.banner),
footer: normalize_addon_option(output_options.footer),
..Default::default()
};

Expand Down
3 changes: 3 additions & 0 deletions packages/rolldown/src/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ export interface BindingOutputOptions {
| ((chunk: RenderedChunk) => MaybePromise<VoidNullable<string>>)
dir?: string
exports?: 'default' | 'named' | 'none' | 'auto'
footer?:
| Nullable<string>
| ((chunk: RenderedChunk) => MaybePromise<VoidNullable<string>>)
format?: 'es' | 'cjs'
plugins: Array<BindingPluginOptions>
sourcemap?: 'file' | 'inline' | 'hidden'
Expand Down
4 changes: 3 additions & 1 deletion packages/rolldown/src/options/output-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface OutputOptions {
exports?: RollupOutputOptions['exports']
sourcemap?: RollupOutputOptions['sourcemap']
banner?: RollupOutputOptions['banner']
footer?: RollupOutputOptions['footer']
}

function normalizeFormat(
Expand Down Expand Up @@ -40,7 +41,7 @@ function normalizeSourcemap(
}
}

const getAddon = <T extends 'banner'>(
const getAddon = <T extends 'banner' | 'footer'>(
config: OutputOptions,
name: T,
): BindingOutputOptions[T] => {
Expand All @@ -63,5 +64,6 @@ export function normalizeOutputOptions(
sourcemap: normalizeSourcemap(sourcemap),
plugins: [],
banner: getAddon(opts, 'banner'),
footer: getAddon(opts, 'footer'),
}
}
29 changes: 29 additions & 0 deletions packages/rolldown/tests/fixtures/output/footer/_config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type {
RollupOptions,
RolldownOutput,
RolldownOutputChunk,
} from '../../../../src'
import { expect } from 'vitest'

const footerTxt = '// footer test\n'
const footer = () => Promise.resolve().then(() => footerTxt)

const config: RollupOptions = {
external: [/external/, 'external-a'],
output: {
footer,
},
}

export default {
config,
afterTest: (output: RolldownOutput) => {
expect(
output.output
.filter(({ type }) => type === 'chunk')
.every((chunk) =>
(chunk as RolldownOutputChunk).code.endsWith(footerTxt),
),
).toBe(true)
},
}
4 changes: 4 additions & 0 deletions packages/rolldown/tests/fixtures/output/footer/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import external from 'external'
import externalA from 'external-a'

console.log(external, externalA)

0 comments on commit ca51a6b

Please sign in to comment.