diff --git a/Cargo.lock b/Cargo.lock index 3f55d2c..8cceaa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -619,6 +619,7 @@ dependencies = [ "metacall", "metassr-utils", "serde_json", + "tracing", ] [[package]] diff --git a/crates/metassr-build/src/bundler.rs b/crates/metassr-build/src/bundler.rs deleted file mode 100644 index e69de29..0000000 diff --git a/crates/metassr-build/src/lib.rs b/crates/metassr-build/src/lib.rs index 36e6b7d..bac06db 100644 --- a/crates/metassr-build/src/lib.rs +++ b/crates/metassr-build/src/lib.rs @@ -1,4 +1,3 @@ -pub mod bundler; pub mod client; pub mod server; pub(crate) mod shared; diff --git a/crates/metassr-build/src/server/mod.rs b/crates/metassr-build/src/server/mod.rs index a81c964..06f9fa0 100644 --- a/crates/metassr-build/src/server/mod.rs +++ b/crates/metassr-build/src/server/mod.rs @@ -23,8 +23,6 @@ use std::{ ffi::OsStr, fs, path::{Path, PathBuf}, - thread::sleep, - time::Duration, }; use targets::TargetsGenerator; @@ -88,8 +86,6 @@ impl Build for ServerSideBuilder { return Err(anyhow!("Bundling failed: {e}")); } - // TODO: find a solution to remove this line - sleep(Duration::from_secs(1)); let dist = DistDir::new(&self.dist_path)?.analyze()?; let manifest = diff --git a/crates/metassr-build/src/server/renderer/head.rs b/crates/metassr-build/src/server/renderer/head.rs index 5ecaa21..dd6b5ea 100644 --- a/crates/metassr-build/src/server/renderer/head.rs +++ b/crates/metassr-build/src/server/renderer/head.rs @@ -1,38 +1,13 @@ use anyhow::{anyhow, Result}; use lazy_static::lazy_static; use metacall::{loaders, metacall_no_arg}; -use metassr_utils::cache_dir::CacheDir; -use std::{ - collections::HashMap, ffi::OsStr, path::PathBuf, sync::Mutex, thread::sleep, time::Duration, -}; +use metassr_utils::{cache_dir::CacheDir, checker::CheckerState}; +use std::{collections::HashMap, ffi::OsStr, path::PathBuf, sync::Mutex}; use metassr_bundler::WebBundler; lazy_static! { - static ref IS_HEAD_SCRIPT_LOADED: Mutex = - Mutex::new(HeadSciptLoadingState::default()); -} - -/// A detector for if the head is loaded or not. -#[derive(Debug)] -pub struct HeadSciptLoadingState(bool); - -impl HeadSciptLoadingState { - pub fn new() -> Self { - Self(false) - } - pub fn loaded(&mut self) { - self.0 = true - } - pub fn is_loaded(&self) -> bool { - self.0 - } -} - -impl Default for HeadSciptLoadingState { - fn default() -> Self { - Self::new() - } + static ref IS_HEAD_SCRIPT_LOADED: Mutex = Mutex::new(CheckerState::default()); } pub struct HeadRenderer { @@ -53,18 +28,16 @@ impl HeadRenderer { pub fn render(&mut self, bundler: bool) -> Result { let mut guard = IS_HEAD_SCRIPT_LOADED.lock().unwrap(); - if !guard.is_loaded() { + if !guard.is_true() { if bundler { self.bundle()?; - // TODO: remove this line - sleep(Duration::from_millis(500)); } let _ = loaders::from_single_file( "node", format!("{}/head.js", self.cache_dir.dir_path().display()), ); - guard.loaded() + guard.make_true() } drop(guard); diff --git a/crates/metassr-bundler/Cargo.toml b/crates/metassr-bundler/Cargo.toml index 8685451..afbc95e 100644 --- a/crates/metassr-bundler/Cargo.toml +++ b/crates/metassr-bundler/Cargo.toml @@ -11,3 +11,4 @@ lazy_static = "1.5.0" metacall = "0.4.1" metassr-utils = { path = "../metassr-utils" } serde_json = "1.0.128" +tracing = "0.1.40" diff --git a/crates/metassr-bundler/src/bundle.js b/crates/metassr-bundler/src/bundle.js index e4cf85c..2d0bb45 100644 --- a/crates/metassr-bundler/src/bundle.js +++ b/crates/metassr-bundler/src/bundle.js @@ -1,26 +1,22 @@ const { rspack } = require('@rspack/core'); -const path = require("path"); +const path = require('path'); function safelyParseJSON(json) { - let parsed - try { - parsed = JSON.parse(json) + return JSON.parse(json) } catch (_) { - parsed = undefined + return undefined } - - return parsed // Could be undefined! } let config = { output: { - filename: "[name].js", + filename: '[name].js', library: { - type: "commonjs2", + type: 'commonjs2', }, - publicPath: "" + publicPath: '' }, resolve: { extensions: ['.js', '.jsx', '.tsx', '.ts'] @@ -46,7 +42,7 @@ let config = { preserveAllComments: false, transform: { react: { - runtime: "automatic", + runtime: 'automatic', throwIfNamespace: true, useBuiltins: false, }, @@ -70,7 +66,7 @@ let config = { }, transform: { react: { - runtime: "automatic", + runtime: 'automatic', throwIfNamespace: true, useBuiltins: false, }, @@ -89,9 +85,7 @@ let config = { } } - - -function web_bundling(entry, dist) { +async function web_bundling(entry, dist) { const compiler = rspack( { @@ -111,19 +105,18 @@ function web_bundling(entry, dist) { ); - compiler.run((error, stats) => { - if (error) { - console.error(error); - process.exit(2); - } - if (stats && stats.hasErrors()) { - process.exitCode = 1; - } - if (!compiler || !stats) { - return; - } + return new Promise((resolve, reject) => { + return compiler.run((error, stats) => { + if (error) { + reject(error.message); + } + + if (error || stats?.hasErrors()) { + reject(stats.toString("errors-only")); + } + resolve(0); + }); }); - } module.exports = { diff --git a/crates/metassr-bundler/src/lib.rs b/crates/metassr-bundler/src/lib.rs index 21a124f..c4b6051 100644 --- a/crates/metassr-bundler/src/lib.rs +++ b/crates/metassr-bundler/src/lib.rs @@ -1,34 +1,38 @@ use anyhow::{anyhow, Result}; use lazy_static::lazy_static; -use metacall::{loaders, metacall, MetacallNull}; -use std::{collections::HashMap, ffi::OsStr, marker::Sized, path::Path, sync::Mutex}; +use metacall::{loaders, metacall, MetacallFuture, MetacallValue}; +use metassr_utils::checker::CheckerState; +use std::{ + collections::HashMap, + ffi::OsStr, + marker::Sized, + path::Path, + sync::{Arc, Condvar, Mutex}, +}; +use tracing::error; lazy_static! { - static ref IS_BUNDLING_SCRIPT_LOADED: Mutex = - Mutex::new(BundleSciptLoadingState::new()); + /// A detector for if the bundling script `./bundle.js` is loaded or not. It is used to solve multiple loading script error in metacall. + static ref IS_BUNDLING_SCRIPT_LOADED: Mutex = Mutex::new(CheckerState::new()); + + /// A simple checker to check if the bundling function is done or not. It is used to block the program until bundling done. + static ref IS_COMPLIATION_WAIT: Arc = Arc::new(CompilationWait::default()); } static BUILD_SCRIPT: &str = include_str!("./bundle.js"); const BUNDLING_FUNC: &str = "web_bundling"; -/// A detector for if the bundling script `./bundle.js` is loaded or not. -#[derive(Debug)] -pub struct BundleSciptLoadingState(bool); - -impl BundleSciptLoadingState { - pub fn new() -> Self { - Self(false) - } - pub fn loaded(&mut self) { - self.0 = true - } - pub fn is_loaded(&self) -> bool { - self.0 - } +/// A simple struct for compilation wait of the bundling function. +struct CompilationWait { + checker: Mutex, + cond: Condvar, } -impl Default for BundleSciptLoadingState { +impl Default for CompilationWait { fn default() -> Self { - Self::new() + Self { + checker: Mutex::new(CheckerState::with(false)), + cond: Condvar::new(), + } } } @@ -56,24 +60,56 @@ impl<'a> WebBundler<'a> { } pub fn exec(&self) -> Result<()> { let mut guard = IS_BUNDLING_SCRIPT_LOADED.lock().unwrap(); - if !guard.is_loaded() { + if !guard.is_true() { if let Err(e) = loaders::from_memory("node", BUILD_SCRIPT) { return Err(anyhow!("Cannot load bundling script: {e:?}")); } - guard.loaded(); + guard.make_true(); } drop(guard); - - if let Err(e) = metacall::( + + fn resolve(_: Box, _: Box) { + let compilation_wait = &*Arc::clone(&IS_COMPLIATION_WAIT); + let mut started = compilation_wait.checker.lock().unwrap(); + + started.make_true(); + // We notify the condvar that the value has changed + compilation_wait.cond.notify_one(); + } + + fn reject(err: Box, _: Box) { + let compilation_wait = &*Arc::clone(&IS_COMPLIATION_WAIT); + let mut started = compilation_wait.checker.lock().unwrap(); + + error!("Bundling rejected: {err:?}"); + + started.make_true(); + // We notify the condvar that the value has changed + compilation_wait.cond.notify_one(); + } + + let future = metacall::( BUNDLING_FUNC, [ serde_json::to_string(&self.targets)?, self.dist_path.to_str().unwrap().to_owned(), ], - ) { - return Err(anyhow!("Cannot running {BUNDLING_FUNC}(): {e:?}")); + ) + .unwrap(); + + future.then(resolve).catch(reject).await_fut(); + + // Wait for the thread to start up. + let compilation_wait = Arc::clone(&IS_COMPLIATION_WAIT); + + let mut started = compilation_wait.checker.lock().unwrap(); + + // Waiting till future done + while !started.is_true() { + started = Arc::clone(&IS_COMPLIATION_WAIT).cond.wait(started).unwrap(); } - + // Reset checker + started.make_false(); Ok(()) } } diff --git a/crates/metassr-create/src/lib.rs b/crates/metassr-create/src/lib.rs index 16e4be4..70390b3 100644 --- a/crates/metassr-create/src/lib.rs +++ b/crates/metassr-create/src/lib.rs @@ -33,7 +33,7 @@ impl Creator { } } pub fn generate(&self) -> Result<()> { - let template = self.template.load(&self)?; + let template = self.template.load(self)?; let root = PathBuf::from(&self.project_name); if root.exists() { diff --git a/crates/metassr-utils/src/checker.rs b/crates/metassr-utils/src/checker.rs new file mode 100644 index 0000000..fc4561c --- /dev/null +++ b/crates/metassr-utils/src/checker.rs @@ -0,0 +1,30 @@ +/// A simple checker state +#[derive(Debug)] +pub struct CheckerState(bool); +impl CheckerState { + pub fn new() -> Self { + Self(false) + } + + pub fn with(state: bool) -> Self { + Self(state) + } + + pub fn make_true(&mut self) { + self.0 = true + } + + pub fn make_false(&mut self) { + self.0 = false + } + + pub fn is_true(&self) -> bool { + self.0 + } +} + +impl Default for CheckerState { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/metassr-utils/src/lib.rs b/crates/metassr-utils/src/lib.rs index fb75249..dc81d5e 100644 --- a/crates/metassr-utils/src/lib.rs +++ b/crates/metassr-utils/src/lib.rs @@ -1,4 +1,5 @@ pub mod cache_dir; +pub mod checker; pub mod dist_analyzer; pub mod rand; pub mod src_analyzer; diff --git a/metassr-cli/src/cli/builder.rs b/metassr-cli/src/cli/builder.rs index 7b5aa92..6eff767 100644 --- a/metassr-cli/src/cli/builder.rs +++ b/metassr-cli/src/cli/builder.rs @@ -8,10 +8,7 @@ use metassr_build::server; use metassr_build::{client::ClientBuilder, server::ServerSideBuilder, traits::Build}; -use std::{ - thread::sleep, - time::{Duration, Instant}, -}; +use std::time::Instant; use tracing::{debug, error}; @@ -38,8 +35,6 @@ impl Exec for Builder { return Err(anyhow!("Couldn't continue building process.")); } - // TODO: find a solution to remove this - sleep(Duration::from_secs(1)); if let Err(e) = ServerSideBuilder::new("", &self.out_dir, self._type.into())?.build() { error!( diff --git a/metassr-cli/src/cli/creator.rs b/metassr-cli/src/cli/creator.rs index 20ad465..155e7c8 100644 --- a/metassr-cli/src/cli/creator.rs +++ b/metassr-cli/src/cli/creator.rs @@ -1,5 +1,5 @@ use clap::ValueEnum; -use metassr_create; +use metassr_create::Creator as MetassrCreator; use std::{fmt::Display, str::FromStr}; use tracing::{error, info}; @@ -30,7 +30,7 @@ impl Creator { impl Exec for Creator { fn exec(&self) -> anyhow::Result<()> { - match metassr_create::Creator::new( + match MetassrCreator::new( &self.project_name, &self.version, &self.description,