#![feature(box_patterns)]
#![feature(let_chains)]
use std::{collections::HashMap, sync::Arc};
use farmfe_core::{
enhanced_magic_string::bundle::Bundle,
parking_lot::Mutex,
plugin::Plugin,
resource::resource_pot::{ResourcePotMetaData, ResourcePotType},
};
use resource_pot_to_bundle::{Polyfill, SharedBundle};
pub mod resource_pot_to_bundle;
const MODULE_NEED_POLYFILLS: [Polyfill; 3] = [
Polyfill::Wildcard,
Polyfill::InteropRequireDefault,
Polyfill::ExportStar,
];
#[derive(Default)]
pub struct FarmPluginBundle {
runtime_code: Mutex<Arc<String>>,
bundle_map: Mutex<HashMap<String, Bundle>>,
}
impl FarmPluginBundle {
pub fn new() -> Self {
Self::default()
}
}
impl Plugin for FarmPluginBundle {
fn name(&self) -> &str {
"farm-plugin-bundle"
}
fn process_resource_pots(
&self,
resource_pots: &mut Vec<&mut farmfe_core::resource::resource_pot::ResourcePot>,
context: &std::sync::Arc<farmfe_core::context::CompilationContext>,
) -> farmfe_core::error::Result<Option<()>> {
if !self.runtime_code.lock().is_empty() {
return Ok(None);
}
let module_graph = context.module_graph.read();
resource_pots.sort_by_key(|item| item.id.clone());
let r = resource_pots
.iter()
.filter(|item| {
context.config.output.target_env.is_library()
|| matches!(item.resource_pot_type, ResourcePotType::Runtime)
})
.map(|item| &**item)
.collect::<Vec<_>>();
let mut shared_bundle = SharedBundle::new(r, &module_graph, context)?;
let inject_resource_pot_id = resource_pots
.iter()
.find(|item| {
(context.config.output.target_env.is_library() && item.entry_module.is_some())
|| matches!(item.resource_pot_type, ResourcePotType::Runtime)
})
.map(|i| i.id.clone());
if let Some(resource_pot_id) = inject_resource_pot_id {
let polyfill = &mut shared_bundle
.bundle_map
.get_mut(&resource_pot_id)
.unwrap()
.polyfill;
MODULE_NEED_POLYFILLS
.iter()
.for_each(|item| polyfill.add(item.clone()));
}
shared_bundle.render()?;
let mut defer_minify = vec![];
for resource_pot in resource_pots.iter() {
if matches!(resource_pot.resource_pot_type, ResourcePotType::Runtime)
|| (context.config.output.target_env.is_library()
&& resource_pot.resource_pot_type == ResourcePotType::Js)
{
let resource_pot_id = resource_pot.id.clone();
let bundle = shared_bundle.codegen(&resource_pot_id)?;
defer_minify.push(resource_pot_id.clone());
if matches!(resource_pot.resource_pot_type, ResourcePotType::Runtime) {
*self.runtime_code.lock() = Arc::new(bundle.to_string());
} else {
self.bundle_map.lock().insert(resource_pot_id, bundle);
}
}
}
for resource_pot in resource_pots {
if defer_minify.contains(&resource_pot.id) {
resource_pot.defer_minify_as_resource_pot();
}
}
Ok(None)
}
fn render_resource_pot_modules(
&self,
resource_pot: &farmfe_core::resource::resource_pot::ResourcePot,
_context: &Arc<farmfe_core::context::CompilationContext>,
_hook_context: &farmfe_core::plugin::PluginHookContext,
) -> farmfe_core::error::Result<Option<farmfe_core::resource::resource_pot::ResourcePotMetaData>>
{
if matches!(resource_pot.resource_pot_type, ResourcePotType::Runtime) {
return Ok(Some(ResourcePotMetaData {
rendered_modules: HashMap::new(),
rendered_content: self.runtime_code.lock().clone(),
rendered_map_chain: vec![],
custom_data: resource_pot.meta.custom_data.clone(),
}));
} else if let Some(bundle) = self.bundle_map.lock().get(&resource_pot.id) {
return Ok(Some(ResourcePotMetaData {
rendered_modules: HashMap::new(),
rendered_content: Arc::new(bundle.to_string()),
rendered_map_chain: vec![],
custom_data: resource_pot.meta.custom_data.clone(),
}));
}
Ok(None)
}
}