From 63f6d918f65636a903babe558c379832cea6fc55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A6=E7=94=B7?= Date: Wed, 26 Jun 2024 16:28:11 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=20anaylze=20watc?= =?UTF-8?q?h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/index.tsx | 31 +++++++++++++ client/mako.config.json | 7 ++- crates/mako/src/config/config.rs | 4 +- crates/mako/src/dev/mod.rs | 75 ++++++++++++++++++++++++++++++-- 4 files changed, 111 insertions(+), 6 deletions(-) diff --git a/client/index.tsx b/client/index.tsx index 5090d5865..eec49f61b 100644 --- a/client/index.tsx +++ b/client/index.tsx @@ -106,9 +106,15 @@ function App() { ); }; + const getFoamTreeData = (chartData: any) => { + const formatData = format(chartData?.chunkModules || []); + const resData = filterModulesForSize(formatData, 'statSize'); + return { groups: resData }; + }; const createFoamTree = (chartData: any) => { const formatData = format(chartData?.chunkModules || []); const resData = filterModulesForSize(formatData, 'statSize'); + debugger; return new FoamTree({ element: chartRef.current, layout: 'squarified', @@ -161,6 +167,16 @@ function App() { useEffect(() => { window.addEventListener('load', () => { setChartData(window?.chartData); + // 如果开启了热更新,那么启动 websocket 服务。 + if (window?.hmrWatch) { + const socket = new WebSocket('ws://localhost:3000/__/sendStatsData'); + + socket.addEventListener('message', (rawMessage) => { + const msg = JSON.parse(rawMessage.data); + console.log('msg==', msg); + setChartData(msg); + }); + } }); window.addEventListener('resize', resize); return () => { @@ -172,7 +188,22 @@ function App() { console.warn('数据未初始化!!'); return; } + // 如果已经实例化并且 chartData 发生改变,那么就重新设置值。 + if (treeMapRef.current) { + debugger; + treeMapRef.current.set({ + dataObject: getFoamTreeData(chartData), + }); + treeMapRef.current.update(); + return; + } treeMapRef.current = createFoamTree(chartData); + return () => { + if (treeMapRef.current) { + treeMapRef.current.dispose(); + treeMapRef.current = null; + } + }; }, [chartData]); return ( <> diff --git a/client/mako.config.json b/client/mako.config.json index 4b1efdec3..958397c4c 100644 --- a/client/mako.config.json +++ b/client/mako.config.json @@ -1,3 +1,8 @@ { - "devtool": false + "devtool": "source-map", + "stats": { "modules": true }, + "hash": true, + "analyze": { + "watch": true + } } diff --git a/crates/mako/src/config/config.rs b/crates/mako/src/config/config.rs index 9bb792b34..560aa21fb 100644 --- a/crates/mako/src/config/config.rs +++ b/crates/mako/src/config/config.rs @@ -200,7 +200,9 @@ pub struct StatsConfig { } #[derive(Deserialize, Serialize, Clone, Debug)] -pub struct AnalyzeConfig {} +pub struct AnalyzeConfig { + pub watch: Option, +} #[derive(Deserialize, Serialize, Clone, Debug)] pub enum CodeSplittingStrategy { diff --git a/crates/mako/src/dev/mod.rs b/crates/mako/src/dev/mod.rs index 976b35930..4df15fb0c 100644 --- a/crates/mako/src/dev/mod.rs +++ b/crates/mako/src/dev/mod.rs @@ -21,6 +21,7 @@ use {hyper, hyper_staticfile_jsutf8, hyper_tungstenite, open}; use crate::compiler::{Compiler, Context}; use crate::plugin::{PluginGenerateEndParams, PluginGenerateStats}; +use crate::stats::create_stats_info; use crate::utils::tokio_runtime; pub struct DevServer { @@ -43,7 +44,6 @@ impl DevServer { let root = self.root.clone(); let compiler = self.compiler.clone(); let txws_watch = txws.clone(); - if self.compiler.context.config.dev_server.is_some() { std::thread::spawn(move || { if let Err(e) = Self::watch_for_changes(root, compiler, txws_watch, callback) { @@ -53,7 +53,7 @@ impl DevServer { } else if let Err(e) = Self::watch_for_changes(root, compiler, txws_watch, callback) { eprintln!("Error watching files: {:?}", e); } - + let compiler = self.compiler.clone(); // server if self.compiler.context.config.dev_server.is_some() { let config_port = self @@ -70,15 +70,20 @@ impl DevServer { let txws = txws.clone(); let make_svc = make_service_fn(move |_conn| { let context = context.clone(); + let compiler = compiler.clone(); let txws = txws.clone(); async move { Ok::<_, hyper::Error>(service_fn(move |req| { let context = context.clone(); let txws = txws.clone(); + let compile = compiler.clone(); + let staticfile = hyper_staticfile_jsutf8::Static::new( context.config.output.path.clone(), ); - async move { Self::handle_requests(req, context, staticfile, txws).await } + async move { + Self::handle_requests(req, context, staticfile, txws, compile).await + } })) } }); @@ -124,8 +129,10 @@ impl DevServer { context: Arc, staticfile: hyper_staticfile_jsutf8::Static, txws: broadcast::Sender, + compiler: Arc, ) -> Result> { let path = req.uri().path(); + let path_without_slash_start = path.trim_start_matches('/'); let not_found_response = || { hyper::Response::builder() @@ -148,6 +155,31 @@ impl DevServer { Ok(not_found_response()) } } + "/__/sendStatsData" => { + if hyper_tungstenite::is_upgrade_request(&req) { + debug!("new websocket connection"); + let (response, websocket) = hyper_tungstenite::upgrade(req, None).unwrap(); + let txws = txws.clone(); + tokio_runtime::spawn(async move { + let receiver = txws.subscribe(); + if txws.receiver_count() > 0 { + let stats = create_stats_info(0, compiler.as_ref()); + let json_data = serde_json::to_string_pretty(&stats).unwrap(); + txws.send(WsMessage { + hash: 0, + stats_data: json_data, + }) + .unwrap(); + } + Self::handle_websocket_stats_data(websocket, receiver) + .await + .unwrap(); + }); + Ok(response) + } else { + Ok(not_found_response()) + } + } _ => { // for bundle outputs @@ -221,6 +253,32 @@ impl DevServer { } } + // 发送热更新之后的数据 + async fn handle_websocket_stats_data( + websocket: hyper_tungstenite::HyperWebsocket, + mut receiver: broadcast::Receiver, + ) -> Result<()> { + let websocket = websocket.await?; + let (mut sender, mut ws_recv) = websocket.split(); + let task = tokio_runtime::spawn(async move { + loop { + if let Ok(msg) = receiver.recv().await { + if sender.send(Message::text(msg.stats_data)).await.is_err() { + break; + } + } + } + }); + while let Some(message) = ws_recv.next().await { + if let Ok(Message::Close(_)) = message { + break; + } + } + debug!("websocket connection disconnected"); + task.abort(); + Ok(()) + } + // TODO: refact socket message data structure async fn handle_websocket( websocket: hyper_tungstenite::HyperWebsocket, @@ -406,8 +464,16 @@ impl DevServer { let receiver_count = txws.receiver_count(); debug!("receiver count: {}", receiver_count); + // 解析更新完之后的数据 + let stats = create_stats_info(0, compiler.as_ref()); + let json_data = serde_json::to_string_pretty(&stats).unwrap(); + let ws_new_data = WsMessage { + hash: **hmr_hash, + stats_data: json_data, + }; + if receiver_count > 0 { - txws.send(WsMessage { hash: **hmr_hash }).unwrap(); + txws.send(ws_new_data).unwrap(); debug!("send message to clients"); } @@ -429,4 +495,5 @@ pub struct Stats { #[derive(Clone, Debug)] struct WsMessage { hash: u64, + stats_data: String, } From ebaa815e20208c5e393ec19d2aa4f7491aea1b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A6=E7=94=B7?= Date: Wed, 26 Jun 2024 16:57:59 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=20anaylze=20watc?= =?UTF-8?q?h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/index.tsx | 1 - client/mako.config.json | 7 +------ crates/mako/src/dev/mod.rs | 1 - crates/mako/src/generate/analyze.rs | 8 ++++++++ 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/client/index.tsx b/client/index.tsx index eec49f61b..8c8184f5d 100644 --- a/client/index.tsx +++ b/client/index.tsx @@ -14,7 +14,6 @@ function App() { // ref 用于保存Treemap实例 const treeMapRef = useRef(null); const [chartData, setChartData] = useState(''); - // toolTip展示使用 const [tooltipContent, setToolTipContent] = useState(''); const createModulesTree = (modules) => { diff --git a/client/mako.config.json b/client/mako.config.json index 958397c4c..4b1efdec3 100644 --- a/client/mako.config.json +++ b/client/mako.config.json @@ -1,8 +1,3 @@ { - "devtool": "source-map", - "stats": { "modules": true }, - "hash": true, - "analyze": { - "watch": true - } + "devtool": false } diff --git a/crates/mako/src/dev/mod.rs b/crates/mako/src/dev/mod.rs index 4df15fb0c..12995a169 100644 --- a/crates/mako/src/dev/mod.rs +++ b/crates/mako/src/dev/mod.rs @@ -77,7 +77,6 @@ impl DevServer { let context = context.clone(); let txws = txws.clone(); let compile = compiler.clone(); - let staticfile = hyper_staticfile_jsutf8::Static::new( context.config.output.path.clone(), ); diff --git a/crates/mako/src/generate/analyze.rs b/crates/mako/src/generate/analyze.rs index 905585cfa..814fde798 100644 --- a/crates/mako/src/generate/analyze.rs +++ b/crates/mako/src/generate/analyze.rs @@ -10,6 +10,12 @@ pub struct Analyze {} impl Analyze { pub fn write_analyze(stats: &StatsJsonMap, context: Arc) -> Result<()> { + let analyze = context.config.analyze.clone().unwrap(); + let mut is_watch = false; + if analyze.watch.is_some() && analyze.watch.unwrap() { + is_watch = true; + } + let stats_json = serde_json::to_string_pretty(&stats).unwrap(); let html_str = format!( r#" @@ -23,12 +29,14 @@ impl Analyze {
"#, include_str!("../../../../client/dist/index.css"), stats_json, + is_watch, include_str!("../../../../client/dist/index.js").replace("", "<\\/script>") ); let report_path = context.config.output.path.join("report.html");