From e8462291652a9ea5dd3d737adfa1870d6c57b0b2 Mon Sep 17 00:00:00 2001 From: brightwu <1521488775@qq.com> Date: Sun, 5 Mar 2023 11:01:21 +0800 Subject: [PATCH] fix: lazy compilation and partial bundling bug (#44) --- .vscode/settings.json | 8 +- crates/compiler/src/build/mod.rs | 22 +-- .../compiler/src/generate/partial_bundling.rs | 43 ++++-- crates/compiler/src/lib.rs | 2 + .../src/update/diff_and_patch_module_graph.rs | 2 +- crates/compiler/src/update/mod.rs | 116 +++++++++++++--- .../src/update/patch_module_group_graph.rs | 23 +++- .../src/update/regenerate_resources.rs | 129 +++++++++++------ crates/core/src/config/mod.rs | 4 - crates/core/src/context/mod.rs | 2 +- crates/core/src/lib.rs | 1 + crates/core/src/module/module_graph.rs | 6 +- crates/core/src/module/module_group.rs | 8 ++ crates/core/src/plugin/mod.rs | 4 +- crates/core/src/plugin/plugin_driver.rs | 4 +- crates/core/src/resource/mod.rs | 12 ++ crates/core/src/resource/resource_pot.rs | 7 +- crates/core/src/resource/resource_pot_map.rs | 4 + crates/node/src/lib.rs | 14 ++ .../rust_plugin_adapter/mod.rs | 8 +- crates/plugin_html/src/lib.rs | 108 +++++++++------ crates/plugin_html/src/resources_injector.rs | 130 ++++++++++-------- crates/plugin_partial_bundling/src/lib.rs | 54 +++++--- .../src/module_bucket.rs | 6 +- crates/plugin_runtime/src/lib.rs | 5 +- crates/plugin_script/tests/mod.rs | 7 +- .../react/src/comps/counter-button/index.tsx | 2 +- .../react/src/comps/description/index.tsx | 6 +- examples/react/src/main.tsx | 4 +- packages/core/CHANGELOG.md | 9 ++ packages/core/binding/binding.d.ts | 1 + packages/core/binding/index.d.ts | 1 - packages/core/package.json | 6 +- packages/core/src/config/index.ts | 1 - packages/core/src/server/hmr-engine.ts | 15 ++ .../server/middlewares/lazy-compilation.ts | 19 +++ packages/runtime-plugin-hmr/CHANGELOG.md | 8 ++ packages/runtime-plugin-hmr/package.json | 7 +- .../src/hot-module-state.ts | 6 +- packages/runtime-plugin-hmr/src/types.ts | 3 + packages/runtime/CHANGELOG.md | 6 + packages/runtime/package.json | 2 +- packages/runtime/src/module-system.ts | 8 +- packages/runtime/src/resource-loader.ts | 21 ++- pnpm-lock.yaml | 6 +- 45 files changed, 593 insertions(+), 267 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 761cd6f9f..cc2637721 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,6 @@ { - "editor.formatOnSave": true, - "editor.tabSize": 2 -} \ No newline at end of file + "editor.formatOnSave": true, + "editor.tabSize": 2, + "editor.fontFamily": "Fira Code", + "editor.fontLigatures": true +} diff --git a/crates/compiler/src/build/mod.rs b/crates/compiler/src/build/mod.rs index edb861e69..cff6747c4 100644 --- a/crates/compiler/src/build/mod.rs +++ b/crates/compiler/src/build/mod.rs @@ -331,11 +331,6 @@ impl Compiler { let mut module_graph = context.module_graph.write(); - // check if the module already exists - if module_graph.has_module(&module.id) { - return false; - } - // mark entry module if matches!(kind, ResolveKind::Entry) { module_graph.entries.insert(module.id.clone()); @@ -343,7 +338,12 @@ impl Compiler { tracing::trace!("added module to the graph: {:?}", module.id); - module_graph.add_module(module); + // check if the module already exists + if module_graph.has_module(&module.id) { + module_graph.replace_module(module); + } else { + module_graph.add_module(module); + } true } @@ -383,14 +383,18 @@ fn resolve_module( tracing::trace!("resolving module: {:?}", resolve_param); let resolve_module_id_result = Compiler::resolve_module_id(resolve_param, context)?; - - // be care of dead lock, see https://github.com/Amanieu/parking_lot/issues/212 - let module_graph = context.module_graph.read(); + let mut module_graph = context.module_graph.write(); let res = if module_graph.has_module(&resolve_module_id_result.module_id) { // the module has already been handled and it should not be handled twice ResolveModuleResult::Built(resolve_module_id_result.module_id) } else { + // insert a dummy module to the graph to prevent the module from being handled twice + module_graph.add_module(Compiler::create_module( + resolve_module_id_result.module_id.clone(), + false, + false, + )); ResolveModuleResult::Success(Box::new(ResolvedModuleInfo { module: Compiler::create_module( resolve_module_id_result.module_id.clone(), diff --git a/crates/compiler/src/generate/partial_bundling.rs b/crates/compiler/src/generate/partial_bundling.rs index 45920eae4..0befbcc4f 100644 --- a/crates/compiler/src/generate/partial_bundling.rs +++ b/crates/compiler/src/generate/partial_bundling.rs @@ -3,7 +3,11 @@ use std::sync::Arc; use farmfe_core::{ context::CompilationContext, error::CompilationError, - module::module_group::{ModuleGroup, ModuleGroupGraph}, + hashbrown::HashSet, + module::{ + module_group::{ModuleGroup, ModuleGroupGraph}, + ModuleId, + }, parking_lot::Mutex, plugin::PluginHookContext, resource::{resource_pot::ResourcePot, resource_pot_map::ResourcePotMap}, @@ -61,27 +65,46 @@ fn generate_resource_pot_map( ) -> farmfe_core::error::Result { tracing::trace!("Starting generate_resource_pot_map"); - let resource_pot_map = Mutex::new(ResourcePotMap::new()); + let mut modules = HashSet::new(); for g in module_group_graph.module_groups_mut() { - let resources_pots = call_partial_bundling_hook(g, context, hook_context)?; + modules.extend(g.modules().clone()); + } + + let resources_pots = + call_partial_bundling_hook(&modules.into_iter().collect(), context, hook_context)?; - let mut resource_pot_map = resource_pot_map.lock(); + let mut resource_pot_map = ResourcePotMap::new(); + let module_graph = context.module_graph.read(); - for resource_pot in resources_pots { - g.add_resource_pot(resource_pot.id.clone()); - resource_pot_map.add_resource_pot(resource_pot); + for mut resource_pot in resources_pots { + let mut module_groups = HashSet::new(); + + for module_id in resource_pot.modules() { + let module = module_graph.module(module_id).unwrap(); + module_groups.extend(module.module_groups.clone()); } + + resource_pot.module_groups = module_groups.clone(); + + for module_group_id in module_groups { + let module_group = module_group_graph + .module_group_mut(&module_group_id) + .unwrap(); + module_group.add_resource_pot(resource_pot.id.clone()); + } + + resource_pot_map.add_resource_pot(resource_pot); } tracing::trace!("generate_resource_pot_map finished"); - Ok(resource_pot_map.into_inner()) + Ok(resource_pot_map) } #[tracing::instrument(skip_all)] pub fn call_partial_bundling_hook( - g: &mut ModuleGroup, + modules: &Vec, context: &Arc, hook_context: &PluginHookContext, ) -> farmfe_core::error::Result> { @@ -89,7 +112,7 @@ pub fn call_partial_bundling_hook( let res = context .plugin_driver - .partial_bundling(g, context, hook_context)? + .partial_bundling(modules, context, hook_context)? .ok_or(CompilationError::PluginHookResultCheckError { hook_name: "partial_bundling".to_string(), })?; diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 07c137f6e..3650e0c09 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -1,4 +1,6 @@ #![deny(clippy::all)] +#![allow(clippy::needless_collect)] +#![allow(clippy::ptr_arg)] #![feature(box_patterns)] use std::sync::Arc; diff --git a/crates/compiler/src/update/diff_and_patch_module_graph.rs b/crates/compiler/src/update/diff_and_patch_module_graph.rs index d73f15b6d..7ddcd9e41 100644 --- a/crates/compiler/src/update/diff_and_patch_module_graph.rs +++ b/crates/compiler/src/update/diff_and_patch_module_graph.rs @@ -108,7 +108,7 @@ pub fn patch_module_graph( m }; - module_graph.replace_module(&updated, module); + module_graph.replace_module(module); } removed_modules diff --git a/crates/compiler/src/update/mod.rs b/crates/compiler/src/update/mod.rs index a1991b467..522a67bcc 100644 --- a/crates/compiler/src/update/mod.rs +++ b/crates/compiler/src/update/mod.rs @@ -7,10 +7,14 @@ use farmfe_core::{ context::CompilationContext, error::CompilationError, hashbrown::HashSet, - module::{module_graph::ModuleGraphEdge, Module, ModuleId}, + module::{ + module_graph::ModuleGraphEdge, module_group::ModuleGroupId, Module, ModuleId, ModuleType, + }, plugin::{PluginResolveHookParam, ResolveKind}, rayon::ThreadPool, + resource::ResourceType, }; +use farmfe_plugin_html::get_dynamic_resources_map; use farmfe_toolkit::tracing; use crate::{ @@ -43,6 +47,7 @@ pub struct UpdateResult { /// This code string should be returned to the client side as MIME type `application/javascript` pub resources: String, pub boundaries: HashMap>>, + pub dynamic_resources_map: Option>>, } #[derive(Debug, Clone)] @@ -111,23 +116,23 @@ impl Compiler { self.optimize_update_module_graph(&update_context).unwrap(); + let previous_module_groups = { + let module_group_graph = self.context.module_group_graph.read(); + module_group_graph + .module_groups() + .into_iter() + .map(|m| m.id.clone()) + .collect::>() + }; + let (affected_module_groups, updated_module_ids, diff_result) = self.diff_and_patch_context(paths, &update_context); - let cloned_updated_module_ids = updated_module_ids.clone(); - - let cloned_context = self.context.clone(); - std::thread::spawn(move || { - // TODO: manage a task queue, and run the tasks in sequence - regenerate_resources_for_affected_module_groups( - affected_module_groups, - &cloned_updated_module_ids, - &cloned_context, - ) - .unwrap(); - - finalize_resources(&cloned_context).unwrap(); - }); + let dynamic_resources_map = self.regenerate_resources( + affected_module_groups, + previous_module_groups, + &updated_module_ids, + ); // TODO1: only regenerate the resources for script modules. // TODO2: should reload when html change // TODO3: cover it with tests @@ -142,6 +147,7 @@ impl Compiler { removed_module_ids: diff_result.removed_modules.into_iter().collect(), resources, boundaries, + dynamic_resources_map, }) } @@ -237,10 +243,10 @@ impl Compiler { let mut update_module_graph = update_context.module_graph.write(); if update_module_graph.has_module(&module.id) { - return; + update_module_graph.replace_module(module); + } else { + update_module_graph.add_module(module); } - - update_module_graph.add_module(module); } fn add_edge_to_update_module_graph( @@ -318,6 +324,72 @@ impl Compiler { (affected_module_groups, start_points, diff_result) } + + fn regenerate_resources( + &self, + affected_module_groups: HashSet, + previous_module_groups: HashSet, + updated_module_ids: &Vec, + ) -> Option>> { + let mut dynamic_resources_map = None; + let cloned_updated_module_ids = updated_module_ids.clone(); + + let cloned_context = self.context.clone(); + + // if there are new module groups, we should run the tasks synchronously + if affected_module_groups + .iter() + .any(|ag| !previous_module_groups.contains(ag)) + { + regenerate_resources_for_affected_module_groups( + affected_module_groups, + &cloned_updated_module_ids, + &cloned_context, + ) + .unwrap(); + + finalize_resources(&cloned_context).unwrap(); + let module_group_graph = self.context.module_group_graph.read(); + let resource_pot_map = self.context.resource_pot_map.read(); + let resources_map = self.context.resources_map.lock(); + let module_graph = self.context.module_graph.read(); + let html_entries_ids = module_graph + .entries + .clone() + .into_iter() + .filter(|m| { + let module = module_graph.module(m).unwrap(); + matches!(module.module_type, ModuleType::Html) + }) + .collect::>(); + let mut dynamic_resources = HashMap::new(); + + for html_entry_id in html_entries_ids { + dynamic_resources.extend(get_dynamic_resources_map( + &*module_group_graph, + &html_entry_id, + &*resource_pot_map, + &*resources_map, + )); + } + + dynamic_resources_map = Some(dynamic_resources); + } else { + std::thread::spawn(move || { + // TODO: manage a task queue, and run the tasks in sequence + regenerate_resources_for_affected_module_groups( + affected_module_groups, + &cloned_updated_module_ids, + &cloned_context, + ) + .unwrap(); + + finalize_resources(&cloned_context).unwrap(); + }); + } + + dynamic_resources_map + } } /// Similar to [crate::build::resolve_module], but the resolved module may be existed in both context and update_context @@ -349,12 +421,18 @@ fn resolve_module( )); } - let update_module_graph = update_context.module_graph.read(); + let mut update_module_graph = update_context.module_graph.write(); if update_module_graph.has_module(&resolve_module_id_result.module_id) { return Ok(ResolveModuleResult::ExistingWhenUpdate( resolve_module_id_result.module_id, )); } + // just a placeholder module, it will be replaced by the real module later + update_module_graph.add_module(Compiler::create_module( + resolve_module_id_result.module_id.clone(), + false, + false, + )); Ok(ResolveModuleResult::Success(Box::new(ResolvedModuleInfo { module: Compiler::create_module( diff --git a/crates/compiler/src/update/patch_module_group_graph.rs b/crates/compiler/src/update/patch_module_group_graph.rs index eb61ef693..0b1b54486 100644 --- a/crates/compiler/src/update/patch_module_group_graph.rs +++ b/crates/compiler/src/update/patch_module_group_graph.rs @@ -198,6 +198,7 @@ pub fn patch_module_group_graph( let module_group_id = added_module_id.clone(); let module_group = ModuleGroup::new(module_group_id.clone()); module_group_graph.add_module_group(module_group); + affected_module_groups.insert(module_group_id.clone()); let module_group_ids = { let module = module_graph @@ -276,10 +277,26 @@ pub fn patch_module_group_graph( } } - affected_module_groups + let affected_module_groups = affected_module_groups .into_iter() .filter(|g_id| module_group_graph.has(g_id)) - .collect() + .collect::>(); + + let mut final_affected_module_groups = HashSet::new(); + + for module_group_id in affected_module_groups { + let module_group = module_group_graph.module_group(&module_group_id).unwrap(); + + for module_id in module_group.modules() { + let module = module_graph.module(module_id).unwrap(); + + for module_group_id in &module.module_groups { + final_affected_module_groups.insert(module_group_id.clone()); + } + } + } + + final_affected_module_groups } #[cfg(test)] @@ -420,7 +437,7 @@ mod tests { ); assert_eq!( affected_groups, - HashSet::from(["A".into(), "B".into(), "F".into()]) + HashSet::from(["A".into(), "B".into(), "F".into(), "D".into()]) ); let update_module_group_graph = module_group_graph_from_entries( diff --git a/crates/compiler/src/update/regenerate_resources.rs b/crates/compiler/src/update/regenerate_resources.rs index ee1d88462..db90ac063 100644 --- a/crates/compiler/src/update/regenerate_resources.rs +++ b/crates/compiler/src/update/regenerate_resources.rs @@ -34,7 +34,6 @@ pub fn render_and_generate_update_resource( let mut update_resource_pot = ResourcePot::new( ResourcePotId::new(String::from("__UPDATE_RESOURCE_POT__")), ResourcePotType::Js, - "__UPDATE_MODULE_GROUP__".into(), ); for added in &diff_result.added_modules { @@ -79,14 +78,10 @@ pub fn regenerate_resources_for_affected_module_groups( updated_module_ids: &Vec, context: &Arc, ) -> farmfe_core::error::Result<()> { - let mut affected_resource_pots_ids = vec![]; + clear_resource_pot_of_modules_in_module_groups(&affected_module_groups, context); - for module_group_id in &affected_module_groups { - let resource_pots_ids = - generate_and_diff_resource_pots(module_group_id, context)?; - - affected_resource_pots_ids.extend(resource_pots_ids); - } + let mut affected_resource_pots_ids = + generate_and_diff_resource_pots(&affected_module_groups, context)?; let mut resource_pot_map = context.resource_pot_map.write(); // always rerender the updated module's resource pot @@ -120,64 +115,108 @@ pub fn regenerate_resources_for_affected_module_groups( } fn generate_and_diff_resource_pots( - module_group_id: &ModuleGroupId, + module_groups: &HashSet, context: &Arc, ) -> farmfe_core::error::Result> { - clear_resource_pot_of_modules_in_module_group(module_group_id, context); - let mut module_group_graph = context.module_group_graph.write(); - let module_group = module_group_graph - .module_group_mut(module_group_id) - .unwrap(); - let resource_pots = - call_partial_bundling_hook(module_group, context, &PluginHookContext::default())?; - let resource_pots_ids = resource_pots + + let modules = module_groups .iter() - .map(|r| r.id.clone()) - .collect::>(); + .fold(HashSet::new(), |mut acc, module_group_id| { + let module_group = module_group_graph.module_group(module_group_id).unwrap(); + acc.extend(module_group.modules().clone()); + acc + }) + .into_iter() + .collect::>(); + let resources_pots = + call_partial_bundling_hook(&modules, context, &PluginHookContext::default())?; + let resources_pots_ids = resources_pots + .iter() + .map(|rp| rp.id.clone()) + .collect::>(); + + let module_graph = context.module_graph.read(); let mut resource_pot_map = context.resource_pot_map.write(); - let previous_resource_pots = module_group.resource_pots().clone(); - // remove the old resource pots from the graph - for resource_pot in &previous_resource_pots { - if !resource_pots_ids.contains(resource_pot) { - let resource_pot = resource_pot_map.remove_resource_pot(resource_pot).unwrap(); + let mut new_resource_pot_ids = vec![]; - // also remove the related resource - let mut resource_maps = context.resources_map.lock(); + for mut resource_pot in resources_pots { + let mut module_groups = HashSet::new(); - for resource in resource_pot.resources() { - resource_maps.remove(resource); - } + for module_id in resource_pot.modules() { + let module = module_graph.module(module_id).unwrap(); + module_groups.extend(module.module_groups.clone()); } - } - let mut new_resource_pot_ids = vec![]; + resource_pot.module_groups = module_groups.clone(); + + for module_group_id in module_groups { + let module_group = module_group_graph + .module_group_mut(&module_group_id) + .unwrap(); + let mut resources_pots_to_remove = vec![]; + + // Remove the old resource pots + for resource_pot in module_group.resource_pots() { + if !resources_pots_ids.contains(resource_pot) { + resources_pots_to_remove.push(resource_pot.clone()); + + if resource_pot_map.has_resource_pot(resource_pot) { + let resource_pot = resource_pot_map + .remove_resource_pot(resource_pot) + .unwrap_or_else(|| { + panic!( + "The resource pot {:?} should be in the resource pot map", + resource_pot + ) + }); + + // also remove the related resource + let mut resource_maps = context.resources_map.lock(); + + for resource in resource_pot.resources() { + resource_maps.remove(resource); + } + } + } + } - // add the new resource pots to the graph - for resource_pot in resource_pots { - if !previous_resource_pots.contains(&resource_pot.id) { + for resource_pot in resources_pots_to_remove { + module_group.remove_resource_pot(&resource_pot); + } + + if module_group.has_resource_pot(&resource_pot.id) { + // the resource pot is already in the module group + continue; + } else { + new_resource_pot_ids.push(resource_pot.id.clone()); + module_group.add_resource_pot(resource_pot.id.clone()); + } + } + + if !resource_pot_map.has_resource_pot(&resource_pot.id) { new_resource_pot_ids.push(resource_pot.id.clone()); resource_pot_map.add_resource_pot(resource_pot); } } - module_group.set_resource_pots(HashSet::from_iter(resource_pots_ids)); - Ok(new_resource_pot_ids) } -fn clear_resource_pot_of_modules_in_module_group( - module_group_id: &ModuleGroupId, +fn clear_resource_pot_of_modules_in_module_groups( + module_group_id: &HashSet, context: &Arc, ) { - let mut module_graph = context.module_graph.write(); - let module_group_graph = context.module_group_graph.read(); - let module_group = module_group_graph.module_group(module_group_id).unwrap(); - - for module_id in module_group.modules() { - let module = module_graph.module_mut(module_id).unwrap(); - module.resource_pot = None; + for module_group_id in module_group_id { + let mut module_graph = context.module_graph.write(); + let module_group_graph = context.module_group_graph.read(); + let module_group = module_group_graph.module_group(module_group_id).unwrap(); + + for module_id in module_group.modules() { + let module = module_graph.module_mut(module_id).unwrap(); + module.resource_pot = None; + } } } diff --git a/crates/core/src/config/mod.rs b/crates/core/src/config/mod.rs index 880068e84..6e702db13 100644 --- a/crates/core/src/config/mod.rs +++ b/crates/core/src/config/mod.rs @@ -147,9 +147,6 @@ pub struct PartialBundlingModuleBucketsConfig { pub name: String, /// Regex vec to match the modules in the module bucket pub test: Vec, - /// If true, this bucket will always contains all modules matches by the test field, this may bring duplicate modules when you have multiple entries and use dynamic import. - /// be careful to use it - pub isolate: bool, } impl Default for PartialBundlingModuleBucketsConfig { @@ -157,7 +154,6 @@ impl Default for PartialBundlingModuleBucketsConfig { Self { name: "".to_string(), test: vec![], - isolate: false, } } } diff --git a/crates/core/src/context/mod.rs b/crates/core/src/context/mod.rs index a11bbf4c6..fe2c862c0 100644 --- a/crates/core/src/context/mod.rs +++ b/crates/core/src/context/mod.rs @@ -27,7 +27,7 @@ pub struct CompilationContext { } impl CompilationContext { - pub fn new(mut config: Config, plugins: Vec>) -> Result { + pub fn new(config: Config, plugins: Vec>) -> Result { Ok(Self { module_graph: RwLock::new(ModuleGraph::new()), module_group_graph: RwLock::new(ModuleGroupGraph::new()), diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 3fb36e95f..a4112e5af 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,4 +1,5 @@ #![deny(clippy::all)] +#![allow(clippy::ptr_arg)] #![feature(trivial_bounds)] // #![feature(unsize)] // #![feature(trait_upcasting)] diff --git a/crates/core/src/module/module_graph.rs b/crates/core/src/module/module_graph.rs index 853aad23c..15c011b13 100644 --- a/crates/core/src/module/module_graph.rs +++ b/crates/core/src/module/module_graph.rs @@ -388,11 +388,11 @@ impl ModuleGraph { self.g.remove_node(i).unwrap() } - pub fn replace_module(&mut self, module_id: &ModuleId, module: Module) { + pub fn replace_module(&mut self, module: Module) { let i = self .id_index_map - .get(module_id) - .unwrap_or_else(|| panic!("module_id {:?} should in the module graph", module_id)); + .get(&module.id) + .unwrap_or_else(|| panic!("module_id {:?} should in the module graph", module.id)); self.g[*i] = module; } } diff --git a/crates/core/src/module/module_group.rs b/crates/core/src/module/module_group.rs index 8466acc44..fc6a8c6f6 100644 --- a/crates/core/src/module/module_group.rs +++ b/crates/core/src/module/module_group.rs @@ -289,6 +289,10 @@ impl ModuleGroup { self.resource_pots.insert(resource_pot_id); } + pub fn remove_resource_pot(&mut self, resource_pot_id: &ResourcePotId) { + self.resource_pots.retain(|id| id != resource_pot_id); + } + pub fn resource_pots(&self) -> &HashSet { &self.resource_pots } @@ -296,4 +300,8 @@ impl ModuleGroup { pub fn set_resource_pots(&mut self, resource_pots: HashSet) { self.resource_pots = resource_pots; } + + pub fn has_resource_pot(&self, resource_pot_id: &ResourcePotId) -> bool { + self.resource_pots.contains(resource_pot_id) + } } diff --git a/crates/core/src/plugin/mod.rs b/crates/core/src/plugin/mod.rs index 99560e35e..74254e694 100644 --- a/crates/core/src/plugin/mod.rs +++ b/crates/core/src/plugin/mod.rs @@ -121,10 +121,10 @@ pub trait Plugin: Any + Send + Sync { Ok(None) } - /// partial bundling modules of module group to [Vec] + /// partial bundling modules to [Vec] fn partial_bundling( &self, - _module_group: &mut ModuleGroup, + _modules: &Vec, _context: &Arc, _hook_context: &PluginHookContext, ) -> Result>> { diff --git a/crates/core/src/plugin/plugin_driver.rs b/crates/core/src/plugin/plugin_driver.rs index b16f332dc..d665e4767 100644 --- a/crates/core/src/plugin/plugin_driver.rs +++ b/crates/core/src/plugin/plugin_driver.rs @@ -15,7 +15,7 @@ use crate::{ module::{ module_graph::ModuleGraph, module_group::{ModuleGroup, ModuleGroupGraph}, - ModuleMetaData, ModuleType, + ModuleId, ModuleMetaData, ModuleType, }, resource::{resource_pot::ResourcePot, resource_pot_map::ResourcePotMap, Resource}, stats::Stats, @@ -164,7 +164,7 @@ impl PluginDriver { hook_first!( partial_bundling, Result>>, - module_group: &mut ModuleGroup, + modules: &Vec, context: &Arc, _hook_context: &PluginHookContext ); diff --git a/crates/core/src/resource/mod.rs b/crates/core/src/resource/mod.rs index ecdbafaaa..fc9ffb78f 100644 --- a/crates/core/src/resource/mod.rs +++ b/crates/core/src/resource/mod.rs @@ -31,6 +31,18 @@ impl ResourceType { ResourceType::SourceMap => "map".to_string(), } } + + pub fn to_html_tag(&self) -> String { + match self { + ResourceType::Asset(str) => str.to_string(), + ResourceType::Custom(str) => str.to_string(), + ResourceType::Runtime => "script".to_string(), + ResourceType::Js => "script".to_string(), + ResourceType::Css => "link".to_string(), + ResourceType::Html => "html".to_string(), + ResourceType::SourceMap => "map".to_string(), + } + } } #[cache_item] diff --git a/crates/core/src/resource/resource_pot.rs b/crates/core/src/resource/resource_pot.rs index 863ea8515..1276de53e 100644 --- a/crates/core/src/resource/resource_pot.rs +++ b/crates/core/src/resource/resource_pot.rs @@ -23,13 +23,12 @@ pub struct ResourcePot { pub entry_module: Option, /// the resources generated in this [ResourcePot] resources: HashSet, - // the [ModuleGroup] this [ResourcePot] belongs to - pub module_group: ModuleGroupId, + pub module_groups: HashSet, pub immutable: bool, } impl ResourcePot { - pub fn new(id: ResourcePotId, ty: ResourcePotType, group_id: ModuleGroupId) -> Self { + pub fn new(id: ResourcePotId, ty: ResourcePotType) -> Self { Self { id, resource_pot_type: ty, @@ -37,7 +36,7 @@ impl ResourcePot { meta: ResourcePotMetaData::Custom(Box::new(EmptyResourcePotMetaData) as _), entry_module: None, resources: HashSet::new(), - module_group: group_id, + module_groups: HashSet::new(), immutable: false, } } diff --git a/crates/core/src/resource/resource_pot_map.rs b/crates/core/src/resource/resource_pot_map.rs index ba0fe6bfe..c84511eda 100644 --- a/crates/core/src/resource/resource_pot_map.rs +++ b/crates/core/src/resource/resource_pot_map.rs @@ -44,6 +44,10 @@ impl ResourcePotMap { pub fn resource_pots_mut(&mut self) -> Vec<&mut ResourcePot> { self.map.values_mut().collect() } + + pub fn has_resource_pot(&self, id: &ResourcePotId) -> bool { + self.map.contains_key(id) + } } impl Default for ResourcePotMap { diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 168720323..90121276a 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -24,6 +24,7 @@ pub struct JsUpdateResult { pub removed: Vec, pub modules: String, pub boundaries: HashMap>>, + pub dynamic_resources_map: Option>>>, } #[napi(js_name = "Compiler")] @@ -156,6 +157,19 @@ impl JsCompiler { .collect(), modules: res.resources, boundaries: res.boundaries, + dynamic_resources_map: res.dynamic_resources_map.map(|dynamic_resources_map| { + dynamic_resources_map + .into_iter() + .map(|(k, v)| { + ( + k.id(self.compiler.context().config.mode.clone()), + v.into_iter() + .map(|(path, ty)| vec![path, ty.to_html_tag()]) + .collect(), + ) + }) + .collect() + }), }) } diff --git a/crates/node/src/plugin_adapters/rust_plugin_adapter/mod.rs b/crates/node/src/plugin_adapters/rust_plugin_adapter/mod.rs index 183116b6f..0f2ed830b 100644 --- a/crates/node/src/plugin_adapters/rust_plugin_adapter/mod.rs +++ b/crates/node/src/plugin_adapters/rust_plugin_adapter/mod.rs @@ -5,7 +5,7 @@ use farmfe_core::{ context::CompilationContext, error::{CompilationError, Result}, hashbrown::HashMap, - module::{module_graph::ModuleGraph, ModuleMetaData}, + module::{module_graph::ModuleGraph, ModuleId, ModuleMetaData}, parking_lot::RwLock, plugin::{ Plugin, PluginHookContext, PluginLoadHookParam, PluginLoadHookResult, @@ -134,13 +134,11 @@ impl Plugin for RustPluginAdapter { fn partial_bundling( &self, - module_group: &mut farmfe_core::module::module_group::ModuleGroup, + modules: &Vec, context: &Arc, hook_context: &PluginHookContext, ) -> Result>> { - self - .plugin - .partial_bundling(module_group, context, hook_context) + self.plugin.partial_bundling(modules, context, hook_context) } fn process_resource_pot_map( diff --git a/crates/plugin_html/src/lib.rs b/crates/plugin_html/src/lib.rs index ab046f3b4..0f098cb3a 100644 --- a/crates/plugin_html/src/lib.rs +++ b/crates/plugin_html/src/lib.rs @@ -4,13 +4,17 @@ use farmfe_core::{ context::CompilationContext, error::CompilationError, hashbrown::HashMap, - module::{HtmlModuleMetaData, ModuleId, ModuleMetaData, ModuleType}, + module::{ + module_group::{ModuleGroupGraph, ModuleGroupId}, + HtmlModuleMetaData, ModuleId, ModuleMetaData, ModuleType, + }, plugin::{ Plugin, PluginAnalyzeDepsHookParam, PluginHookContext, PluginLoadHookParam, PluginLoadHookResult, PluginParseHookParam, }, resource::{ resource_pot::{HtmlResourcePotMetaData, ResourcePot, ResourcePotMetaData, ResourcePotType}, + resource_pot_map::ResourcePotMap, Resource, ResourceType, }, swc_html_ast::Document, @@ -193,7 +197,12 @@ impl Plugin for FarmPluginHtml { let mut html_entry_resource = None; for rp_id in module_group.resource_pots() { - let rp = resource_pot_map.resource_pot(rp_id).unwrap(); + let rp = resource_pot_map.resource_pot(rp_id).unwrap_or_else(|| { + panic!( + "Resource pot {} not found in resource pot map", + rp_id.to_string() + ) + }); for resource in rp.resources() { if rp.modules().contains(&html_entry_id) { @@ -204,43 +213,12 @@ impl Plugin for FarmPluginHtml { dep_resources.extend(rp.resources().into_iter().map(|r| r.to_string())); } - let mut dep_module_groups = vec![]; - - module_group_graph.bfs(&module_group_id, &mut |mg_id| { - if mg_id != &module_group_id { - dep_module_groups.push(mg_id.clone()); - } - }); - - let mut dynamic_resources_map = HashMap::>::new(); - - for mg_id in dep_module_groups { - let mg = module_group_graph.module_group(&mg_id).unwrap(); - - for rp_id in mg.resource_pots() { - let rp = resource_pot_map.resource_pot(rp_id).unwrap(); - - if dynamic_resources_map.contains_key(&mg_id) { - let resources = dynamic_resources_map.get_mut(&mg_id).unwrap(); - - for r in rp.resources() { - let resource = resources_map.get(r).unwrap(); - resources.push((resource.name.clone(), resource.resource_type.clone())); - } - } else { - let mut resources = vec![]; - - for r in rp.resources() { - let resource = resources_map - .get(r) - .unwrap_or_else(|| panic!("{} not found", r)); - resources.push((resource.name.clone(), resource.resource_type.clone())); - } - - dynamic_resources_map.insert(mg_id.clone(), resources); - } - } - } + let dynamic_resources_map = get_dynamic_resources_map( + &*module_group_graph, + &module_group_id, + &*resource_pot_map, + resources_map, + ); resources_to_inject.insert( html_entry_resource.unwrap(), @@ -314,3 +292,55 @@ impl FarmPluginHtml { Self {} } } + +pub fn get_dynamic_resources_map( + module_group_graph: &ModuleGroupGraph, + module_group_id: &ModuleGroupId, + resource_pot_map: &ResourcePotMap, + resources_map: &HashMap, +) -> HashMap> { + let mut dep_module_groups = vec![]; + + module_group_graph.bfs(&module_group_id, &mut |mg_id| { + if mg_id != module_group_id { + dep_module_groups.push(mg_id.clone()); + } + }); + + let mut dynamic_resources_map = HashMap::>::new(); + + for mg_id in dep_module_groups { + let mg = module_group_graph.module_group(&mg_id).unwrap(); + + for rp_id in mg.resource_pots() { + let rp = resource_pot_map.resource_pot(rp_id).unwrap_or_else(|| { + panic!( + "Resource pot {} not found in resource pot map", + rp_id.to_string() + ) + }); + + if dynamic_resources_map.contains_key(&mg_id) { + let resources = dynamic_resources_map.get_mut(&mg_id).unwrap(); + + for r in rp.resources() { + let resource = resources_map.get(r).unwrap(); + resources.push((resource.name.clone(), resource.resource_type.clone())); + } + } else { + let mut resources = vec![]; + + for r in rp.resources() { + let resource = resources_map + .get(r) + .unwrap_or_else(|| panic!("{} not found", r)); + resources.push((resource.name.clone(), resource.resource_type.clone())); + } + + dynamic_resources_map.insert(mg_id.clone(), resources); + } + } + } + + dynamic_resources_map +} diff --git a/crates/plugin_html/src/resources_injector.rs b/crates/plugin_html/src/resources_injector.rs index 2c3f5e635..dc7f4340e 100644 --- a/crates/plugin_html/src/resources_injector.rs +++ b/crates/plugin_html/src/resources_injector.rs @@ -1,5 +1,5 @@ use farmfe_core::{ - config::{Mode, FARM_GLOBAL_THIS, FARM_MODULE_SYSTEM}, + config::{Mode, FARM_MODULE_SYSTEM}, hashbrown::HashMap, module::ModuleId, resource::ResourceType, @@ -47,6 +47,71 @@ impl ResourcesInjector { pub fn inject(&mut self, ast: &mut Document) { ast.visit_mut_with(self); } + + fn inject_initial_loaded_resources(&self, element: &mut Element) { + let mut initial_resources = vec![]; + initial_resources.extend(self.script_resources.clone()); + initial_resources.extend(self.css_resources.clone()); + + let initial_resources_code = initial_resources + .into_iter() + .map(|path| format!("'{}'", path)) + .collect::>() + .join(","); + + element.children.push(Child::Element(create_element( + "script", + Some(&format!( + r#"{}.setInitialLoadedResources({});"#, + FARM_MODULE_SYSTEM, + format!("[{}]", initial_resources_code) + )), + vec![(FARM_ENTRY, "true")], + ))); + } + + fn inject_dynamic_resources_map(&self, element: &mut Element) { + let mut dynamic_resources_code = String::new(); + + // inject dynamic resources + for (module_id, resources) in &self.dynamic_resources_map { + let mut resources_code = String::new(); + + for (resource_name, resource_type) in resources { + match resource_type { + ResourceType::Js => { + resources_code += &format!(r#"{{ path: '{}', type: 'script' }},"#, resource_name) + } + ResourceType::Css => { + resources_code += &format!(r#"{{ path: '{}', type: 'link' }},"#, resource_name) + } + _ => { + panic!( + "unknown supported type ({:?}) when injecting dynamic resources", + resource_type + ) + } + } + } + + dynamic_resources_code += &format!( + r#"'{}': [{}],"#, + module_id.id(self.mode.clone()), + resources_code + ); + } + + dynamic_resources_code = format!("{{ {} }}", dynamic_resources_code); + + element.children.push(Child::Element(create_element( + "script", + Some(&format!( + r#"{}.setDynamicModuleResourcesMap({});"#, + FARM_MODULE_SYSTEM, dynamic_resources_code + )), + vec![(FARM_ENTRY, "true")], + ))); + } } impl VisitMut for ResourcesInjector { @@ -93,79 +158,28 @@ impl VisitMut for ResourcesInjector { ))); } - let mut dynamic_resources_code = String::new(); - - // inject dynamic resources - for (module_id, resources) in &self.dynamic_resources_map { - let mut resources_code = String::new(); - - for (resource_name, resource_type) in resources { - match resource_type { - ResourceType::Js => { - resources_code += &format!(r#"{{ path: '{}', type: 'script' }},"#, resource_name) - } - ResourceType::Css => { - resources_code += &format!(r#"{{ path: '{}', type: 'link' }},"#, resource_name) - } - _ => { - panic!( - "unknown supported type ({:?}) when injecting dynamic resources", - resource_type - ) - } - } - } + self.inject_initial_loaded_resources(element); + self.inject_dynamic_resources_map(element); - dynamic_resources_code += &format!( - r#"'{}': [{}],"#, - module_id.id(self.mode.clone()), - resources_code - ); - } - - dynamic_resources_code = format!("{{ {} }}", dynamic_resources_code); - - element.children.push(Child::Element(create_element( - "script", - Some(&format!( - r#"var {} = globalThis || window || self; - var __farm_module_system_local__ = {}.{}; - __farm_module_system_local__.setDynamicModuleResourcesMap({});"#, - FARM_GLOBAL_THIS, FARM_GLOBAL_THIS, FARM_MODULE_SYSTEM, dynamic_resources_code - )), - vec![(FARM_ENTRY, "true")], - ))); element.children.push(Child::Element(create_element( "script", Some(&format!( - r#"var {} = globalThis || window || self; - var __farm_module_system_local__ = {}.{}; - __farm_module_system_local__.setPublicPaths(['{}']);"#, - FARM_GLOBAL_THIS, FARM_GLOBAL_THIS, FARM_MODULE_SYSTEM, self.public_path + r#"{}.setPublicPaths(['{}']);"#, + FARM_MODULE_SYSTEM, self.public_path )), vec![(FARM_ENTRY, "true")], ))); element.children.push(Child::Element(create_element( "script", - Some(&format!( - r#"var {} = globalThis || window || self; - var __farm_module_system_local__ = {}.{}; - __farm_module_system_local__.bootstrap();"#, - FARM_GLOBAL_THIS, FARM_GLOBAL_THIS, FARM_MODULE_SYSTEM - )), + Some(&format!(r#"{}.bootstrap();"#, FARM_MODULE_SYSTEM)), vec![(FARM_ENTRY, "true")], ))); for entry in &self.script_entries { element.children.push(Child::Element(create_element( "script", - Some(&format!( - r#"var {} = globalThis || window || self; - var __farm_module_system_local__ = {}.{}; - __farm_module_system_local__.require("{}")"#, - FARM_GLOBAL_THIS, FARM_GLOBAL_THIS, FARM_MODULE_SYSTEM, entry - )), + Some(&format!(r#"{}.require("{}")"#, FARM_MODULE_SYSTEM, entry)), vec![(FARM_ENTRY, "true")], ))); } diff --git a/crates/plugin_partial_bundling/src/lib.rs b/crates/plugin_partial_bundling/src/lib.rs index 3773c29bf..44eca4180 100644 --- a/crates/plugin_partial_bundling/src/lib.rs +++ b/crates/plugin_partial_bundling/src/lib.rs @@ -42,7 +42,7 @@ impl Plugin for FarmPluginPartialBundling { /// Whatever the order of the module group is, the result should be the same. fn partial_bundling( &self, - module_group: &mut ModuleGroup, + modules: &Vec, context: &Arc, _hook_context: &PluginHookContext, ) -> farmfe_core::error::Result>> { @@ -52,18 +52,26 @@ impl Plugin for FarmPluginPartialBundling { // First, generate ModuleBucket let mut module_bucket_map = HashMap::::new(); - for module_id in module_group.modules() { + for module_id in modules { let module = module_graph.module(module_id).unwrap(); + if module.resource_pot.is_some() { + panic!( + "Module {:?} has already been assigned to a resource pot: {:?}.", + module_id, + module.resource_pot.as_ref().unwrap() + ); + } + let add_module = |module_bucket_id: ModuleBucketId, module_bucket_map: &mut HashMap, - isolate: bool| { + customized_by_user: bool| { if module_bucket_map.contains_key(&module_bucket_id) { let module_bucket = module_bucket_map.get_mut(&module_bucket_id).unwrap(); module_bucket.add_module(module_id.clone()); } else { - let mut module_bucket = ModuleBucket::new(module_bucket_id.clone(), HashSet::new()); - module_bucket.isolate = isolate; + let mut module_bucket = + ModuleBucket::new(module_bucket_id.clone(), HashSet::new(), customized_by_user); module_bucket.add_module(module_id.clone()); module_bucket_map.insert(module_bucket_id, module_bucket); } @@ -82,18 +90,14 @@ impl Plugin for FarmPluginPartialBundling { if regex.iter().any(|r| r.is_match(&module_id.to_string())) { module_in_custom_buckets = true; - // This module is already in other resource pot, and we do not set isolate to true, skip it - if !bucket_config.isolate && module.resource_pot.is_some() { - continue; - } let bucket_id: ModuleBucketId = bucket_name.clone().into(); - add_module(bucket_id, &mut module_bucket_map, bucket_config.isolate); + add_module(bucket_id, &mut module_bucket_map, true); } } // The module is already in a resource pot, skip it. - if module_in_custom_buckets || module.resource_pot.is_some() { + if module_in_custom_buckets { continue; } @@ -116,7 +120,7 @@ impl Plugin for FarmPluginPartialBundling { for module_id in module_bucket.modules() { let module = module_graph.module(module_id).unwrap(); - let rule = if module_bucket.isolate { + let rule = if module_bucket.customized_by_user { (module.module_type.clone(), false) } else { (module.module_type.clone(), module.immutable) @@ -134,23 +138,33 @@ impl Plugin for FarmPluginPartialBundling { for (rule, module_ids) in rules_map.into_iter() { let (module_type, immutable) = rule; + let mut sorted_module_ids = module_ids.iter().map(|m| m.to_string()).collect::>(); + sorted_module_ids.sort(); + let id = sha256( format!( - "{}-{}-{}-{}-{}", - module_group.id.to_string(), + "{}-{}-{}-{}", module_bucket.id.to_string(), module_type.to_string(), - module_ids.iter().map(|m| m.to_string()).collect::>().join("_"), + sorted_module_ids.join("_"), immutable ) .as_bytes(), 8, ); - let mut resource_pot = ResourcePot::new( - ResourcePotId::new(id), - module_type.into(), - module_group.id.clone(), - ); + // let id = format!( + // "{}-{}-{}-{}", + // module_bucket.id.to_string(), + // module_type.to_string(), + // module_ids + // .iter() + // .map(|m| m.to_string()) + // .collect::>() + // .join("_"), + // immutable + // ) + // .replace("/", "+"); + let mut resource_pot = ResourcePot::new(ResourcePotId::new(id), module_type.into()); resource_pot.immutable = immutable; diff --git a/crates/plugin_partial_bundling/src/module_bucket.rs b/crates/plugin_partial_bundling/src/module_bucket.rs index 2fe0bb778..6a4e9ebc5 100644 --- a/crates/plugin_partial_bundling/src/module_bucket.rs +++ b/crates/plugin_partial_bundling/src/module_bucket.rs @@ -11,16 +11,16 @@ use farmfe_core::{ #[derive(Debug)] pub struct ModuleBucket { pub id: ModuleBucketId, - pub isolate: bool, + pub customized_by_user: bool, modules: HashSet, } impl ModuleBucket { - pub fn new(id: ModuleBucketId, modules: HashSet) -> Self { + pub fn new(id: ModuleBucketId, modules: HashSet, customized_by_user: bool) -> Self { Self { id, + customized_by_user, modules, - isolate: false, } } diff --git a/crates/plugin_runtime/src/lib.rs b/crates/plugin_runtime/src/lib.rs index a172f0a88..502f82f07 100644 --- a/crates/plugin_runtime/src/lib.rs +++ b/crates/plugin_runtime/src/lib.rs @@ -71,7 +71,6 @@ impl Plugin for FarmPluginRuntime { PartialBundlingModuleBucketsConfig { name: "FARM_RUNTIME".to_string(), test: vec![format!(".+{}", RUNTIME_SUFFIX)], - isolate: true, }, ); @@ -291,7 +290,9 @@ impl Plugin for FarmPluginRuntime { expr: Box::new(Expr::Lit(Lit::Str(Str { span: DUMMY_SP, value: resource_pot - .module_group + .entry_module + .as_ref() + .unwrap() .id(context.config.mode.clone()) .into(), raw: None, diff --git a/crates/plugin_script/tests/mod.rs b/crates/plugin_script/tests/mod.rs index bbe60edb0..d270f38f5 100644 --- a/crates/plugin_script/tests/mod.rs +++ b/crates/plugin_script/tests/mod.rs @@ -118,11 +118,8 @@ fn load_parse_and_analyze_deps() { ] ); - let mut resource_pot = ResourcePot::new( - ResourcePotId::new("index".to_string()), - ResourcePotType::Js, - "any".into(), - ); + let mut resource_pot = + ResourcePot::new(ResourcePotId::new("index".to_string()), ResourcePotType::Js); resource_pot.resource_pot_type = ResourcePotType::Js; resource_pot.meta = ResourcePotMetaData::Js(JsResourcePotMetaData { diff --git a/examples/react/src/comps/counter-button/index.tsx b/examples/react/src/comps/counter-button/index.tsx index b04355dd2..84e891e2b 100644 --- a/examples/react/src/comps/counter-button/index.tsx +++ b/examples/react/src/comps/counter-button/index.tsx @@ -4,7 +4,7 @@ import './index.css' const COUNT_DOWN = 60; const BUTTON_TEXT = 'Send'; -console.log(BUTTON_TEXT) + export function CounterButton() { const [count, setCount] = useState(COUNT_DOWN); const [text, setText] = useState(BUTTON_TEXT); diff --git a/examples/react/src/comps/description/index.tsx b/examples/react/src/comps/description/index.tsx index 27b1a1863..b12f23fa5 100644 --- a/examples/react/src/comps/description/index.tsx +++ b/examples/react/src/comps/description/index.tsx @@ -1,7 +1,11 @@ -import React from 'react'; +import React, { Suspense } from 'react'; + +const Clock = React.lazy(() => import('../clock')); export function Description() { return

Farm is a supper fast building engine written in rust.

+ +
} \ No newline at end of file diff --git a/examples/react/src/main.tsx b/examples/react/src/main.tsx index 11cf47a68..708678b9f 100644 --- a/examples/react/src/main.tsx +++ b/examples/react/src/main.tsx @@ -4,8 +4,6 @@ import { CounterButton } from './comps/counter-button'; import { Description } from './comps/description'; import './main.css'; -const Clock = React.lazy(() => import('./comps/clock')); - export function Main() { - return
+ return <>
} diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 6f273c928..c5e88664c 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,14 @@ # @farmfe/core +## 0.3.1 + +### Patch Changes + +- Fix lazy compilation and partial bundling bugs +- Updated dependencies + - @farmfe/runtime-plugin-hmr@3.0.1 + - @farmfe/runtime@0.3.1 + ## 0.3.0 ### Minor Changes diff --git a/packages/core/binding/binding.d.ts b/packages/core/binding/binding.d.ts index bea692287..c6d866800 100644 --- a/packages/core/binding/binding.d.ts +++ b/packages/core/binding/binding.d.ts @@ -20,6 +20,7 @@ export interface JsUpdateResult { removed: Array; modules: string; boundaries: Record>>; + dynamicResourcesMap?: Record>>; } export type JsCompiler = Compiler; export class Compiler { diff --git a/packages/core/binding/index.d.ts b/packages/core/binding/index.d.ts index ec9ae9dc1..20737395b 100644 --- a/packages/core/binding/index.d.ts +++ b/packages/core/binding/index.d.ts @@ -154,7 +154,6 @@ export interface Config { moduleBuckets?: { name: string; test: string[]; - isolate?: boolean; }[]; }; lazyCompilation?: boolean; diff --git a/packages/core/package.json b/packages/core/package.json index 0f6cddb9b..ccc4f9d36 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@farmfe/core", - "version": "0.3.0", + "version": "0.3.1", "main": "dist/index.js", "types": "dist/index.d.ts", "type": "module", @@ -61,8 +61,8 @@ "type-check": "tsc -p tsconfig.build.json --noEmit" }, "dependencies": { - "@farmfe/runtime": "workspace:^0.3.0", - "@farmfe/runtime-plugin-hmr": "workspace:^3.0.0", + "@farmfe/runtime": "workspace:^0.3.1", + "@farmfe/runtime-plugin-hmr": "workspace:^3.0.1", "@swc/helpers": "^0.4.9", "boxen": "^7.0.1", "chalk": "^5.2.0", diff --git a/packages/core/src/config/index.ts b/packages/core/src/config/index.ts index 13fa33bf3..9d9ea1083 100644 --- a/packages/core/src/config/index.ts +++ b/packages/core/src/config/index.ts @@ -216,7 +216,6 @@ async function readConfigFile( { name: 'farm.config.bundle.js', test: ['.+'], - isolate: true, }, ], }, diff --git a/packages/core/src/server/hmr-engine.ts b/packages/core/src/server/hmr-engine.ts index 42664265e..b4f3c49e7 100644 --- a/packages/core/src/server/hmr-engine.ts +++ b/packages/core/src/server/hmr-engine.ts @@ -6,6 +6,7 @@ import debounce from 'lodash.debounce'; import { Logger } from '../logger.js'; import { relative } from 'path'; import chalk from 'chalk'; +import type { Resource } from '@farmfe/runtime/src/resource-loader.js'; export class HmrEngine { private _updateQueue: string[] = []; @@ -46,6 +47,19 @@ export class HmrEngine { `${Date.now() - start}ms` )}` ); + let dynamicResourcesMap: Record = null; + + if (result.dynamicResourcesMap) { + for (const [key, value] of Object.entries(result.dynamicResourcesMap)) { + if (!dynamicResourcesMap) { + dynamicResourcesMap = {} as Record; + } + dynamicResourcesMap[key] = value.map((r) => ({ + path: r[0], + type: r[1] as 'script' | 'link', + })); + } + } const resultStr = `export default { added: [${result.added.map((r) => `'${r}'`).join(', ')}], @@ -53,6 +67,7 @@ export class HmrEngine { removed: [${result.removed.map((r) => `'${r}'`).join(', ')}], modules: ${result.modules.trim().slice(0, -1)}, boundaries: ${JSON.stringify(result.boundaries)}, + dynamicResourcesMap: ${JSON.stringify(dynamicResourcesMap)} }`; const id = Date.now().toString(); diff --git a/packages/core/src/server/middlewares/lazy-compilation.ts b/packages/core/src/server/middlewares/lazy-compilation.ts index cf34883aa..eb3881a31 100644 --- a/packages/core/src/server/middlewares/lazy-compilation.ts +++ b/packages/core/src/server/middlewares/lazy-compilation.ts @@ -6,6 +6,7 @@ import { Context } from 'koa'; import chalk from 'chalk'; import { DevServer } from '../index.js'; +import type { Resource } from '@farmfe/runtime/src/resource-loader.js'; export function lazyCompilation(server: DevServer) { const compiler = server.getCompiler(); @@ -26,8 +27,26 @@ export function lazyCompilation(server: DevServer) { ); if (result) { + let dynamicResourcesMap: Record = null; + + if (result.dynamicResourcesMap) { + for (const [key, value] of Object.entries( + result.dynamicResourcesMap + )) { + if (!dynamicResourcesMap) { + dynamicResourcesMap = {} as Record; + } + + dynamicResourcesMap[key] = value.map((r) => ({ + path: r[0], + type: r[1] as 'script' | 'link', + })); + } + } + const code = `export default { modules: ${result.modules.trim().slice(0, -1)}, + dynamicResourcesMap: ${JSON.stringify(dynamicResourcesMap)} }`; ctx.type = 'application/javascript'; ctx.body = code; diff --git a/packages/runtime-plugin-hmr/CHANGELOG.md b/packages/runtime-plugin-hmr/CHANGELOG.md index fe73b4f24..b8a42250c 100644 --- a/packages/runtime-plugin-hmr/CHANGELOG.md +++ b/packages/runtime-plugin-hmr/CHANGELOG.md @@ -1,5 +1,13 @@ # @farmfe/runtime-plugin-hmr +## 3.0.1 + +### Patch Changes + +- Fix lazy compilation and partial bundling bugs +- Updated dependencies + - @farmfe/runtime@0.3.1 + ## 3.0.0 ### Patch Changes diff --git a/packages/runtime-plugin-hmr/package.json b/packages/runtime-plugin-hmr/package.json index 5dcf2b087..63eaa1cfe 100644 --- a/packages/runtime-plugin-hmr/package.json +++ b/packages/runtime-plugin-hmr/package.json @@ -1,6 +1,6 @@ { "name": "@farmfe/runtime-plugin-hmr", - "version": "3.0.0", + "version": "3.0.1", "description": "Runtime hmr plugin of Farm", "author": { "name": "bright wu", @@ -11,9 +11,6 @@ "build": "tsc -p tsconfig.json --noEmit" }, "devDependencies": { - "@farmfe/runtime": "workspace:^0.3.0" - }, - "peerDependencies": { - "@farmfe/runtime": "workspace:^0.3.0" + "@farmfe/runtime": "workspace:^0.3.1" } } diff --git a/packages/runtime-plugin-hmr/src/hot-module-state.ts b/packages/runtime-plugin-hmr/src/hot-module-state.ts index 4a5784dad..3447e986a 100644 --- a/packages/runtime-plugin-hmr/src/hot-module-state.ts +++ b/packages/runtime-plugin-hmr/src/hot-module-state.ts @@ -29,8 +29,6 @@ export function applyHotUpdates( result: HmrUpdateResult, moduleSystem: ModuleSystem ) { - console.log(result); - for (const id of result.removed) { moduleSystem.delete(id); } @@ -48,6 +46,10 @@ export function applyHotUpdates( } } + if (result.dynamicResourcesMap) { + moduleSystem.dynamicModuleResourcesMap = result.dynamicResourcesMap; + } + // TODO support accept dependencies change for (const updated_id of Object.keys(result.boundaries)) { const chains = result.boundaries[updated_id]; diff --git a/packages/runtime-plugin-hmr/src/types.ts b/packages/runtime-plugin-hmr/src/types.ts index 86a5862cc..63d8d9bf8 100644 --- a/packages/runtime-plugin-hmr/src/types.ts +++ b/packages/runtime-plugin-hmr/src/types.ts @@ -1,3 +1,5 @@ +import type { Resource } from '@farmfe/runtime/src/resource-loader'; + export interface HmrUpdatePacket { id: string; } @@ -14,4 +16,5 @@ export interface HmrUpdateResult { string, (module: any, exports: any, require: (id: string) => any) => void >; + dynamicResourcesMap: Record | null; } diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index c92368f91..b3d667150 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -1,5 +1,11 @@ # @farmfe/runtime +## 0.3.1 + +### Patch Changes + +- Fix lazy compilation and partial bundling bugs + ## 0.3.0 ### Minor Changes diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 1aa5b69c6..02ab2a344 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@farmfe/runtime", - "version": "0.3.0", + "version": "0.3.1", "description": "Runtime of Farm", "author": { "name": "bright wu", diff --git a/packages/runtime/src/module-system.ts b/packages/runtime/src/module-system.ts index f6286ad86..a8eca109a 100644 --- a/packages/runtime/src/module-system.ts +++ b/packages/runtime/src/module-system.ts @@ -83,7 +83,7 @@ export class ModuleSystem { if (!resources || resources.length === 0) { throw new Error( - `Dynamic imported module "${module}" does not belong to any resource` + `Dynamic imported module "${moduleId}" does not belong to any resource` ); } @@ -139,6 +139,12 @@ export class ModuleSystem { } } + setInitialLoadedResources(resources: string[]) { + for (const resource of resources) { + this.resourceLoader.setLoadedResource(resource); + } + } + // These two methods are used to support dynamic module loading, the dynamic module info is collected by the compiler and injected during compile time // This method can also be called during runtime to add new dynamic modules setDynamicModuleResourcesMap( diff --git a/packages/runtime/src/resource-loader.ts b/packages/runtime/src/resource-loader.ts index 58287d238..d4ca8f617 100644 --- a/packages/runtime/src/resource-loader.ts +++ b/packages/runtime/src/resource-loader.ts @@ -21,24 +21,27 @@ export class ResourceLoader { this.publicPaths = publicPaths; } - async load(resource: Resource): Promise { + load(resource: Resource): Promise { let index = 0; while (index < this.publicPaths.length) { const publicPath = this.publicPaths[index]; const url = `${publicPath === '/' ? '' : publicPath}/${resource.path}`; - if (this._loadedResources[url]) { + if (this._loadedResources[resource.path]) { return; } - + let promise = Promise.resolve(); try { if (resource.type === 'script') { - await this._loadScript(url); + promise = this._loadScript(url); } else if (resource.type === 'link') { - await this._loadLink(url); + promise = this._loadLink(url); } - this._loadedResources[url] = true; - return; + + promise.then(() => { + this._loadedResources[resource.path] = true; + }); + return promise; } catch (e) { console.error(`[Farm] Failed to load resource "${url}"`, e); index++; @@ -46,6 +49,10 @@ export class ResourceLoader { } } + setLoadedResource(path: string) { + this._loadedResources[path] = true; + } + private _loadScript(path: string): Promise { if (targetEnv === 'node') { return import(path); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b780b80f5..6b5f8134c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,8 +90,8 @@ importers: packages/core: specifiers: - '@farmfe/runtime': workspace:^0.3.0 - '@farmfe/runtime-plugin-hmr': workspace:^3.0.0 + '@farmfe/runtime': workspace:^0.3.1 + '@farmfe/runtime-plugin-hmr': workspace:^3.0.1 '@napi-rs/cli': ^2.10.0 '@swc/helpers': ^0.4.9 '@types/figlet': ^1.5.5 @@ -145,7 +145,7 @@ importers: packages/runtime-plugin-hmr: specifiers: - '@farmfe/runtime': workspace:^0.3.0 + '@farmfe/runtime': workspace:^0.3.1 devDependencies: '@farmfe/runtime': link:../runtime