Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions crates/mako/src/dev/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ use anyhow::{anyhow, Ok, Result};
use rayon::prelude::*;
use tracing::debug;

use crate::ast::file::File;
use crate::build::BuildError;
use crate::compiler::Compiler;
use crate::generate::transform::transform_modules;
use crate::module::{Dependency, Module, ModuleId, ResolveType};
use crate::module_graph::ModuleGraph;
use crate::plugin::NextBuildParam;
use crate::resolve::{self, clear_resolver_cache};

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -253,6 +255,9 @@ impl Compiler {
update_result.added.extend(added_module_ids);

debug!("update_result: {:?}", &update_result);

self.context.plugin_driver.after_update(self)?;

Result::Ok(update_result)
}

Expand Down Expand Up @@ -326,14 +331,20 @@ impl Compiler {
resolved_deps.iter().for_each(|dep| {
let resolved_path = dep.resolver_resource.get_resolved_path();
let is_external = dep.resolver_resource.get_external().is_some();
let module_id = ModuleId::new(resolved_path.clone());
let module = if is_external {
let dep_module_id = ModuleId::new(resolved_path.clone());
let dep_module = if is_external {
Self::create_external_module(&dep.resolver_resource, self.context.clone())
} else {
Self::create_empty_module(&module_id)
Self::create_empty_module(&dep_module_id)
};
target_dependencies.push((module_id.clone(), dep.dependency.clone()));
dependence_modules.insert(module_id, module);
target_dependencies.push((dep_module_id.clone(), dep.dependency.clone()));
dependence_modules.insert(dep_module_id, dep_module);

self.context.plugin_driver.next_build(&NextBuildParam {
current_module: &module.id,
next_file: &File::new(resolved_path.clone(), self.context.clone()),
resource: &dep.resolver_resource,
});
});

let modules_diff = diff(&current_dependencies, &target_dependencies);
Expand Down
12 changes: 12 additions & 0 deletions crates/mako/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,18 @@ pub trait Plugin: Any + Send + Sync {
fn before_write_fs(&self, _path: &Path, _content: &[u8]) -> Result<()> {
Ok(())
}

fn after_update(&self, _compiler: &Compiler) -> Result<()> {
Ok(())
}
}

#[derive(Default)]
pub struct PluginDriver {
plugins: Vec<Arc<dyn Plugin>>,
}

#[derive(Debug)]
pub struct NextBuildParam<'a> {
pub current_module: &'a ModuleId,
pub next_file: &'a File,
Expand Down Expand Up @@ -426,4 +431,11 @@ impl PluginDriver {
}
Ok(content.clone())
}

pub fn after_update(&self, compiler: &Compiler) -> Result<()> {
for plugin in &self.plugins {
plugin.after_update(compiler)?;
}
Ok(())
}
}
148 changes: 117 additions & 31 deletions crates/mako/src/plugins/ssu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use crate::config::{
use crate::generate::chunk::ChunkType;
use crate::generate::chunk_pot::util::{hash_hashmap, hash_vec};
use crate::generate::generate_chunks::{ChunkFile, ChunkFileType};
use crate::generate::transform::transform_modules;
use crate::module::ModuleId;
use crate::plugin::{NextBuildParam, Plugin, PluginLoadParam};
use crate::resolve::ResolverResource;

Expand Down Expand Up @@ -64,9 +66,17 @@ impl CacheState {
}
}

#[derive(Debug, Copy, Clone, Default)]
enum SSUScanStage {
#[default]
FirstBuild,
Updating,
}

