Skip to content

Commit

Permalink
feat: add external option (#259)
Browse files Browse the repository at this point in the history
  • Loading branch information
underfin committed Nov 14, 2023
1 parent 69aaa4a commit a80db39
Show file tree
Hide file tree
Showing 21 changed files with 219 additions and 47 deletions.
1 change: 1 addition & 0 deletions crates/bench/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub async fn run_fixture(fixture_path: PathBuf) {
let mut bundler = Bundler::new(InputOptions {
input: vec![InputItem { name: Some("main".to_string()), import: "./main.js".to_string() }],
cwd: fixture_path.clone(),
..Default::default()
});

if fixture_path.join("dist").is_dir() {
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown/examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ async fn main() {
let mut bundler = Bundler::new(InputOptions {
input: vec![InputItem { name: Some("basic".to_string()), import: "./index.js".to_string() }],
cwd,
..Default::default()
});

let outputs = bundler.write(Default::default()).await.unwrap();
Expand Down
7 changes: 4 additions & 3 deletions crates/rolldown/src/bundler/bundler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use sugar_path::AsPath;

use super::{
bundle::output::Output,
options::input_options::SharedInputOptions,
plugin_driver::{PluginDriver, SharedPluginDriver},
stages::link_stage::{LinkStage, LinkStageOutput},
};
Expand All @@ -20,7 +21,7 @@ use crate::{
type BuildResult<T> = Result<T, Vec<BuildError>>;

pub struct Bundler<T: FileSystem + Default> {
input_options: InputOptions,
input_options: SharedInputOptions,
plugin_driver: SharedPluginDriver,
fs: T,
resolver: SharedResolver<T>,
Expand All @@ -42,7 +43,7 @@ impl<T: FileSystem + Default + 'static> Bundler<T> {
Self {
resolver: Resolver::with_cwd_and_fs(input_options.cwd.clone(), false, fs.share()).into(),
plugin_driver: Arc::new(PluginDriver::new(plugins)),
input_options,
input_options: Arc::new(input_options),
fs,
}
}
Expand Down Expand Up @@ -102,7 +103,7 @@ impl<T: FileSystem + Default + 'static> Bundler<T> {

async fn build_inner(&mut self) -> BatchedResult<LinkStageOutput> {
let build_info = ScanStage::new(
&self.input_options,
Arc::clone(&self.input_options),
Arc::clone(&self.plugin_driver),
self.fs.share(),
Arc::clone(&self.resolver),
Expand Down
22 changes: 15 additions & 7 deletions crates/rolldown/src/bundler/module_loader/module_loader.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::Arc;

use index_vec::IndexVec;
use rolldown_common::{ImportKind, ModuleId, RawPath, ResourceId};
use rolldown_fs::FileSystem;
Expand All @@ -10,7 +12,7 @@ use super::Msg;
use crate::bundler::module::external_module::ExternalModule;
use crate::bundler::module::{Module, ModuleVec};
use crate::bundler::module_loader::module_task_context::ModuleTaskCommonData;
use crate::bundler::options::input_options::InputOptions;
use crate::bundler::options::input_options::SharedInputOptions;
use crate::bundler::plugin_driver::SharedPluginDriver;
use crate::bundler::runtime::{Runtime, RUNTIME_PATH};
use crate::bundler::utils::ast_symbol::AstSymbol;
Expand All @@ -19,10 +21,10 @@ use crate::bundler::utils::symbols::Symbols;
use crate::error::BatchedResult;
use crate::SharedResolver;

pub struct ModuleLoader<'a, T: FileSystem + Default> {
pub struct ModuleLoader<T: FileSystem + Default> {
ctx: ModuleLoaderContext,
input_options: &'a InputOptions,
common_data: ModuleTaskCommonData<'a, T>,
input_options: SharedInputOptions,
common_data: ModuleTaskCommonData<T>,
rx: tokio::sync::mpsc::UnboundedReceiver<Msg>,
}

Expand All @@ -33,16 +35,22 @@ pub struct ModuleLoaderContext {
intermediate_modules: IndexVec<ModuleId, Option<Module>>,
}

impl<'a, T: FileSystem + 'static + Default> ModuleLoader<'a, T> {
impl<T: FileSystem + 'static + Default> ModuleLoader<T> {
pub fn new(
input_options: &'a InputOptions,
input_options: SharedInputOptions,
plugin_driver: SharedPluginDriver,
fs: T,
resolver: SharedResolver<T>,
) -> Self {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<Msg>();

let common_data = ModuleTaskCommonData { input_options, tx, resolver, fs, plugin_driver };
let common_data = ModuleTaskCommonData {
input_options: Arc::clone(&input_options),
tx,
resolver,
fs,
plugin_driver,
};

Self { common_data, rx, input_options, ctx: ModuleLoaderContext::default() }
}
Expand Down
10 changes: 5 additions & 5 deletions crates/rolldown/src/bundler/module_loader/module_task_context.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
use rolldown_fs::FileSystem;

use crate::{
bundler::{options::input_options::InputOptions, plugin_driver::SharedPluginDriver},
bundler::{options::input_options::SharedInputOptions, plugin_driver::SharedPluginDriver},
SharedResolver,
};

use super::Msg;

/// Used to store common data shared between all tasks.
pub struct ModuleTaskCommonData<'task, T: FileSystem + Default> {
pub input_options: &'task InputOptions,
pub struct ModuleTaskCommonData<T: FileSystem + Default> {
pub input_options: SharedInputOptions,
pub tx: tokio::sync::mpsc::UnboundedSender<Msg>,
pub resolver: SharedResolver<T>,
pub fs: T,
pub plugin_driver: SharedPluginDriver,
}

impl<'task, T: FileSystem + Default> ModuleTaskCommonData<'task, T> {
pub unsafe fn assume_static(&self) -> &'static ModuleTaskCommonData<'static, T> {
impl<T: FileSystem + Default> ModuleTaskCommonData<T> {
pub unsafe fn assume_static(&self) -> &'static Self {
std::mem::transmute(self)
}
}
37 changes: 28 additions & 9 deletions crates/rolldown/src/bundler/module_loader/normal_module_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
bundler::{
module::normal_module_builder::NormalModuleBuilder,
module_loader::NormalModuleTaskResult,
options::input_options::SharedInputOptions,
plugin_driver::SharedPluginDriver,
utils::{
ast_scope::AstScope,
Expand All @@ -27,7 +28,7 @@ use crate::{
HookLoadArgs, HookResolveIdArgsOptions, HookTransformArgs,
};
pub struct NormalModuleTask<'task, T: FileSystem + Default> {
ctx: &'task ModuleTaskCommonData<'task, T>,
ctx: &'task ModuleTaskCommonData<T>,
module_id: ModuleId,
path: ResourceId,
module_type: ModuleType,
Expand All @@ -38,7 +39,7 @@ pub struct NormalModuleTask<'task, T: FileSystem + Default> {

impl<'task, T: FileSystem + Default + 'static> NormalModuleTask<'task, T> {
pub fn new(
ctx: &'task ModuleTaskCommonData<'task, T>,
ctx: &'task ModuleTaskCommonData<T>,
id: ModuleId,
is_entry: bool,
path: ResourceId,
Expand Down Expand Up @@ -171,23 +172,40 @@ impl<'task, T: FileSystem + Default + 'static> NormalModuleTask<'task, T> {

#[allow(clippy::option_if_let_else)]
pub(crate) async fn resolve_id<F: FileSystem + Default>(
input_options: &SharedInputOptions,
resolver: &Resolver<F>,
plugin_driver: &SharedPluginDriver,
importer: &ResourceId,
specifier: &str,
options: HookResolveIdArgsOptions,
) -> BatchedResult<ResolvedRequestInfo> {
// let is_marked_as_external = is_external(specifier, Some(importer.id()), false).await?;

// if is_marked_as_external {
// return Ok(ModuleId::new(specifier, true));
// }
// Check external with unresolved path
if input_options
.external
.call(specifier.to_string(), Some(importer.prettify().to_string()), false)
.await?
{
return Ok(ResolvedRequestInfo {
path: specifier.to_string().into(),
module_type: ModuleType::Unknown,
is_external: true,
});
}

let resolved_id =
resolve_id(resolver, plugin_driver, specifier, Some(importer), options, false).await?;

match resolved_id {
Some(info) => Ok(info),
Some(mut info) => {
if !info.is_external {
// Check external with resolved path
info.is_external = input_options
.external
.call(specifier.to_string(), Some(importer.prettify().to_string()), true)
.await?;
}
Ok(info)
}
None => {
Ok(ResolvedRequestInfo {
path: specifier.to_string().into(),
Expand All @@ -210,15 +228,16 @@ impl<'task, T: FileSystem + Default + 'static> NormalModuleTask<'task, T> {
) -> BatchedResult<Vec<(ImportRecordId, ResolvedRequestInfo)>> {
let jobs = dependencies.iter_enumerated().map(|(idx, item)| {
let specifier = item.module_request.clone();
let input_options = Arc::clone(&self.ctx.input_options);
// FIXME(hyf0): should not use `Arc<Resolver>` here
let resolver = Arc::clone(&self.ctx.resolver);
let plugin_driver = Arc::clone(&self.ctx.plugin_driver);
let importer = self.path.clone();
let kind = item.kind;
// let is_external = self.is_external.clone();
// let on_warn = self.input_options.on_warn.clone();
tokio::spawn(async move {
Self::resolve_id(
&input_options,
&resolver,
&plugin_driver,
&importer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ use crate::bundler::{
visitors::scanner::{self, ScanResult},
};
pub struct RuntimeNormalModuleTask<'task, T: FileSystem + Default> {
common_data: &'task ModuleTaskCommonData<'task, T>,
common_data: &'task ModuleTaskCommonData<T>,
module_id: ModuleId,
module_type: ModuleType,
errors: Vec<BuildError>,
warnings: Vec<BuildError>,
}

impl<'task, T: FileSystem + Default> RuntimeNormalModuleTask<'task, T> {
pub fn new(common_data: &'task ModuleTaskCommonData<'task, T>, id: ModuleId) -> Self {
impl<'task, T: FileSystem + Default + 'static> RuntimeNormalModuleTask<'task, T> {
pub fn new(common_data: &'task ModuleTaskCommonData<T>, id: ModuleId) -> Self {
Self {
common_data,
module_id: id,
Expand Down
54 changes: 53 additions & 1 deletion crates/rolldown/src/bundler/options/input_options.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,56 @@
use std::fmt::Debug;
use std::path::PathBuf;
use std::pin::Pin;
use std::sync::Arc;

use derivative::Derivative;
use futures::Future;
use rolldown_error::BuildError;

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

pub enum External {
ArrayString(Vec<String>),
Fn(Box<ExternalFn>),
}

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

impl Default for External {
fn default() -> Self {
Self::ArrayString(vec![])
}
}

impl External {
pub async fn call(
&self,
source: String,
importer: Option<String>,
is_resolved: bool,
) -> Result<bool, BuildError> {
match self {
Self::ArrayString(value) => {
let result = value.iter().any(|item| item == &source);
Ok(result)
}
Self::Fn(value) => value(source, importer, is_resolved).await,
}
}
}

#[derive(Debug)]
pub struct InputItem {
Expand All @@ -20,10 +69,13 @@ impl From<String> for InputItem {
pub struct InputOptions {
pub input: Vec<InputItem>,
pub cwd: PathBuf,
pub external: External,
}

impl Default for InputOptions {
fn default() -> Self {
Self { input: vec![], cwd: std::env::current_dir().unwrap() }
Self { input: vec![], cwd: std::env::current_dir().unwrap(), external: External::default() }
}
}

pub type SharedInputOptions = Arc<InputOptions>;
13 changes: 7 additions & 6 deletions crates/rolldown/src/bundler/stages/scan_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{
bundler::{
module::ModuleVec,
module_loader::ModuleLoader,
options::input_options::SharedInputOptions,
plugin_driver::SharedPluginDriver,
runtime::Runtime,
utils::{
Expand All @@ -17,11 +18,11 @@ use crate::{
},
},
error::{BatchedErrors, BatchedResult},
HookResolveIdArgsOptions, InputOptions, SharedResolver,
HookResolveIdArgsOptions, SharedResolver,
};

pub struct ScanStage<'me, Fs: FileSystem + Default> {
input_options: &'me InputOptions,
pub struct ScanStage<Fs: FileSystem + Default> {
input_options: SharedInputOptions,
plugin_driver: SharedPluginDriver,
fs: Fs,
resolver: SharedResolver<Fs>,
Expand All @@ -35,9 +36,9 @@ pub struct ScanStageOutput {
pub runtime: Runtime,
}

impl<'me, Fs: FileSystem + Default + 'static> ScanStage<'me, Fs> {
impl<Fs: FileSystem + Default + 'static> ScanStage<Fs> {
pub fn new(
input_options: &'me InputOptions,
input_options: SharedInputOptions,
plugin_driver: SharedPluginDriver,
fs: Fs,
resolver: SharedResolver<Fs>,
Expand Down Expand Up @@ -93,7 +94,7 @@ impl<'me, Fs: FileSystem + Default + 'static> ScanStage<'me, Fs> {
assert!(!self.input_options.input.is_empty(), "You must supply options.input to rolldown");

let mut module_loader = ModuleLoader::new(
self.input_options,
Arc::clone(&self.input_options),
Arc::clone(&self.plugin_driver),
self.fs.share(),
Arc::clone(&self.resolver),
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 @@ -14,7 +14,7 @@ pub use crate::{
bundler::Bundler,
options::{
file_name_template::FileNameTemplate,
input_options::{InputItem, InputOptions},
input_options::{External, InputItem, InputOptions},
output_options::{OutputFormat, OutputOptions},
},
},
Expand Down
3 changes: 2 additions & 1 deletion crates/rolldown/tests/common/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
process::Command,
};

use rolldown::{Bundler, FileNameTemplate, InputOptions, Output, OutputOptions};
use rolldown::{Bundler, External, FileNameTemplate, InputOptions, Output, OutputOptions};
use rolldown_error::BuildError;
use rolldown_testing::TestConfig;

Expand Down Expand Up @@ -101,6 +101,7 @@ impl Fixture {
})
.unwrap(),
cwd: fixture_path.to_path_buf(),
external: test_config.input.external.map(|e| External::ArrayString(e)).unwrap_or_default(),
});

if fixture_path.join("dist").is_dir() {
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown_binding/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface InputItem {
import: string
}
export interface InputOptions {
external?: (source: string, importer?: string, isResolved: boolean) => boolean
input: Array<InputItem>
plugins: Array<PluginOptions>
cwd: string
Expand Down

0 comments on commit a80db39

Please sign in to comment.