pub struct SUPlus {
scanning: Arc<Mutex<bool>>,
enabled: Arc<Mutex<bool>>,
stage: Arc<Mutex<SSUScanStage>>,
cache_valid: Arc<Mutex<bool>>,
will_full_rebuild: Arc<Mutex<bool>>,
dependence_node_module_files: DashSet<File>,
cached_state: Arc<Mutex<CacheState>>,
current_state: Arc<Mutex<CacheState>>,
Expand All @@ -89,13 +99,15 @@ impl From<bool> for CodeType {
}

const SSU_ENTRY_PREFIX: &str = "virtual:ssu:entry:node_modules:";
const SSU_MOCK_CSS_FILE: &str = "virtual:C:/node_modules/css/css.css";
const SSU_MOCK_CSS_FILE: &str = "virtual:C:/node_modules/_mako_css/css.css";
const SSU_MOCK_JS_FILE: &str = "virtual:C:/node_modules/_mako_js/js.js";

impl SUPlus {
pub fn new() -> Self {
SUPlus {
scanning: Arc::new(Mutex::new(true)),
enabled: Arc::new(Mutex::new(true)),
stage: Arc::new(Mutex::new(Default::default())),
cache_valid: Arc::new(Mutex::new(true)),
will_full_rebuild: Arc::new(Mutex::new(false)),
dependence_node_module_files: Default::default(),
cached_state: Default::default(),
current_state: Default::default(),
Expand Down Expand Up @@ -127,25 +139,29 @@ impl SUPlus {
alias_hash.wrapping_add(external_hash)
}

fn start_scan(&self) {
let mut s = self.scanning.lock().unwrap();
*s = true;
fn in_building_stage(&self) {
let mut s = self.stage.lock().unwrap();
*s = SSUScanStage::FirstBuild;
}

fn stop_scan(&self) {
let mut s = self.scanning.lock().unwrap();
*s = false;
fn in_updating_stage(&self) {
let mut s = self.stage.lock().unwrap();
*s = SSUScanStage::Updating;
}

fn enable_cache(&self) {
let mut e = self.enabled.lock().unwrap();
let mut e = self.cache_valid.lock().unwrap();
*e = true;
}

fn disable_cache(&self) {
let mut e = self.enabled.lock().unwrap();
let mut e = self.cache_valid.lock().unwrap();
*e = false;
}

fn will_full_rebuild(&self) -> bool {
*self.will_full_rebuild.lock().unwrap()
}
}

impl Plugin for SUPlus {
Expand Down Expand Up @@ -227,14 +243,22 @@ impl Plugin for SUPlus {
let port = context.config.dev_server.as_ref().unwrap().port.to_string();
let host = &context.config.dev_server.as_ref().unwrap().host;
let host = if host == "0.0.0.0" { "127.0.0.1" } else { host };
let hmr_runtime = include_str!("../runtime/runtime_hmr_entry.js")
.to_string()
.replace("__PORT__", &port)
.replace("__HOST__", host);
let hmr_runtime = if context.config.hmr.is_some() {
include_str!("../runtime/runtime_hmr_entry.js")
.to_string()
.replace("__PORT__", &port)
.replace("__HOST__", host)
} else {
"".to_string()
};

let content = format!(
r#"
require("{SSU_MOCK_CSS_FILE}");
try{{
// it will throw due to the node_module chunk is not loaded yet
require("{SSU_MOCK_JS_FILE}");
}}catch(e){{}};
let patch = require._su_patch();
console.log(patch);
{}
Expand Down Expand Up @@ -263,6 +287,14 @@ module.export = Promise.all(
if param.file.path.starts_with(SSU_MOCK_CSS_FILE) {
return Ok(Some(Content::Css("._mako_mock_css { }".to_string())));
}

if param.file.path.starts_with(SSU_MOCK_JS_FILE) {
return Ok(Some(Content::Js(JsContent {
is_jsx: false,
content: "console.log('_mako_ssu_placeholder')".to_string(),
})));
}

Ok(None)
}

Expand All @@ -284,7 +316,7 @@ module.export = Promise.all(
.to_string()
);

match (from, to) {
let should_transform = match (from, to) {
(CodeType::SourceCode, CodeType::Dependency) => {
if let ResolverResource::Resolved(resolved) = &next_build_param.resource {
self.dependence_node_module_files
Expand All @@ -304,14 +336,38 @@ module.export = Promise.all(
v.as_str().unwrap_or("0.0.0").to_string()
});

self.current_state
.lock()
.unwrap()
.cached_boundaries
.insert(path_name, version);
let mut ssu_state = self.current_state.lock().unwrap();

let stage = self.stage.lock().unwrap();

let scanning = *self.scanning.lock().unwrap();
!scanning
match *stage {
SSUScanStage::FirstBuild => {
ssu_state.cached_boundaries.insert(path_name, version);
false
}
SSUScanStage::Updating => {
let mut cache_valid = self.cache_valid.lock().unwrap();

if *cache_valid {
// cache hit
if let Some(cached_version) =
ssu_state.cached_boundaries.get(&path_name)
&& *cached_version == version
{
false
} else {
ssu_state.cached_boundaries.insert(path_name, version);
*cache_valid = false;
*self.will_full_rebuild.lock().unwrap() = true;

true
}
} else {
ssu_state.cached_boundaries.insert(path_name, version);
true
}
}
}
} else {
true
}
Expand All @@ -337,7 +393,37 @@ module.export = Promise.all(
true
}
_ => true,
};

debug!(
"{} -> {} {should_transform}",
next_build_param.current_module.id,
next_build_param
.next_file
.pathname
.to_string_lossy()
.to_string()
);

should_transform
}

fn after_update(&self, compiler: &Compiler) -> Result<()> {
if self.will_full_rebuild() {
let files = self
.dependence_node_module_files
.iter()
.map(|f| f.clone())
.collect::<Vec<File>>();

debug!("start to build after update");
let mut modules = compiler.build(files.clone())?;

modules.extend(files.into_iter().map(|f| ModuleId::from(f.path)));

transform_modules(modules.into_iter().collect(), &compiler.context)?
}
Ok(())
Comment on lines +411 to +426
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

处理 after_update 方法中的可能错误

after_update 方法中,调用了 transform_modules,该方法可能返回错误。建议在调用后检查错误并进行适当的错误处理,以避免在运行时出现未捕获的异常。

}

fn after_build(&self, _context: &Arc<Context>, compiler: &Compiler) -> Result<()> {
Expand All @@ -360,6 +446,7 @@ module.export = Promise.all(

if cache_valid {
self.enable_cache();
self.in_updating_stage();
return Ok(());
}

Expand All @@ -371,12 +458,10 @@ module.export = Promise.all(
.map(|f| f.clone())
.collect::<Vec<File>>();

self.stop_scan();

debug!("start to build dep");
compiler.build(files)?;

self.start_scan();
self.in_updating_stage();

#[cfg(debug_assertions)]
{
Expand All @@ -397,7 +482,8 @@ module.export = Promise.all(
chunk_files: &[ChunkFile],
context: &Arc<Context>,
) -> Result<()> {
if *self.enabled.lock().unwrap() {
if *self.cache_valid.lock().unwrap() {
debug!("cache valid skip generate chunk files");
return Ok(());
}

Expand Down Expand Up @@ -470,7 +556,7 @@ module.export = Promise.all(
}

fn runtime_plugins(&self, _context: &Arc<Context>) -> Result<Vec<String>> {
if *self.enabled.lock().unwrap() {
if *self.cache_valid.lock().unwrap() {
let cache = self.cached_state.lock().unwrap();

let code = format!(
Expand Down Expand Up @@ -499,7 +585,7 @@ requireModule._su_patch = function(){{
.into_iter()
.filter(|c| c.chunk_type == ChunkType::Sync)
.for_each(|c| {
println!("chunk: {}", c.filename());
debug!("chunk: {}", c.filename());
});

Ok(vec![r#"
Expand Down