From 3a199a45442272bf0741eff5a3292c5e094c07f9 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sat, 23 Nov 2024 17:03:10 +0000 Subject: [PATCH 1/8] POC adding HTML playground support --- Cargo.lock | 309 ++++++++++++++++++ crates/bsnext_dto/src/lib.rs | 2 + crates/bsnext_html/Cargo.toml | 9 + crates/bsnext_html/src/lib.rs | 129 ++++++++ crates/bsnext_input/src/lib.rs | 8 +- crates/bsnext_input/src/paths.rs | 67 ---- crates/bsnext_md/src/md_fs.rs | 6 +- crates/bsnext_md/src/md_writer.rs | 2 +- crates/bsnext_system/Cargo.toml | 1 + crates/bsnext_system/src/input_fs.rs | 15 +- crates/bsnext_system/src/lib.rs | 15 +- crates/bsnext_system/src/monitor.rs | 21 +- crates/bsnext_system/src/start_kind.rs | 15 +- .../src/start_kind/start_from_inputs.rs | 11 +- .../src/start_kind/start_from_paths.rs | 79 ++++- crates/bsnext_yaml/src/lib.rs | 6 +- crates/bsnext_yaml/src/yaml_fs.rs | 6 +- crates/bsnext_yaml/src/yaml_writer.rs | 13 + examples/html/playground.html | 17 + 19 files changed, 627 insertions(+), 104 deletions(-) create mode 100644 crates/bsnext_html/Cargo.toml create mode 100644 crates/bsnext_html/src/lib.rs delete mode 100644 crates/bsnext_input/src/paths.rs create mode 100644 crates/bsnext_yaml/src/yaml_writer.rs create mode 100644 examples/html/playground.html diff --git a/Cargo.lock b/Cargo.lock index 0ee08bf..fa3ded2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,6 +548,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "bsnext_html" +version = "0.1.0" +dependencies = [ + "bsnext_input", + "scraper", + "unindent", +] + [[package]] name = "bsnext_input" version = "0.2.2" @@ -632,6 +641,7 @@ dependencies = [ "bsnext_dto", "bsnext_example", "bsnext_fs", + "bsnext_html", "bsnext_input", "bsnext_md", "bsnext_output", @@ -932,6 +942,29 @@ dependencies = [ "typenum", ] +[[package]] +name = "cssparser" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "ctor" version = "0.2.8" @@ -957,6 +990,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -967,6 +1011,27 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "ego-tree" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c6ba7d4eec39eaa9ab24d44a0e73a7949a1095a8b3f3abb11eddf27dbb56a53" + [[package]] name = "either" version = "1.12.0" @@ -1072,6 +1137,16 @@ dependencies = [ "libc", ] +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.30" @@ -1161,6 +1236,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1171,6 +1255,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -1259,6 +1352,20 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "html5ever" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e15626aaf9c351bc696217cbe29cb9b5e86c43f8a46b5e2f5c6c5cf7cb904ce" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "http" version = "1.1.0" @@ -1580,6 +1687,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + [[package]] name = "markdown" version = "1.0.0-alpha.17" @@ -1589,6 +1702,20 @@ dependencies = [ "unicode-id", ] +[[package]] +name = "markup5ever" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c88c6129bd24319e62a0359cb6b958fa7e8be6e19bb1663bc396b90883aca5" +dependencies = [ + "log", + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1761,6 +1888,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nom" version = "7.1.3" @@ -1959,6 +2092,77 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.5" @@ -2022,6 +2226,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "proc-macro2" version = "1.0.81" @@ -2284,6 +2494,22 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scraper" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0e749d29b2064585327af5038a5a8eb73aeebad4a3472e83531a436563f7208" +dependencies = [ + "ahash", + "cssparser", + "ego-tree", + "getopts", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + [[package]] name = "sct" version = "0.7.1" @@ -2317,6 +2543,25 @@ dependencies = [ "libc", ] +[[package]] +name = "selectors" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" +dependencies = [ + "bitflags 2.5.0", + "cssparser", + "derive_more", + "fxhash", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "servo_arc", + "smallvec", +] + [[package]] name = "semver" version = "1.0.22" @@ -2398,6 +2643,15 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "servo_arc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2454,6 +2708,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -2501,12 +2761,44 @@ dependencies = [ "syn", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + [[package]] name = "strip-ansi-escapes" version = "0.2.0" @@ -2622,6 +2914,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "terminal_size" version = "0.3.0" @@ -3141,6 +3444,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + [[package]] name = "unsafe-libyaml" version = "0.2.11" diff --git a/crates/bsnext_dto/src/lib.rs b/crates/bsnext_dto/src/lib.rs index 2227ca0..08b08ca 100644 --- a/crates/bsnext_dto/src/lib.rs +++ b/crates/bsnext_dto/src/lib.rs @@ -287,6 +287,7 @@ pub enum InputErrorDTO { DirError(String), YamlError(String), MarkdownError(String), + HtmlError(String), Io(String), UnsupportedExtension(String), MissingExtension(String), @@ -305,6 +306,7 @@ impl From<&InputError> for InputErrorDTO { e @ InputError::PortError(_) => InputErrorDTO::PortError(e.to_string()), e @ InputError::DirError(_) => InputErrorDTO::DirError(e.to_string()), e @ InputError::MarkdownError(_) => InputErrorDTO::MarkdownError(e.to_string()), + e @ InputError::HtmlError(_) => InputErrorDTO::HtmlError(e.to_string()), e @ InputError::YamlError(_) => InputErrorDTO::YamlError(e.to_string()), e @ InputError::Io(_) => InputErrorDTO::Io(e.to_string()), e @ InputError::UnsupportedExtension(_) => { diff --git a/crates/bsnext_html/Cargo.toml b/crates/bsnext_html/Cargo.toml new file mode 100644 index 0000000..11d0fe2 --- /dev/null +++ b/crates/bsnext_html/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "bsnext_html" +version = "0.1.0" +edition = "2021" + +[dependencies] +bsnext_input = { path = "../bsnext_input" } +unindent = "0.2.3" +scraper = "0.21.0" diff --git a/crates/bsnext_html/src/lib.rs b/crates/bsnext_html/src/lib.rs new file mode 100644 index 0000000..ad87588 --- /dev/null +++ b/crates/bsnext_html/src/lib.rs @@ -0,0 +1,129 @@ +use bsnext_input::playground::Playground; +use bsnext_input::server_config::{ServerConfig, ServerIdentity}; +use bsnext_input::{Input, InputCreation, InputError}; +use std::fs::read_to_string; +use std::path::Path; + +pub struct HtmlFs; + +impl InputCreation for HtmlFs { + fn from_input_path>( + path: P, + server_ids: Vec, + ) -> Result> { + let str = read_to_string(path).map_err(|e| Box::new(e.into()))?; + let input = playground_html_str_to_input(&str, server_ids) + .map_err(|e| Box::new(InputError::HtmlError(e.to_string())))?; + Ok(input) + } + + fn from_input_str>(content: P) -> Result> { + let input = playground_html_str_to_input(&content.as_ref(), vec![]) + .map_err(|e| Box::new(InputError::HtmlError(e.to_string())))?; + Ok(input) + } +} + +fn playground_html_str_to_input( + html: &str, + server_ids: Vec, +) -> Result> { + use unindent::unindent; + let mut document = scraper::Html::parse_fragment(html); + let style = scraper::Selector::parse("style:first-of-type").unwrap(); + let script = scraper::Selector::parse("script:first-of-type").unwrap(); + let mut style_elems = document.select(&style); + let mut script_elems = document.select(&script); + let mut removals = vec![]; + + let mut playground = Playground { + html: "".to_string(), + css: None, + js: None, + }; + + if let Some(style) = style_elems.next() { + removals.push(style.id()); + let t = style.text().nth(0).unwrap(); + let unindented = unindent(t); + playground.css = Some(unindented); + } + + if let Some(script) = script_elems.next() { + removals.push(script.id()); + let t = script.text().nth(0).unwrap(); + let unindented = unindent(t); + playground.js = Some(unindented); + } + + for x in removals { + document.tree.get_mut(x).unwrap().detach(); + } + + let as_html = document.html(); + let trimmed = as_html + .strip_prefix("") + .unwrap() + .strip_suffix("") + .unwrap(); + let un_indented = unindent(trimmed); + playground.html = un_indented; + let mut input = Input::default(); + let server = ServerConfig { + routes: playground.as_routes(), + identity: server_ids + .get(0) + .map(ToOwned::to_owned) + .unwrap_or_else(|| ServerIdentity::named()), + ..Default::default() + }; + input.servers.push(server); + // dbg!(&input.servers.get(0).unwrap().identity); + Ok(input) +} + +#[test] +fn has_selector() { + let mut html = r#" + huh? + +
+
    +
  • first
  • +
  • second
  • +
  • third
  • +
+
+
+

+
+ "#; + + let mut document = scraper::Html::parse_fragment(html); + + #[derive(Debug)] + struct Item { + pub html: String, + pub css: Option, + pub js: Option, + } + + let mut item = Item { + html: "".to_string(), + css: None, + js: None, + }; + + dbg!(&item); + + // fs::write("origin.html", item.html).unwrap(); + // fs::write("origin.js", item.js.unwrap()).unwrap(); + // fs::write("origin.css", item.css.unwrap()).unwrap(); +} diff --git a/crates/bsnext_input/src/lib.rs b/crates/bsnext_input/src/lib.rs index e5f43c7..b5965cb 100644 --- a/crates/bsnext_input/src/lib.rs +++ b/crates/bsnext_input/src/lib.rs @@ -10,7 +10,6 @@ pub mod client_config; #[cfg(test)] pub mod input_test; pub mod path_def; -pub mod paths; pub mod playground; pub mod route; pub mod route_manifest; @@ -91,7 +90,10 @@ pub trait InputSource { } pub trait InputCreation { - fn from_input_path>(path: P) -> Result>; + fn from_input_path>( + path: P, + server_ids: Vec, + ) -> Result>; fn from_input_str>(content: P) -> Result>; } @@ -125,6 +127,8 @@ pub enum InputError { DirError(#[from] DirError), #[error("Markdown error: {0}")] MarkdownError(String), + #[error("HTML error: {0}")] + HtmlError(String), #[error("{0}")] YamlError(#[from] YamlError), #[error(transparent)] diff --git a/crates/bsnext_input/src/paths.rs b/crates/bsnext_input/src/paths.rs deleted file mode 100644 index 1b345ae..0000000 --- a/crates/bsnext_input/src/paths.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::route::{DirRoute, Route, RouteKind}; -use crate::server_config::{ServerConfig, ServerIdentity}; -use crate::{Input, PathDefinition, PathDefs, PathError}; -use std::path::{Path, PathBuf}; - -pub fn from_paths>( - cwd: &Path, - paths: &[T], - identity: ServerIdentity, -) -> Result { - let path_defs = paths - .iter() - .map(|p| { - let pb = PathBuf::from(p.as_ref()); - if pb.is_absolute() { - return PathDefinition { - input: p.as_ref().to_string(), - cwd: cwd.to_path_buf(), - absolute: pb, - }; - } else { - return PathDefinition { - input: p.as_ref().to_string(), - cwd: cwd.to_path_buf(), - absolute: cwd.join(pb), - }; - } - }) - .map(|path_def| { - let exists = path_def.absolute.exists(); - (path_def, exists) - }) - .collect::>(); - - let invalid = path_defs - .into_iter() - .filter_map(|(pb, exists)| if exists { None } else { Some(pb) }) - .collect::>(); - - if !invalid.is_empty() { - return Err(PathError::MissingPaths { - paths: PathDefs(invalid), - }); - } - - let server = ServerConfig { - identity, - routes: paths - .iter() - .map(|p| -> Route { - let str = p.as_ref(); - Route { - path: "/".to_string().parse().unwrap(), - kind: RouteKind::Dir(DirRoute { - dir: str.into(), - base: None, - }), - ..Default::default() - } - }) - .collect(), - ..Default::default() - }; - Ok(Input { - servers: vec![server], - }) -} diff --git a/crates/bsnext_md/src/md_fs.rs b/crates/bsnext_md/src/md_fs.rs index 1e67db6..93fb2bc 100644 --- a/crates/bsnext_md/src/md_fs.rs +++ b/crates/bsnext_md/src/md_fs.rs @@ -1,4 +1,5 @@ use crate::md_to_input; +use bsnext_input::server_config::ServerIdentity; use bsnext_input::{Input, InputCreation, InputError}; use std::fs::read_to_string; use std::path::Path; @@ -6,7 +7,10 @@ use std::path::Path; pub struct MdFs; impl InputCreation for MdFs { - fn from_input_path>(path: P) -> Result> { + fn from_input_path>( + path: P, + server_ids: Vec, + ) -> Result> { let str = read_to_string(path).map_err(|e| Box::new(e.into()))?; let input = md_to_input(&str).map_err(|e| Box::new(InputError::MarkdownError(e.to_string())))?; diff --git a/crates/bsnext_md/src/md_writer.rs b/crates/bsnext_md/src/md_writer.rs index 0b3f2a2..a911f3d 100644 --- a/crates/bsnext_md/src/md_writer.rs +++ b/crates/bsnext_md/src/md_writer.rs @@ -12,7 +12,7 @@ impl InputWriter for MdWriter { } } -pub fn _input_to_str(input: &Input) -> String { +fn _input_to_str(input: &Input) -> String { let mut chunks = vec![]; if let Some(server_config) = input.servers.first() { #[derive(Debug, serde::Serialize)] diff --git a/crates/bsnext_system/Cargo.toml b/crates/bsnext_system/Cargo.toml index 150b2f0..4ff721b 100644 --- a/crates/bsnext_system/Cargo.toml +++ b/crates/bsnext_system/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] bsnext_input = { path = "../bsnext_input" } bsnext_md = { path = "../bsnext_md" } +bsnext_html = { path = "../bsnext_html" } bsnext_yaml = { path = "../bsnext_yaml" } bsnext_tracing = { path = "../bsnext_tracing" } bsnext_core = { path = "../bsnext_core" } diff --git a/crates/bsnext_system/src/input_fs.rs b/crates/bsnext_system/src/input_fs.rs index 02a25e5..9467cfd 100644 --- a/crates/bsnext_system/src/input_fs.rs +++ b/crates/bsnext_system/src/input_fs.rs @@ -1,13 +1,22 @@ +use bsnext_input::server_config::ServerIdentity; use bsnext_input::{Input, InputCreation, InputError}; use std::path::Path; -pub fn from_input_path>(path: P) -> Result> { +pub fn from_input_path>( + path: P, + prev_identities: Vec, +) -> Result> { match path.as_ref().extension().and_then(|x| x.to_str()) { None => Err(Box::new(InputError::MissingExtension( path.as_ref().to_owned(), ))), - Some("yml") | Some("yaml") => bsnext_yaml::yaml_fs::YamlFs::from_input_path(path), - Some("md") | Some("markdown") => bsnext_md::md_fs::MdFs::from_input_path(path), + Some("yml") | Some("yaml") => { + bsnext_yaml::yaml_fs::YamlFs::from_input_path(path, prev_identities) + } + Some("md") | Some("markdown") => { + bsnext_md::md_fs::MdFs::from_input_path(path, prev_identities) + } + Some("html") => bsnext_html::HtmlFs::from_input_path(path, prev_identities), Some(other) => Err(Box::new(InputError::UnsupportedExtension( other.to_string(), ))), diff --git a/crates/bsnext_system/src/lib.rs b/crates/bsnext_system/src/lib.rs index 932d729..b0c9fd0 100644 --- a/crates/bsnext_system/src/lib.rs +++ b/crates/bsnext_system/src/lib.rs @@ -24,6 +24,7 @@ use bsnext_core::server::handler_routes_updated::RoutesUpdated; use bsnext_core::servers_supervisor::get_servers_handler::GetServersMessage; use bsnext_core::servers_supervisor::start_handler::ChildCreatedInsert; use bsnext_dto::internal::{AnyEvent, ChildResult, InternalEvents}; +use bsnext_input::server_config::ServerIdentity; use bsnext_input::startup::{ DidStart, StartupContext, StartupError, StartupResult, SystemStart, SystemStartArgs, }; @@ -43,11 +44,17 @@ pub struct BsSystem { self_addr: Option>, servers_addr: Option>, any_event_sender: Option>, - input_monitors: Vec>, + input_monitors: Vec, any_monitors: HashMap, cwd: Option, } +#[derive(Debug, Clone)] +pub struct InputMonitor { + pub addr: Addr, + pub server_identities: Vec, +} + #[derive(Debug)] pub struct EventWithSpan { pub evt: ExternalEventsDTO, @@ -297,6 +304,11 @@ impl Handler for BsSystem { ctx.notify(MonitorInput { path: path.clone(), cwd: cwd.clone(), + server_identities: input + .servers + .iter() + .map(|x| x.identity.clone()) + .collect::>(), }); self.accept_watchables(&input); @@ -314,6 +326,7 @@ impl Handler for BsSystem { ctx.notify(MonitorInput { path: path.clone(), cwd: cwd.clone(), + server_identities: vec![], }); self.publish_any_event(AnyEvent::Internal(InternalEvents::InputError(input_error))); Ok(DidStart::Started) diff --git a/crates/bsnext_system/src/monitor.rs b/crates/bsnext_system/src/monitor.rs index 07186d9..6a50bae 100644 --- a/crates/bsnext_system/src/monitor.rs +++ b/crates/bsnext_system/src/monitor.rs @@ -1,4 +1,4 @@ -use crate::BsSystem; +use crate::{BsSystem, InputMonitor}; use actix::{Actor, Addr, AsyncContext}; use std::hash::Hash; @@ -33,6 +33,7 @@ pub struct Monitor { pub struct MonitorInput { pub path: PathBuf, pub cwd: PathBuf, + pub server_identities: Vec, } impl actix::Handler for BsSystem { @@ -53,7 +54,11 @@ impl actix::Handler for BsSystem { tracing::trace!("[main.rs] starting input monitor"); let input_watcher_addr = input_watcher.start(); - self.input_monitors.push(input_watcher_addr.clone()); + let input_monitor = InputMonitor { + addr: input_watcher_addr.clone(), + server_identities: msg.server_identities.clone(), + }; + self.input_monitors.push(input_monitor); input_watcher_addr.do_send(RequestWatchPath { recipients: vec![ctx.address().recipient()], @@ -92,8 +97,16 @@ impl BsSystem { let _guard = span.enter(); match msg.ctx.id() { 0 => { - tracing::info!(?inner, "InputFile file changed"); - let input = from_input_path(&inner.absolute_path); + tracing::info!("InputFile file changed {:?}", inner); + + let server_identities: Vec = self + .input_monitors + .iter() + .map(|x| x.server_identities.clone()) + .flatten() + .collect::>(); + + let input = from_input_path(&inner.absolute_path, server_identities); let Ok(input) = input else { let err = input.unwrap_err(); diff --git a/crates/bsnext_system/src/start_kind.rs b/crates/bsnext_system/src/start_kind.rs index fbd38e0..ed300c9 100644 --- a/crates/bsnext_system/src/start_kind.rs +++ b/crates/bsnext_system/src/start_kind.rs @@ -1,7 +1,7 @@ use crate::args::Args; use crate::start_kind::start_from_example::StartFromExample; use crate::start_kind::start_from_inputs::{StartFromInput, StartFromInputPaths}; -use crate::start_kind::start_from_paths::StartFromPaths; +use crate::start_kind::start_from_paths::StartFromDirPaths; use bsnext_input::startup::{StartupContext, SystemStart, SystemStartArgs}; use bsnext_input::{Input, InputError}; @@ -14,7 +14,7 @@ pub enum StartKind { FromInput(StartFromInput), FromInputPaths(StartFromInputPaths), FromExample(StartFromExample), - FromPaths(StartFromPaths), + FromDirPaths(StartFromDirPaths), } impl StartKind { @@ -33,7 +33,7 @@ impl StartKind { } if !args.paths.is_empty() { - StartKind::FromPaths(StartFromPaths { + StartKind::FromDirPaths(StartFromDirPaths { paths: args.paths, write_input: args.write, port: args.port, @@ -42,6 +42,7 @@ impl StartKind { } else { StartKind::FromInputPaths(StartFromInputPaths { input_paths: args.input.clone(), + port: args.port, }) } } @@ -55,7 +56,7 @@ impl SystemStart for StartKind { match self { StartKind::FromInputPaths(from_inputs) => from_inputs.input(ctx), StartKind::FromExample(from_example) => from_example.input(ctx), - StartKind::FromPaths(from_input_paths) => from_input_paths.input(ctx), + StartKind::FromDirPaths(from_dir_paths) => from_dir_paths.input(ctx), StartKind::FromInput(from_inputs) => from_inputs.input(ctx), } } @@ -67,7 +68,7 @@ pub mod start_fs { use std::path::{Path, PathBuf}; use bsnext_input::target::TargetKind; - use bsnext_input::{DirError, Input, InputWriteError}; + use bsnext_input::{DirError, Input, InputWriteError, InputWriter}; #[derive(Default, Debug, PartialEq)] pub enum WriteMode { @@ -82,9 +83,9 @@ pub mod start_fs { write_mode: &WriteMode, ) -> Result { let string = match target_kind { - TargetKind::Yaml => bsnext_yaml::input_to_str(input), + TargetKind::Yaml => bsnext_yaml::yaml_writer::YamlWriter.input_to_str(input), TargetKind::Toml => todo!("toml missing"), - TargetKind::Md => bsnext_md::md_writer::_input_to_str(input), + TargetKind::Md => bsnext_md::md_writer::MdWriter.input_to_str(input), }; let name = match target_kind { TargetKind::Yaml => "bslive.yml", diff --git a/crates/bsnext_system/src/start_kind/start_from_inputs.rs b/crates/bsnext_system/src/start_kind/start_from_inputs.rs index 9be6874..bfe0b70 100644 --- a/crates/bsnext_system/src/start_kind/start_from_inputs.rs +++ b/crates/bsnext_system/src/start_kind/start_from_inputs.rs @@ -7,15 +7,16 @@ use std::path::{Path, PathBuf}; #[derive(Debug)] pub struct StartFromInputPaths { pub input_paths: Vec, + pub port: Option, } impl SystemStart for StartFromInputPaths { fn input(&self, ctx: &StartupContext) -> Result> { - from_yml_paths(&ctx.cwd, &self.input_paths) + from_input_paths(&ctx.cwd, &self.input_paths) } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct StartFromInput { pub input: Input, } @@ -28,7 +29,7 @@ impl SystemStart for StartFromInput { } } -fn from_yml_paths>( +fn from_input_paths>( cwd: &Path, inputs: &[T], ) -> Result> { @@ -37,7 +38,7 @@ fn from_yml_paths>( .map(|path| cwd.join(path.as_ref())) .collect::>(); - let lookups = ["bslive.yml", "bslive.yaml", "bslive.md"] + let lookups = ["bslive.yml", "bslive.yaml", "bslive.md", "bslive.html"] .iter() .map(|path| cwd.join(path)) .collect::>(); @@ -75,7 +76,7 @@ fn from_yml_paths>( tracing::info!(?input_path); - let result = from_input_path(input_path); + let result = from_input_path(input_path, vec![]); match result { Ok(input) => Ok(SystemStartArgs::PathWithInput { path: input_path.to_path_buf(), diff --git a/crates/bsnext_system/src/start_kind/start_from_paths.rs b/crates/bsnext_system/src/start_kind/start_from_paths.rs index 88114c5..32a40c8 100644 --- a/crates/bsnext_system/src/start_kind/start_from_paths.rs +++ b/crates/bsnext_system/src/start_kind/start_from_paths.rs @@ -1,24 +1,26 @@ use crate::start_kind::start_fs; use crate::start_kind::start_fs::WriteMode; -use bsnext_input::paths::from_paths; -use bsnext_input::server_config::ServerIdentity; +use bsnext_input::route::{DirRoute, Route, RouteKind}; +use bsnext_input::server_config::{ServerConfig, ServerIdentity}; use bsnext_input::startup::{StartupContext, SystemStart, SystemStartArgs}; use bsnext_input::target::TargetKind; -use bsnext_input::InputError; +use bsnext_input::{Input, InputError, PathDefinition, PathDefs, PathError}; +use std::path::{Path, PathBuf}; #[derive(Debug)] -pub struct StartFromPaths { +pub struct StartFromDirPaths { pub paths: Vec, pub write_input: bool, pub port: Option, pub force: bool, } -impl SystemStart for StartFromPaths { +impl SystemStart for StartFromDirPaths { fn input(&self, ctx: &StartupContext) -> Result> { let identity = ServerIdentity::from_port_or_named(self.port).map_err(|e| Box::new(e.into()))?; - let input = from_paths(&ctx.cwd, &self.paths, identity).map_err(|e| Box::new(e.into()))?; + let input = + from_dir_paths(&ctx.cwd, &self.paths, identity).map_err(|e| Box::new(e.into()))?; let write_mode = if self.force { WriteMode::Override } else { @@ -34,6 +36,69 @@ impl SystemStart for StartFromPaths { } } +fn from_dir_paths>( + cwd: &Path, + paths: &[T], + identity: ServerIdentity, +) -> Result { + let path_defs = paths + .iter() + .map(|p| { + let pb = PathBuf::from(p.as_ref()); + if pb.is_absolute() { + return PathDefinition { + input: p.as_ref().to_string(), + cwd: cwd.to_path_buf(), + absolute: pb, + }; + } else { + return PathDefinition { + input: p.as_ref().to_string(), + cwd: cwd.to_path_buf(), + absolute: cwd.join(pb), + }; + } + }) + .map(|path_def| { + let exists = path_def.absolute.exists(); + (path_def, exists) + }) + .collect::>(); + + let invalid = path_defs + .into_iter() + .filter_map(|(pb, exists)| if exists { None } else { Some(pb) }) + .collect::>(); + + if !invalid.is_empty() { + return Err(PathError::MissingPaths { + paths: PathDefs(invalid), + }); + } + + let server = ServerConfig { + identity, + routes: paths + .iter() + .map(|p| -> Route { + let str = p.as_ref(); + Route { + path: "/".to_string().parse().unwrap(), + kind: RouteKind::Dir(DirRoute { + dir: str.into(), + base: None, + }), + ..Default::default() + } + }) + .collect(), + ..Default::default() + }; + Ok(Input { + servers: vec![server], + }) +} + #[cfg(test)] mod test { use super::*; @@ -41,7 +106,7 @@ mod test { fn test() -> anyhow::Result<()> { use tempfile::tempdir; let tmp_dir = tempdir()?; - let v = StartFromPaths { + let v = StartFromDirPaths { paths: vec![".".into()], write_input: false, port: Some(3000), diff --git a/crates/bsnext_yaml/src/lib.rs b/crates/bsnext_yaml/src/lib.rs index 5768f57..1ee67cc 100644 --- a/crates/bsnext_yaml/src/lib.rs +++ b/crates/bsnext_yaml/src/lib.rs @@ -1,6 +1,2 @@ pub mod yaml_fs; -use bsnext_input::Input; - -pub fn input_to_str(input: &Input) -> String { - serde_yaml::to_string(&input).expect("create yaml?") -} +pub mod yaml_writer; diff --git a/crates/bsnext_yaml/src/yaml_fs.rs b/crates/bsnext_yaml/src/yaml_fs.rs index 0df1d03..6f2d243 100644 --- a/crates/bsnext_yaml/src/yaml_fs.rs +++ b/crates/bsnext_yaml/src/yaml_fs.rs @@ -1,3 +1,4 @@ +use bsnext_input::server_config::ServerIdentity; use bsnext_input::yml::YamlError; use bsnext_input::{BsLiveRulesError, Input, InputCreation, InputError}; use miette::NamedSource; @@ -7,7 +8,10 @@ use std::path::Path; pub struct YamlFs; impl InputCreation for YamlFs { - fn from_input_path>(path: P) -> Result> { + fn from_input_path>( + path: P, + server_ids: Vec, + ) -> Result> { let str = read_to_string(&path).map_err(|e| Box::new(e.into()))?; if str.trim().is_empty() { return Err(Box::new(InputError::YamlError(YamlError::EmptyError { diff --git a/crates/bsnext_yaml/src/yaml_writer.rs b/crates/bsnext_yaml/src/yaml_writer.rs new file mode 100644 index 0000000..bf686a5 --- /dev/null +++ b/crates/bsnext_yaml/src/yaml_writer.rs @@ -0,0 +1,13 @@ +use bsnext_input::{Input, InputWriter}; + +pub struct YamlWriter; + +impl InputWriter for YamlWriter { + fn input_to_str(&self, input: &Input) -> String { + input_to_str(input) + } +} + +fn input_to_str(input: &Input) -> String { + serde_yaml::to_string(&input).expect("create yaml?") +} diff --git a/examples/html/playground.html b/examples/html/playground.html new file mode 100644 index 0000000..e1019c0 --- /dev/null +++ b/examples/html/playground.html @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file From d0b288c666769fa452d8dcd2dd52a630153d246d Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sat, 23 Nov 2024 19:30:33 +0000 Subject: [PATCH 2/8] more examples --- crates/bsnext_example/src/md.rs | 2 +- crates/bsnext_example/src/playground.rs | 4 +- crates/bsnext_html/src/lib.rs | 75 +++---------------- crates/bsnext_input/src/lib.rs | 36 +++++++-- crates/bsnext_md/src/lib.rs | 9 ++- crates/bsnext_md/src/md_fs.rs | 16 ++-- crates/bsnext_md/tests/md.rs | 8 +- crates/bsnext_md/tests/md_playground.rs | 2 +- crates/bsnext_md/tests/md_serialize.rs | 4 +- crates/bsnext_system/src/input_fs.rs | 18 ++--- crates/bsnext_system/src/lib.rs | 23 +++--- crates/bsnext_system/src/monitor.rs | 32 +++++--- .../src/start_kind/start_from_example.rs | 1 + .../src/start_kind/start_from_inputs.rs | 4 +- crates/bsnext_yaml/src/yaml_fs.rs | 13 ++-- examples/basic/public/index.html | 2 +- examples/basic/public/styles.css | 3 + examples/html/playground.html | 4 + examples/markdown/playground.md | 15 ++-- 19 files changed, 126 insertions(+), 145 deletions(-) diff --git a/crates/bsnext_example/src/md.rs b/crates/bsnext_example/src/md.rs index 2c77b9a..921f628 100644 --- a/crates/bsnext_example/src/md.rs +++ b/crates/bsnext_example/src/md.rs @@ -8,7 +8,7 @@ pub struct MdExample; impl InputSource for MdExample { fn into_input(self, identity: Option) -> InputSourceKind { let input_str = include_str!("../../../examples/markdown/single.md"); - let mut input = md_to_input(input_str).expect("example cannot fail?"); + let mut input = md_to_input(input_str, &Default::default()).expect("example cannot fail?"); let server = input .servers .first_mut() diff --git a/crates/bsnext_example/src/playground.rs b/crates/bsnext_example/src/playground.rs index f267313..460fe9e 100644 --- a/crates/bsnext_example/src/playground.rs +++ b/crates/bsnext_example/src/playground.rs @@ -1,5 +1,5 @@ use bsnext_input::server_config::ServerIdentity; -use bsnext_input::{InputCreation, InputSource, InputSourceKind}; +use bsnext_input::{InputCreation, InputCtx, InputSource, InputSourceKind}; use bsnext_md::md_fs::MdFs; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -8,7 +8,7 @@ pub struct PlaygroundExample; impl InputSource for PlaygroundExample { fn into_input(self, identity: Option) -> InputSourceKind { let input_str = include_str!("../../../examples/markdown/playground.md"); - let mut input = MdFs::from_input_str(input_str).unwrap(); + let mut input = MdFs::from_input_str(input_str, &InputCtx::default()).unwrap(); // update the server identity if it was provided if let (Some(server), Some(identity)) = (input.servers.get_mut(0), identity) { diff --git a/crates/bsnext_html/src/lib.rs b/crates/bsnext_html/src/lib.rs index ad87588..456380f 100644 --- a/crates/bsnext_html/src/lib.rs +++ b/crates/bsnext_html/src/lib.rs @@ -1,33 +1,30 @@ use bsnext_input::playground::Playground; -use bsnext_input::server_config::{ServerConfig, ServerIdentity}; -use bsnext_input::{Input, InputCreation, InputError}; +use bsnext_input::server_config::ServerConfig; +use bsnext_input::{Input, InputCreation, InputCtx, InputError}; use std::fs::read_to_string; use std::path::Path; pub struct HtmlFs; impl InputCreation for HtmlFs { - fn from_input_path>( - path: P, - server_ids: Vec, - ) -> Result> { + fn from_input_path>(path: P, ctx: &InputCtx) -> Result> { let str = read_to_string(path).map_err(|e| Box::new(e.into()))?; - let input = playground_html_str_to_input(&str, server_ids) + let input = playground_html_str_to_input(&str, ctx) .map_err(|e| Box::new(InputError::HtmlError(e.to_string())))?; Ok(input) } - fn from_input_str>(content: P) -> Result> { - let input = playground_html_str_to_input(&content.as_ref(), vec![]) + fn from_input_str>( + content: P, + _ctx: &InputCtx, + ) -> Result> { + let input = playground_html_str_to_input(&content.as_ref(), &InputCtx::default()) .map_err(|e| Box::new(InputError::HtmlError(e.to_string())))?; Ok(input) } } -fn playground_html_str_to_input( - html: &str, - server_ids: Vec, -) -> Result> { +fn playground_html_str_to_input(html: &str, ctx: &InputCtx) -> Result> { use unindent::unindent; let mut document = scraper::Html::parse_fragment(html); let style = scraper::Selector::parse("style:first-of-type").unwrap(); @@ -71,59 +68,9 @@ fn playground_html_str_to_input( let mut input = Input::default(); let server = ServerConfig { routes: playground.as_routes(), - identity: server_ids - .get(0) - .map(ToOwned::to_owned) - .unwrap_or_else(|| ServerIdentity::named()), + identity: ctx.first_id_or_default(), ..Default::default() }; input.servers.push(server); - // dbg!(&input.servers.get(0).unwrap().identity); Ok(input) } - -#[test] -fn has_selector() { - let mut html = r#" - huh? - -
-
    -
  • first
  • -
  • second
  • -
  • third
  • -
-
-
-

-
- "#; - - let mut document = scraper::Html::parse_fragment(html); - - #[derive(Debug)] - struct Item { - pub html: String, - pub css: Option, - pub js: Option, - } - - let mut item = Item { - html: "".to_string(), - css: None, - js: None, - }; - - dbg!(&item); - - // fs::write("origin.html", item.html).unwrap(); - // fs::write("origin.js", item.js.unwrap()).unwrap(); - // fs::write("origin.css", item.css.unwrap()).unwrap(); -} diff --git a/crates/bsnext_input/src/lib.rs b/crates/bsnext_input/src/lib.rs index b5965cb..1decace 100644 --- a/crates/bsnext_input/src/lib.rs +++ b/crates/bsnext_input/src/lib.rs @@ -89,12 +89,38 @@ pub trait InputSource { } } +#[derive(Debug, Default, Clone)] +pub struct InputCtx { + prev_server_ids: Option>, +} + +impl InputCtx { + pub fn new(servers: &[ServerIdentity]) -> Self { + if servers.is_empty() { + Self::default() + } else { + Self { + prev_server_ids: Some(servers.to_vec()), + } + } + } + + pub fn server_ids(&self) -> Option<&[ServerIdentity]> { + self.prev_server_ids.as_ref().map(Vec::as_slice) + } + + pub fn first_id_or_default(&self) -> ServerIdentity { + self.prev_server_ids + .as_ref() + .and_then(|x| x.get(0)) + .map(ToOwned::to_owned) + .unwrap_or_else(|| ServerIdentity::named()) + } +} + pub trait InputCreation { - fn from_input_path>( - path: P, - server_ids: Vec, - ) -> Result>; - fn from_input_str>(content: P) -> Result>; + fn from_input_path>(path: P, ctx: &InputCtx) -> Result>; + fn from_input_str>(content: P, ctx: &InputCtx) -> Result>; } pub trait InputWriter { diff --git a/crates/bsnext_md/src/lib.rs b/crates/bsnext_md/src/lib.rs index d3f73f2..38f05bf 100644 --- a/crates/bsnext_md/src/lib.rs +++ b/crates/bsnext_md/src/lib.rs @@ -5,7 +5,7 @@ use bsnext_input::path_def::PathDef; use bsnext_input::playground::Playground; use bsnext_input::route::{Route, RouteKind}; use bsnext_input::server_config::ServerConfig; -use bsnext_input::Input; +use bsnext_input::{Input, InputCtx}; use markdown::mdast::Node; use markdown::{Constructs, ParseOptions}; use nom::branch::alt; @@ -220,7 +220,7 @@ enum Convert { Playground(Playground), } -pub fn nodes_to_input(nodes: &[Node]) -> Result { +pub fn nodes_to_input(nodes: &[Node], ctx: &InputCtx) -> Result { let mut routes = vec![]; let mut input: Option = None; let mut parser = many0(alt(( @@ -334,6 +334,7 @@ pub fn nodes_to_input(nodes: &[Node]) -> Result { let mut input = Input::default(); let server = ServerConfig { routes, + identity: ctx.first_id_or_default(), ..Default::default() }; input.servers.push(server); @@ -378,7 +379,7 @@ fn str_to_nodes(input: &str) -> Result, MarkdownError> { } } -pub fn md_to_input(input: &str) -> Result { +pub fn md_to_input(input: &str, ctx: &InputCtx) -> Result { let root = str_to_nodes(input)?; - nodes_to_input(&root) + nodes_to_input(&root, ctx) } diff --git a/crates/bsnext_md/src/md_fs.rs b/crates/bsnext_md/src/md_fs.rs index 93fb2bc..c424979 100644 --- a/crates/bsnext_md/src/md_fs.rs +++ b/crates/bsnext_md/src/md_fs.rs @@ -1,23 +1,19 @@ use crate::md_to_input; -use bsnext_input::server_config::ServerIdentity; -use bsnext_input::{Input, InputCreation, InputError}; +use bsnext_input::{Input, InputCreation, InputCtx, InputError}; use std::fs::read_to_string; use std::path::Path; pub struct MdFs; impl InputCreation for MdFs { - fn from_input_path>( - path: P, - server_ids: Vec, - ) -> Result> { + fn from_input_path>(path: P, ctx: &InputCtx) -> Result> { let str = read_to_string(path).map_err(|e| Box::new(e.into()))?; - let input = - md_to_input(&str).map_err(|e| Box::new(InputError::MarkdownError(e.to_string())))?; + let input = md_to_input(&str, ctx) + .map_err(|e| Box::new(InputError::MarkdownError(e.to_string())))?; Ok(input) } - fn from_input_str>(content: P) -> Result> { - let input = md_to_input(content.as_ref()) + fn from_input_str>(content: P, ctx: &InputCtx) -> Result> { + let input = md_to_input(content.as_ref(), ctx) .map_err(|e| Box::new(InputError::MarkdownError(e.to_string())))?; Ok(input) } diff --git a/crates/bsnext_md/tests/md.rs b/crates/bsnext_md/tests/md.rs index 3cc73b4..daf4bc2 100644 --- a/crates/bsnext_md/tests/md.rs +++ b/crates/bsnext_md/tests/md.rs @@ -21,7 +21,7 @@ body { } ``` "#; - let config = md_to_input(&input).expect("unwrap"); + let config = md_to_input(&input, &Default::default()).expect("unwrap"); let server_1 = config.servers.first().unwrap(); assert_eq!( server_1.routes[0], @@ -61,7 +61,7 @@ body { } ``` "#; - let config = md_to_input(&input).expect("unwrap"); + let config = md_to_input(&input, &Default::default()).expect("unwrap"); let server_1 = config.servers.first().unwrap(); assert_eq!( server_1.routes[0], @@ -114,7 +114,7 @@ path: /abc # Before "#; - let input = md_to_input(&markdown).expect("unwrap"); + let input = md_to_input(&markdown, &Default::default()).expect("unwrap"); let server_1 = input.servers.first().unwrap(); let expected_id = ServerIdentity::Address { bind_address: "0.0.0.0:3001".into(), @@ -149,7 +149,7 @@ path: /abc } fn default_md_assertions(input: &str) -> anyhow::Result<()> { - let input = md_to_input(&input).expect("unwrap"); + let input = md_to_input(&input, &Default::default()).expect("unwrap"); let server_1 = input.servers.first().unwrap(); let expected_id = ServerIdentity::Address { bind_address: "0.0.0.0:5001".into(), diff --git a/crates/bsnext_md/tests/md_playground.rs b/crates/bsnext_md/tests/md_playground.rs index 841f5c1..d645112 100644 --- a/crates/bsnext_md/tests/md_playground.rs +++ b/crates/bsnext_md/tests/md_playground.rs @@ -27,7 +27,7 @@ console.log("hello world") ``` "#; - let config = md_to_input(&input).expect("unwrap"); + let config = md_to_input(&input, &Default::default()).expect("unwrap"); let first_server = config.servers.get(0).unwrap(); let routes = first_server .playground diff --git a/crates/bsnext_md/tests/md_serialize.rs b/crates/bsnext_md/tests/md_serialize.rs index ab53ceb..8dfe6f8 100644 --- a/crates/bsnext_md/tests/md_serialize.rs +++ b/crates/bsnext_md/tests/md_serialize.rs @@ -5,10 +5,10 @@ use bsnext_md::md_writer::MdWriter; #[test] fn test_input_to_str() -> anyhow::Result<()> { let input_str = include_str!("../../../examples/markdown/single.md"); - let input = md_to_input(&input_str).expect("unwrap"); + let input = md_to_input(&input_str, &Default::default()).expect("unwrap"); let output = MdWriter.input_to_str(&input); println!("{}", output); - let input = md_to_input(&output).expect("unwrapped 2"); + let input = md_to_input(&output, &Default::default()).expect("unwrapped 2"); println!("{:#?}", input); assert_eq!(input.servers.len(), 1); assert_eq!(input.servers.first().unwrap().routes.len(), 2); diff --git a/crates/bsnext_system/src/input_fs.rs b/crates/bsnext_system/src/input_fs.rs index 9467cfd..bf3e8e5 100644 --- a/crates/bsnext_system/src/input_fs.rs +++ b/crates/bsnext_system/src/input_fs.rs @@ -1,22 +1,14 @@ -use bsnext_input::server_config::ServerIdentity; -use bsnext_input::{Input, InputCreation, InputError}; +use bsnext_input::{Input, InputCreation, InputCtx, InputError}; use std::path::Path; -pub fn from_input_path>( - path: P, - prev_identities: Vec, -) -> Result> { +pub fn from_input_path>(path: P, ctx: &InputCtx) -> Result> { match path.as_ref().extension().and_then(|x| x.to_str()) { None => Err(Box::new(InputError::MissingExtension( path.as_ref().to_owned(), ))), - Some("yml") | Some("yaml") => { - bsnext_yaml::yaml_fs::YamlFs::from_input_path(path, prev_identities) - } - Some("md") | Some("markdown") => { - bsnext_md::md_fs::MdFs::from_input_path(path, prev_identities) - } - Some("html") => bsnext_html::HtmlFs::from_input_path(path, prev_identities), + Some("yml") | Some("yaml") => bsnext_yaml::yaml_fs::YamlFs::from_input_path(path, ctx), + Some("md") | Some("markdown") => bsnext_md::md_fs::MdFs::from_input_path(path, ctx), + Some("html") => bsnext_html::HtmlFs::from_input_path(path, ctx), Some(other) => Err(Box::new(InputError::UnsupportedExtension( other.to_string(), ))), diff --git a/crates/bsnext_system/src/lib.rs b/crates/bsnext_system/src/lib.rs index b0c9fd0..e5cd64e 100644 --- a/crates/bsnext_system/src/lib.rs +++ b/crates/bsnext_system/src/lib.rs @@ -3,7 +3,7 @@ use crate::monitor::{ }; use actix::{Actor, Addr, AsyncContext, Handler, Running}; -use bsnext_input::Input; +use bsnext_input::{Input, InputCtx}; use std::collections::HashMap; use actix_rt::Arbiter; @@ -24,7 +24,6 @@ use bsnext_core::server::handler_routes_updated::RoutesUpdated; use bsnext_core::servers_supervisor::get_servers_handler::GetServersMessage; use bsnext_core::servers_supervisor::start_handler::ChildCreatedInsert; use bsnext_dto::internal::{AnyEvent, ChildResult, InternalEvents}; -use bsnext_input::server_config::ServerIdentity; use bsnext_input::startup::{ DidStart, StartupContext, StartupError, StartupResult, SystemStart, SystemStartArgs, }; @@ -44,7 +43,7 @@ pub struct BsSystem { self_addr: Option>, servers_addr: Option>, any_event_sender: Option>, - input_monitors: Vec, + input_monitors: Option, any_monitors: HashMap, cwd: Option, } @@ -52,7 +51,7 @@ pub struct BsSystem { #[derive(Debug, Clone)] pub struct InputMonitor { pub addr: Addr, - pub server_identities: Vec, + pub ctx: InputCtx, } #[derive(Debug)] @@ -80,7 +79,7 @@ impl BsSystem { self_addr: None, servers_addr: None, any_event_sender: None, - input_monitors: vec![], + input_monitors: None, any_monitors: Default::default(), cwd: None, } @@ -301,14 +300,16 @@ impl Handler for BsSystem { match msg.kind.input(&start_context) { Ok(SystemStartArgs::PathWithInput { path, input }) => { tracing::debug!("PathWithInput"); + let ids = input + .servers + .iter() + .map(|x| x.identity.clone()) + .collect::>(); + let input_ctx = InputCtx::new(&ids); ctx.notify(MonitorInput { path: path.clone(), cwd: cwd.clone(), - server_identities: input - .servers - .iter() - .map(|x| x.identity.clone()) - .collect::>(), + ctx: input_ctx, }); self.accept_watchables(&input); @@ -326,7 +327,7 @@ impl Handler for BsSystem { ctx.notify(MonitorInput { path: path.clone(), cwd: cwd.clone(), - server_identities: vec![], + ctx: InputCtx::default(), }); self.publish_any_event(AnyEvent::Internal(InternalEvents::InputError(input_error))); Ok(DidStart::Started) diff --git a/crates/bsnext_system/src/monitor.rs b/crates/bsnext_system/src/monitor.rs index 6a50bae..25d2e7c 100644 --- a/crates/bsnext_system/src/monitor.rs +++ b/crates/bsnext_system/src/monitor.rs @@ -10,7 +10,7 @@ use bsnext_fs::{ }; use bsnext_input::route::{DebounceDuration, DirRoute, FilterKind, RouteKind, Spec, SpecOpts}; use bsnext_input::server_config::ServerIdentity; -use bsnext_input::{Input, InputError, PathDefinition, PathDefs, PathError}; +use bsnext_input::{Input, InputCtx, InputError, PathDefinition, PathDefs, PathError}; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; @@ -33,7 +33,7 @@ pub struct Monitor { pub struct MonitorInput { pub path: PathBuf, pub cwd: PathBuf, - pub server_identities: Vec, + pub ctx: InputCtx, } impl actix::Handler for BsSystem { @@ -56,9 +56,9 @@ impl actix::Handler for BsSystem { let input_watcher_addr = input_watcher.start(); let input_monitor = InputMonitor { addr: input_watcher_addr.clone(), - server_identities: msg.server_identities.clone(), + ctx: msg.ctx.clone(), }; - self.input_monitors.push(input_monitor); + self.input_monitors = Some(input_monitor); input_watcher_addr.do_send(RequestWatchPath { recipients: vec![ctx.address().recipient()], @@ -99,20 +99,32 @@ impl BsSystem { 0 => { tracing::info!("InputFile file changed {:?}", inner); - let server_identities: Vec = self + let ctx = self .input_monitors - .iter() - .map(|x| x.server_identities.clone()) - .flatten() - .collect::>(); + .as_ref() + .map(|x| x.ctx.clone()) + .unwrap_or_default(); - let input = from_input_path(&inner.absolute_path, server_identities); + let input = from_input_path(&inner.absolute_path, &ctx); let Ok(input) = input else { let err = input.unwrap_err(); return Some(AnyEvent::Internal(InternalEvents::InputError(*err))); }; + if let Some(mon) = self.input_monitors.as_mut() { + let next = input + .servers + .iter() + .map(|s| s.identity.clone()) + .collect::>(); + let ctx = InputCtx::new(&next); + tracing::info!("will set next ids {:?}", next); + if !next.is_empty() { + mon.ctx = ctx + } + } + self.accept_watchables(&input); self.resolve_servers(input); diff --git a/crates/bsnext_system/src/start_kind/start_from_example.rs b/crates/bsnext_system/src/start_kind/start_from_example.rs index 5f76fb1..ef2945f 100644 --- a/crates/bsnext_system/src/start_kind/start_from_example.rs +++ b/crates/bsnext_system/src/start_kind/start_from_example.rs @@ -19,6 +19,7 @@ pub struct StartFromExample { impl SystemStart for StartFromExample { fn input(&self, ctx: &StartupContext) -> Result> { + // todo: mimic this for other kinds of startup - basically allow 'port' to be given and respected let identity = ServerIdentity::from_port_or_named(self.port).map_err(|e| Box::new(e.into()))?; let input_source_kind = self.example.into_input(Some(identity)); diff --git a/crates/bsnext_system/src/start_kind/start_from_inputs.rs b/crates/bsnext_system/src/start_kind/start_from_inputs.rs index bfe0b70..c39d755 100644 --- a/crates/bsnext_system/src/start_kind/start_from_inputs.rs +++ b/crates/bsnext_system/src/start_kind/start_from_inputs.rs @@ -1,7 +1,7 @@ use bsnext_input::startup::{StartupContext, SystemStart, SystemStartArgs}; use crate::input_fs::from_input_path; -use bsnext_input::{Input, InputError}; +use bsnext_input::{Input, InputCtx, InputError}; use std::path::{Path, PathBuf}; #[derive(Debug)] @@ -76,7 +76,7 @@ fn from_input_paths>( tracing::info!(?input_path); - let result = from_input_path(input_path, vec![]); + let result = from_input_path(input_path, &InputCtx::default()); match result { Ok(input) => Ok(SystemStartArgs::PathWithInput { path: input_path.to_path_buf(), diff --git a/crates/bsnext_yaml/src/yaml_fs.rs b/crates/bsnext_yaml/src/yaml_fs.rs index 6f2d243..948ad60 100644 --- a/crates/bsnext_yaml/src/yaml_fs.rs +++ b/crates/bsnext_yaml/src/yaml_fs.rs @@ -1,6 +1,5 @@ -use bsnext_input::server_config::ServerIdentity; use bsnext_input::yml::YamlError; -use bsnext_input::{BsLiveRulesError, Input, InputCreation, InputError}; +use bsnext_input::{BsLiveRulesError, Input, InputCreation, InputCtx, InputError}; use miette::NamedSource; use std::fs::read_to_string; use std::path::Path; @@ -8,10 +7,7 @@ use std::path::Path; pub struct YamlFs; impl InputCreation for YamlFs { - fn from_input_path>( - path: P, - server_ids: Vec, - ) -> Result> { + fn from_input_path>(path: P, _ctx: &InputCtx) -> Result> { let str = read_to_string(&path).map_err(|e| Box::new(e.into()))?; if str.trim().is_empty() { return Err(Box::new(InputError::YamlError(YamlError::EmptyError { @@ -36,7 +32,10 @@ impl InputCreation for YamlFs { Ok(output) } - fn from_input_str>(_content: P) -> Result> { + fn from_input_str>( + _content: P, + _ctx: &InputCtx, + ) -> Result> { todo!() } } diff --git a/examples/basic/public/index.html b/examples/basic/public/index.html index 45d6b1b..081fd9d 100644 --- a/examples/basic/public/index.html +++ b/examples/basic/public/index.html @@ -9,7 +9,7 @@ -

Edit me!

+

Edit me! - a full HTML

\ No newline at end of file diff --git a/examples/basic/public/styles.css b/examples/basic/public/styles.css index 870d334..9fbc98d 100644 --- a/examples/basic/public/styles.css +++ b/examples/basic/public/styles.css @@ -1,3 +1,6 @@ @import url("./reset.css"); /** your styles here */ +body { + background: red; +} \ No newline at end of file diff --git a/examples/html/playground.html b/examples/html/playground.html index e1019c0..dc67872 100644 --- a/examples/html/playground.html +++ b/examples/html/playground.html @@ -2,6 +2,10 @@ p { color: red; } + + abc-shane { + color: red; + } diff --git a/examples/markdown/playground.md b/examples/markdown/playground.md index 9242404..53e7f6c 100644 --- a/examples/markdown/playground.md +++ b/examples/markdown/playground.md @@ -1,12 +1,7 @@ ---- -servers: - - name: playground ---- - ```html playground
- Hello world! + Hello world 5.0
``` @@ -14,7 +9,7 @@ servers: @import url("reset.css"); :root { - border: 1px dotted red; + border: 1px dotted pink; } * { @@ -23,6 +18,10 @@ servers: ``` ```js -console.log('Hello from playground.md') +let int = 0; +setInterval(() => { + int += 1; + document.body.textContent += int +}, 1000); ``` From 3aa3f36d7913a2484a8b04672f33c0a5caf807c7 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sat, 23 Nov 2024 21:49:52 +0000 Subject: [PATCH 3/8] more renaming --- crates/bsnext_example/src/md.rs | 8 +++++--- crates/bsnext_md/src/lib.rs | 7 +------ crates/bsnext_md/src/md_fs.rs | 7 ++++++- crates/bsnext_md/tests/md.rs | 11 ++++++----- crates/bsnext_md/tests/md_playground.rs | 5 +++-- crates/bsnext_md/tests/md_serialize.rs | 8 ++++---- .../tests/snapshots/md_playground__md_playground.snap | 4 ++-- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/crates/bsnext_example/src/md.rs b/crates/bsnext_example/src/md.rs index 921f628..4ae1452 100644 --- a/crates/bsnext_example/src/md.rs +++ b/crates/bsnext_example/src/md.rs @@ -1,6 +1,7 @@ use bsnext_input::server_config::ServerIdentity; -use bsnext_input::{InputSource, InputSourceKind}; -use bsnext_md::md_to_input; +use bsnext_input::{Input, InputCreation, InputCtx, InputSource, InputSourceKind}; +use bsnext_md::md_fs::MdFs; +use bsnext_md::{nodes_to_input, MarkdownError}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct MdExample; @@ -8,7 +9,8 @@ pub struct MdExample; impl InputSource for MdExample { fn into_input(self, identity: Option) -> InputSourceKind { let input_str = include_str!("../../../examples/markdown/single.md"); - let mut input = md_to_input(input_str, &Default::default()).expect("example cannot fail?"); + let mut input = + MdFs::from_input_str(input_str, &Default::default()).expect("example cannot fail?"); let server = input .servers .first_mut() diff --git a/crates/bsnext_md/src/lib.rs b/crates/bsnext_md/src/lib.rs index 38f05bf..bc1325f 100644 --- a/crates/bsnext_md/src/lib.rs +++ b/crates/bsnext_md/src/lib.rs @@ -361,7 +361,7 @@ fn code_val(n: &Node) -> &str { } } -fn str_to_nodes(input: &str) -> Result, MarkdownError> { +pub(crate) fn str_to_nodes(input: &str) -> Result, MarkdownError> { let opts = ParseOptions { constructs: Constructs { frontmatter: true, @@ -378,8 +378,3 @@ fn str_to_nodes(input: &str) -> Result, MarkdownError> { } } } - -pub fn md_to_input(input: &str, ctx: &InputCtx) -> Result { - let root = str_to_nodes(input)?; - nodes_to_input(&root, ctx) -} diff --git a/crates/bsnext_md/src/md_fs.rs b/crates/bsnext_md/src/md_fs.rs index c424979..e668e49 100644 --- a/crates/bsnext_md/src/md_fs.rs +++ b/crates/bsnext_md/src/md_fs.rs @@ -1,4 +1,4 @@ -use crate::md_to_input; +use crate::{nodes_to_input, str_to_nodes, MarkdownError}; use bsnext_input::{Input, InputCreation, InputCtx, InputError}; use std::fs::read_to_string; use std::path::Path; @@ -18,3 +18,8 @@ impl InputCreation for MdFs { Ok(input) } } + +fn md_to_input(input: &str, ctx: &InputCtx) -> Result { + let root = str_to_nodes(input)?; + nodes_to_input(&root, ctx) +} diff --git a/crates/bsnext_md/tests/md.rs b/crates/bsnext_md/tests/md.rs index daf4bc2..b4e41a4 100644 --- a/crates/bsnext_md/tests/md.rs +++ b/crates/bsnext_md/tests/md.rs @@ -1,7 +1,8 @@ use bsnext_input::path_def::PathDef; use bsnext_input::route::{Route, RouteKind}; use bsnext_input::server_config::ServerIdentity; -use bsnext_md::md_to_input; +use bsnext_input::InputCreation; +use bsnext_md::md_fs::MdFs; use std::str::FromStr; #[test] @@ -21,7 +22,7 @@ body { } ``` "#; - let config = md_to_input(&input, &Default::default()).expect("unwrap"); + let config = MdFs::from_input_str(&input, &Default::default()).expect("unwrap"); let server_1 = config.servers.first().unwrap(); assert_eq!( server_1.routes[0], @@ -61,7 +62,7 @@ body { } ``` "#; - let config = md_to_input(&input, &Default::default()).expect("unwrap"); + let config = MdFs::from_input_str(&input, &Default::default()).expect("unwrap"); let server_1 = config.servers.first().unwrap(); assert_eq!( server_1.routes[0], @@ -114,7 +115,7 @@ path: /abc # Before "#; - let input = md_to_input(&markdown, &Default::default()).expect("unwrap"); + let input = MdFs::from_input_str(&markdown, &Default::default()).expect("unwrap"); let server_1 = input.servers.first().unwrap(); let expected_id = ServerIdentity::Address { bind_address: "0.0.0.0:3001".into(), @@ -149,7 +150,7 @@ path: /abc } fn default_md_assertions(input: &str) -> anyhow::Result<()> { - let input = md_to_input(&input, &Default::default()).expect("unwrap"); + let input = MdFs::from_input_str(&input, &Default::default()).expect("unwrap"); let server_1 = input.servers.first().unwrap(); let expected_id = ServerIdentity::Address { bind_address: "0.0.0.0:5001".into(), diff --git a/crates/bsnext_md/tests/md_playground.rs b/crates/bsnext_md/tests/md_playground.rs index d645112..3906fe4 100644 --- a/crates/bsnext_md/tests/md_playground.rs +++ b/crates/bsnext_md/tests/md_playground.rs @@ -1,4 +1,5 @@ -use bsnext_md::md_to_input; +use bsnext_input::InputCreation; +use bsnext_md::md_fs::MdFs; #[test] fn test_md_playground() -> anyhow::Result<()> { @@ -27,7 +28,7 @@ console.log("hello world") ``` "#; - let config = md_to_input(&input, &Default::default()).expect("unwrap"); + let config = MdFs::from_input_str(&input, &Default::default()).expect("unwrap"); let first_server = config.servers.get(0).unwrap(); let routes = first_server .playground diff --git a/crates/bsnext_md/tests/md_serialize.rs b/crates/bsnext_md/tests/md_serialize.rs index 8dfe6f8..cc2b8ae 100644 --- a/crates/bsnext_md/tests/md_serialize.rs +++ b/crates/bsnext_md/tests/md_serialize.rs @@ -1,14 +1,14 @@ -use bsnext_input::InputWriter; -use bsnext_md::md_to_input; +use bsnext_input::{InputCreation, InputWriter}; +use bsnext_md::md_fs::MdFs; use bsnext_md::md_writer::MdWriter; #[test] fn test_input_to_str() -> anyhow::Result<()> { let input_str = include_str!("../../../examples/markdown/single.md"); - let input = md_to_input(&input_str, &Default::default()).expect("unwrap"); + let input = MdFs::from_input_str(&input_str, &Default::default()).expect("unwrap"); let output = MdWriter.input_to_str(&input); println!("{}", output); - let input = md_to_input(&output, &Default::default()).expect("unwrapped 2"); + let input = MdFs::from_input_str(&output, &Default::default()).expect("unwrapped 2"); println!("{:#?}", input); assert_eq!(input.servers.len(), 1); assert_eq!(input.servers.first().unwrap().routes.len(), 2); diff --git a/crates/bsnext_md/tests/snapshots/md_playground__md_playground.snap b/crates/bsnext_md/tests/snapshots/md_playground__md_playground.snap index 6fc8724..adf8ecd 100644 --- a/crates/bsnext_md/tests/snapshots/md_playground__md_playground.snap +++ b/crates/bsnext_md/tests/snapshots/md_playground__md_playground.snap @@ -24,7 +24,7 @@ expression: routes inner: Addition( InjectAddition { addition_position: Prepend( - "\n\n \n \n \n \n Document\n \n \n \n", + "\n\n \n \n \n \n Browsersync Live - Playground\n \n \n \n", ), }, ), @@ -40,7 +40,7 @@ expression: routes inner: Addition( InjectAddition { addition_position: Append( - "\n \n \n\n", + "\n \n \n\n", ), }, ), From e028f42a19dc1c1fd480fd6cbbe691d3b686e13f Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sat, 23 Nov 2024 22:16:36 +0000 Subject: [PATCH 4/8] tests --- Cargo.lock | 2 + crates/bsnext_html/Cargo.toml | 4 + crates/bsnext_html/src/lib.rs | 32 +++++--- crates/bsnext_html/tests/html_playground.rs | 82 +++++++++++++++++++ ...playground__html_playground_content-2.snap | 9 ++ ...playground__html_playground_content-3.snap | 9 ++ ...l_playground__html_playground_content.snap | 9 ++ crates/bsnext_input/src/lib.rs | 2 +- crates/bsnext_md/src/lib.rs | 2 +- examples/markdown/playground.md | 4 +- 10 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 crates/bsnext_html/tests/html_playground.rs create mode 100644 crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-2.snap create mode 100644 crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-3.snap create mode 100644 crates/bsnext_html/tests/snapshots/html_playground__html_playground_content.snap diff --git a/Cargo.lock b/Cargo.lock index fa3ded2..d2ae36d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -552,7 +552,9 @@ dependencies = [ name = "bsnext_html" version = "0.1.0" dependencies = [ + "anyhow", "bsnext_input", + "insta", "scraper", "unindent", ] diff --git a/crates/bsnext_html/Cargo.toml b/crates/bsnext_html/Cargo.toml index 11d0fe2..8400258 100644 --- a/crates/bsnext_html/Cargo.toml +++ b/crates/bsnext_html/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" bsnext_input = { path = "../bsnext_input" } unindent = "0.2.3" scraper = "0.21.0" + +[dev-dependencies] +anyhow = { workspace = true } +insta = { workspace = true } diff --git a/crates/bsnext_html/src/lib.rs b/crates/bsnext_html/src/lib.rs index 456380f..820b8a0 100644 --- a/crates/bsnext_html/src/lib.rs +++ b/crates/bsnext_html/src/lib.rs @@ -14,11 +14,8 @@ impl InputCreation for HtmlFs { Ok(input) } - fn from_input_str>( - content: P, - _ctx: &InputCtx, - ) -> Result> { - let input = playground_html_str_to_input(&content.as_ref(), &InputCtx::default()) + fn from_input_str>(content: P, ctx: &InputCtx) -> Result> { + let input = playground_html_str_to_input(&content.as_ref(), ctx) .map_err(|e| Box::new(InputError::HtmlError(e.to_string())))?; Ok(input) } @@ -26,13 +23,18 @@ impl InputCreation for HtmlFs { fn playground_html_str_to_input(html: &str, ctx: &InputCtx) -> Result> { use unindent::unindent; + + // parse the HTML let mut document = scraper::Html::parse_fragment(html); + let style = scraper::Selector::parse("style:first-of-type").unwrap(); let script = scraper::Selector::parse("script:first-of-type").unwrap(); + let mut style_elems = document.select(&style); let mut script_elems = document.select(&script); - let mut removals = vec![]; + let mut node_ids_to_remove = vec![]; + // start an empty playground let mut playground = Playground { html: "".to_string(), css: None, @@ -40,23 +42,24 @@ fn playground_html_str_to_input(html: &str, ctx: &InputCtx) -> Result") @@ -65,12 +68,19 @@ fn playground_html_str_to_input(html: &str, ctx: &InputCtx) -> Result + p { + color: red; + } + + abc-shane { + color: red; + } + +
+

Test!

+ +
+"#; + +#[test] +fn test_html_playground_content() -> anyhow::Result<()> { + let idens = vec![]; + let ctx = InputCtx::new(&idens); + let as_input = HtmlFs::from_input_str(input, &ctx)?; + let Some(server) = as_input.servers.get(0) else { + return Err(anyhow::anyhow!("no server")); + }; + let routes = server.routes(); + let html = routes.get(0).unwrap(); + let js = routes.get(1).unwrap(); + let css = routes.get(2).unwrap(); + assert_debug_snapshot!(html.kind); + assert_debug_snapshot!(js.kind); + assert_debug_snapshot!(css.kind); + // assert_debug_snapshot!(js.kind); + // assert_debug_snapshot!(css.kind); + // Ok(()) + // for x in server.routes() { + // dbg!(&x.kind); + // match &x.kind { + // RouteKind::Raw(RawRoute::()) => {} + // RouteKind::Proxy(_) => {} + // RouteKind::Dir(_) => {} + // } + // } + Ok(()) +} +#[test] +fn test_html_playground_without_server_id() -> anyhow::Result<()> { + let idens = vec![]; + let ctx = InputCtx::new(&idens); + let as_input = HtmlFs::from_input_str(input, &ctx)?; + assert_eq!(as_input.servers.len(), 1); + let first = as_input.servers.get(0).unwrap(); + let is_named = matches!(first.identity, ServerIdentity::Named { .. }); + assert_eq!(is_named, true); + Ok(()) +} +#[test] +fn test_html_playground_with_server_id() -> anyhow::Result<()> { + let ident = ServerIdentity::Address { + bind_address: String::from("127.0.0.1:8080"), + }; + let ctx = InputCtx::new(&[ident.clone()]); + let as_input = HtmlFs::from_input_str(input, &ctx)?; + + assert_eq!(as_input.servers.len(), 1); + let first = as_input.servers.get(0).unwrap(); + assert_eq!(ident, first.identity); + Ok(()) +} diff --git a/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-2.snap b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-2.snap new file mode 100644 index 0000000..19bbacc --- /dev/null +++ b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-2.snap @@ -0,0 +1,9 @@ +--- +source: crates/bsnext_html/tests/html_playground.rs +expression: js.kind +--- +Raw( + Raw { + raw: "class BSlive extends HTMLElement {\n connectedCallback() {\n this.innerHTML = \"Hello world\";\n }\n}\n\ncustomElements.define(\"abc-shane\", BSlive);\n", + }, +) diff --git a/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-3.snap b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-3.snap new file mode 100644 index 0000000..81a1760 --- /dev/null +++ b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-3.snap @@ -0,0 +1,9 @@ +--- +source: crates/bsnext_html/tests/html_playground.rs +expression: css.kind +--- +Raw( + Raw { + raw: "p {\n color: red;\n}\n\nabc-shane {\n color: red;\n}\n", + }, +) diff --git a/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content.snap b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content.snap new file mode 100644 index 0000000..e5ac0af --- /dev/null +++ b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content.snap @@ -0,0 +1,9 @@ +--- +source: crates/bsnext_html/tests/html_playground.rs +expression: html.kind +--- +Raw( + Html { + html: "\n
\n

Test!

\n \n
\n", + }, +) diff --git a/crates/bsnext_input/src/lib.rs b/crates/bsnext_input/src/lib.rs index 1decace..93ea622 100644 --- a/crates/bsnext_input/src/lib.rs +++ b/crates/bsnext_input/src/lib.rs @@ -109,7 +109,7 @@ impl InputCtx { self.prev_server_ids.as_ref().map(Vec::as_slice) } - pub fn first_id_or_default(&self) -> ServerIdentity { + pub fn first_id_or_named(&self) -> ServerIdentity { self.prev_server_ids .as_ref() .and_then(|x| x.get(0)) diff --git a/crates/bsnext_md/src/lib.rs b/crates/bsnext_md/src/lib.rs index bc1325f..fb673af 100644 --- a/crates/bsnext_md/src/lib.rs +++ b/crates/bsnext_md/src/lib.rs @@ -334,7 +334,7 @@ pub fn nodes_to_input(nodes: &[Node], ctx: &InputCtx) -> Result - Hello world 5.0 + Hello world 6.0 ``` @@ -9,7 +9,7 @@ @import url("reset.css"); :root { - border: 1px dotted pink; + border: 50px dotted orange; } * { From d336201a31b1238399acc41fb375edc966d039ba Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sat, 23 Nov 2024 22:53:16 +0000 Subject: [PATCH 5/8] support args on first setup call --- crates/bsnext_example/src/md.rs | 3 +- crates/bsnext_html/src/lib.rs | 12 +++++-- crates/bsnext_html/tests/html_playground.rs | 29 +++++++++++------ crates/bsnext_input/src/lib.rs | 31 +++++++++++++++---- crates/bsnext_system/src/lib.rs | 2 +- crates/bsnext_system/src/monitor.rs | 2 +- .../src/start_kind/start_from_inputs.rs | 11 +++++-- 7 files changed, 66 insertions(+), 24 deletions(-) diff --git a/crates/bsnext_example/src/md.rs b/crates/bsnext_example/src/md.rs index 4ae1452..ace7250 100644 --- a/crates/bsnext_example/src/md.rs +++ b/crates/bsnext_example/src/md.rs @@ -1,7 +1,6 @@ use bsnext_input::server_config::ServerIdentity; -use bsnext_input::{Input, InputCreation, InputCtx, InputSource, InputSourceKind}; +use bsnext_input::{InputCreation, InputSource, InputSourceKind}; use bsnext_md::md_fs::MdFs; -use bsnext_md::{nodes_to_input, MarkdownError}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct MdExample; diff --git a/crates/bsnext_html/src/lib.rs b/crates/bsnext_html/src/lib.rs index 820b8a0..d08490c 100644 --- a/crates/bsnext_html/src/lib.rs +++ b/crates/bsnext_html/src/lib.rs @@ -1,5 +1,5 @@ use bsnext_input::playground::Playground; -use bsnext_input::server_config::ServerConfig; +use bsnext_input::server_config::{ServerConfig, ServerIdentity}; use bsnext_input::{Input, InputCreation, InputCtx, InputError}; use std::fs::read_to_string; use std::path::Path; @@ -72,10 +72,18 @@ fn playground_html_str_to_input(html: &str, ctx: &InputCtx) -> Result p { color: red; @@ -31,8 +30,8 @@ const input: &str = r#" #[test] fn test_html_playground_content() -> anyhow::Result<()> { let idens = vec![]; - let ctx = InputCtx::new(&idens); - let as_input = HtmlFs::from_input_str(input, &ctx)?; + let ctx = InputCtx::new(&idens, None); + let as_input = HtmlFs::from_input_str(INPUT, &ctx)?; let Some(server) = as_input.servers.get(0) else { return Err(anyhow::anyhow!("no server")); }; @@ -59,8 +58,8 @@ fn test_html_playground_content() -> anyhow::Result<()> { #[test] fn test_html_playground_without_server_id() -> anyhow::Result<()> { let idens = vec![]; - let ctx = InputCtx::new(&idens); - let as_input = HtmlFs::from_input_str(input, &ctx)?; + let ctx = InputCtx::new(&idens, None); + let as_input = HtmlFs::from_input_str(INPUT, &ctx)?; assert_eq!(as_input.servers.len(), 1); let first = as_input.servers.get(0).unwrap(); let is_named = matches!(first.identity, ServerIdentity::Named { .. }); @@ -72,11 +71,23 @@ fn test_html_playground_with_server_id() -> anyhow::Result<()> { let ident = ServerIdentity::Address { bind_address: String::from("127.0.0.1:8080"), }; - let ctx = InputCtx::new(&[ident.clone()]); - let as_input = HtmlFs::from_input_str(input, &ctx)?; + let ctx = InputCtx::new(&[ident.clone()], None); + let as_input = HtmlFs::from_input_str(INPUT, &ctx)?; assert_eq!(as_input.servers.len(), 1); let first = as_input.servers.get(0).unwrap(); assert_eq!(ident, first.identity); Ok(()) } +#[test] +fn test_html_playground_with_port() -> anyhow::Result<()> { + let ident = ServerIdentity::Address { + bind_address: String::from("0.0.0.0:8080"), + }; + let input_args = InputArgs { port: Some(8080) }; + let ctx = InputCtx::new(&[], Some(input_args)); + let as_input = HtmlFs::from_input_str(INPUT, &ctx)?; + let first = as_input.servers.get(0).unwrap(); + assert_eq!(first.identity, ident); + Ok(()) +} diff --git a/crates/bsnext_input/src/lib.rs b/crates/bsnext_input/src/lib.rs index 93ea622..2d7e3a9 100644 --- a/crates/bsnext_input/src/lib.rs +++ b/crates/bsnext_input/src/lib.rs @@ -89,19 +89,27 @@ pub trait InputSource { } } +#[derive(Debug, Default, Clone)] +pub struct InputArgs { + pub port: Option, +} + #[derive(Debug, Default, Clone)] pub struct InputCtx { prev_server_ids: Option>, + args: Option, } impl InputCtx { - pub fn new(servers: &[ServerIdentity]) -> Self { - if servers.is_empty() { - Self::default() + pub fn new(servers: &[ServerIdentity], args: Option) -> Self { + let prev = if servers.is_empty() { + None } else { - Self { - prev_server_ids: Some(servers.to_vec()), - } + Some(servers.to_vec()) + }; + Self { + prev_server_ids: prev, + args: args.to_owned(), } } @@ -116,6 +124,17 @@ impl InputCtx { .map(ToOwned::to_owned) .unwrap_or_else(|| ServerIdentity::named()) } + + pub fn first_id(&self) -> Option { + self.prev_server_ids + .as_ref() + .and_then(|x| x.get(0)) + .map(ToOwned::to_owned) + } + + pub fn port(&self) -> Option { + self.args.as_ref().and_then(|x| x.port) + } } pub trait InputCreation { diff --git a/crates/bsnext_system/src/lib.rs b/crates/bsnext_system/src/lib.rs index e5cd64e..e74db75 100644 --- a/crates/bsnext_system/src/lib.rs +++ b/crates/bsnext_system/src/lib.rs @@ -305,7 +305,7 @@ impl Handler for BsSystem { .iter() .map(|x| x.identity.clone()) .collect::>(); - let input_ctx = InputCtx::new(&ids); + let input_ctx = InputCtx::new(&ids, None); ctx.notify(MonitorInput { path: path.clone(), cwd: cwd.clone(), diff --git a/crates/bsnext_system/src/monitor.rs b/crates/bsnext_system/src/monitor.rs index 25d2e7c..81c3a50 100644 --- a/crates/bsnext_system/src/monitor.rs +++ b/crates/bsnext_system/src/monitor.rs @@ -118,7 +118,7 @@ impl BsSystem { .iter() .map(|s| s.identity.clone()) .collect::>(); - let ctx = InputCtx::new(&next); + let ctx = InputCtx::new(&next, None); tracing::info!("will set next ids {:?}", next); if !next.is_empty() { mon.ctx = ctx diff --git a/crates/bsnext_system/src/start_kind/start_from_inputs.rs b/crates/bsnext_system/src/start_kind/start_from_inputs.rs index c39d755..16f7517 100644 --- a/crates/bsnext_system/src/start_kind/start_from_inputs.rs +++ b/crates/bsnext_system/src/start_kind/start_from_inputs.rs @@ -1,7 +1,7 @@ use bsnext_input::startup::{StartupContext, SystemStart, SystemStartArgs}; use crate::input_fs::from_input_path; -use bsnext_input::{Input, InputCtx, InputError}; +use bsnext_input::{Input, InputArgs, InputCtx, InputError}; use std::path::{Path, PathBuf}; #[derive(Debug)] @@ -12,7 +12,7 @@ pub struct StartFromInputPaths { impl SystemStart for StartFromInputPaths { fn input(&self, ctx: &StartupContext) -> Result> { - from_input_paths(&ctx.cwd, &self.input_paths) + from_input_paths(&ctx.cwd, &self.input_paths, &self.port) } } @@ -32,6 +32,7 @@ impl SystemStart for StartFromInput { fn from_input_paths>( cwd: &Path, inputs: &[T], + port: &Option, ) -> Result> { let input_candidates = inputs .iter() @@ -76,7 +77,11 @@ fn from_input_paths>( tracing::info!(?input_path); - let result = from_input_path(input_path, &InputCtx::default()); + let input_args = InputArgs { + port: port.to_owned(), + }; + let initial_ctx = InputCtx::new(&[], Some(input_args)); + let result = from_input_path(input_path, &initial_ctx); match result { Ok(input) => Ok(SystemStartArgs::PathWithInput { path: input_path.to_path_buf(), From 902ec7466ff4539d92b7eb4ff7684788d4f6189b Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sun, 24 Nov 2024 09:36:00 +0000 Subject: [PATCH 6/8] support writer --- Cargo.lock | 2 + bslive.html | 23 ++++++++ crates/bsnext_example/Cargo.toml | 1 + crates/bsnext_example/src/playground.rs | 6 +-- crates/bsnext_html/Cargo.toml | 1 + crates/bsnext_html/src/html_writer.rs | 54 +++++++++++++++++++ crates/bsnext_html/src/lib.rs | 4 +- ...ml_writer__html_writer_for_playground.snap | 13 +++++ crates/bsnext_html/tests/html_playground.rs | 11 ---- crates/bsnext_input/src/target.rs | 1 + crates/bsnext_system/src/start_kind.rs | 2 + examples/html/playground.html | 6 +-- 12 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 bslive.html create mode 100644 crates/bsnext_html/src/html_writer.rs create mode 100644 crates/bsnext_html/src/snapshots/bsnext_html__html_writer__html_writer_for_playground.snap diff --git a/Cargo.lock b/Cargo.lock index d2ae36d..c0a8474 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -525,6 +525,7 @@ dependencies = [ name = "bsnext_example" version = "0.2.2" dependencies = [ + "bsnext_html", "bsnext_input", "bsnext_md", "clap", @@ -554,6 +555,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bsnext_input", + "indent", "insta", "scraper", "unindent", diff --git a/bslive.html b/bslive.html new file mode 100644 index 0000000..6222540 --- /dev/null +++ b/bslive.html @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/crates/bsnext_example/Cargo.toml b/crates/bsnext_example/Cargo.toml index caf28d6..b6922b7 100644 --- a/crates/bsnext_example/Cargo.toml +++ b/crates/bsnext_example/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" [dependencies] bsnext_input = { path = "../bsnext_input" } bsnext_md = { path = "../bsnext_md" } +bsnext_html = { path = "../bsnext_html" } clap = { workspace = true } diff --git a/crates/bsnext_example/src/playground.rs b/crates/bsnext_example/src/playground.rs index 460fe9e..ad92e50 100644 --- a/crates/bsnext_example/src/playground.rs +++ b/crates/bsnext_example/src/playground.rs @@ -1,14 +1,14 @@ +use bsnext_html::HtmlFs; use bsnext_input::server_config::ServerIdentity; use bsnext_input::{InputCreation, InputCtx, InputSource, InputSourceKind}; -use bsnext_md::md_fs::MdFs; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct PlaygroundExample; impl InputSource for PlaygroundExample { fn into_input(self, identity: Option) -> InputSourceKind { - let input_str = include_str!("../../../examples/markdown/playground.md"); - let mut input = MdFs::from_input_str(input_str, &InputCtx::default()).unwrap(); + let input_str = include_str!("../../../examples/html/playground.html"); + let mut input = HtmlFs::from_input_str(input_str, &InputCtx::default()).unwrap(); // update the server identity if it was provided if let (Some(server), Some(identity)) = (input.servers.get_mut(0), identity) { diff --git a/crates/bsnext_html/Cargo.toml b/crates/bsnext_html/Cargo.toml index 8400258..bb874e8 100644 --- a/crates/bsnext_html/Cargo.toml +++ b/crates/bsnext_html/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] bsnext_input = { path = "../bsnext_input" } unindent = "0.2.3" +indent = "0.1.1" scraper = "0.21.0" [dev-dependencies] diff --git a/crates/bsnext_html/src/html_writer.rs b/crates/bsnext_html/src/html_writer.rs new file mode 100644 index 0000000..62623d6 --- /dev/null +++ b/crates/bsnext_html/src/html_writer.rs @@ -0,0 +1,54 @@ +use bsnext_input::playground::Playground; +use bsnext_input::server_config::ServerConfig; +use bsnext_input::{Input, InputWriter}; + +pub struct HtmlWriter; + +impl InputWriter for HtmlWriter { + fn input_to_str(&self, input: &Input) -> String { + if input.servers.is_empty() { + todo!("html requires at least 1 server definition") + } + if input.servers.len() > 1 { + todo!("more than 1 server not supported yet") + } + let server = input.servers.first().expect("must access first"); + let Some(playground) = &server.playground else { + todo!("only playground is supported in HTML for now") + }; + let mut blocks: Vec = vec![]; + if let Some(css) = &playground.css { + blocks.push("".into()); + } + blocks.push(playground.html.clone()); + if let Some(js) = &playground.js { + blocks.push("".into()); + } + blocks.join("\n") + } +} + +#[test] +fn test_html_writer_for_playground() { + let css = r#"body { + background: red; +}"#; + let js = r#"console.log("hello world!")"#; + let playground = Playground { + html: "

Hello world

".to_string(), + js: Some(js.to_string()), + css: Some(css.to_string()), + }; + let mut input = Input::default(); + let mut server = ServerConfig::default(); + server.playground = Some(playground); + input.servers.push(server); + let output = HtmlWriter.input_to_str(&input); + insta::assert_snapshot!(output); +} diff --git a/crates/bsnext_html/src/lib.rs b/crates/bsnext_html/src/lib.rs index d08490c..66708c6 100644 --- a/crates/bsnext_html/src/lib.rs +++ b/crates/bsnext_html/src/lib.rs @@ -4,6 +4,8 @@ use bsnext_input::{Input, InputCreation, InputCtx, InputError}; use std::fs::read_to_string; use std::path::Path; +pub mod html_writer; + pub struct HtmlFs; impl InputCreation for HtmlFs { @@ -82,8 +84,8 @@ fn playground_html_str_to_input(html: &str, ctx: &InputCtx) -> Result + body { + background: red; + } + +

Hello world

+ diff --git a/crates/bsnext_html/tests/html_playground.rs b/crates/bsnext_html/tests/html_playground.rs index e9d99a0..07e5dc2 100644 --- a/crates/bsnext_html/tests/html_playground.rs +++ b/crates/bsnext_html/tests/html_playground.rs @@ -42,17 +42,6 @@ fn test_html_playground_content() -> anyhow::Result<()> { assert_debug_snapshot!(html.kind); assert_debug_snapshot!(js.kind); assert_debug_snapshot!(css.kind); - // assert_debug_snapshot!(js.kind); - // assert_debug_snapshot!(css.kind); - // Ok(()) - // for x in server.routes() { - // dbg!(&x.kind); - // match &x.kind { - // RouteKind::Raw(RawRoute::()) => {} - // RouteKind::Proxy(_) => {} - // RouteKind::Dir(_) => {} - // } - // } Ok(()) } #[test] diff --git a/crates/bsnext_input/src/target.rs b/crates/bsnext_input/src/target.rs index 39bf79f..3d863d2 100644 --- a/crates/bsnext_input/src/target.rs +++ b/crates/bsnext_input/src/target.rs @@ -4,4 +4,5 @@ pub enum TargetKind { Yaml, Toml, Md, + Html, } diff --git a/crates/bsnext_system/src/start_kind.rs b/crates/bsnext_system/src/start_kind.rs index ed300c9..bcd0565 100644 --- a/crates/bsnext_system/src/start_kind.rs +++ b/crates/bsnext_system/src/start_kind.rs @@ -86,11 +86,13 @@ pub mod start_fs { TargetKind::Yaml => bsnext_yaml::yaml_writer::YamlWriter.input_to_str(input), TargetKind::Toml => todo!("toml missing"), TargetKind::Md => bsnext_md::md_writer::MdWriter.input_to_str(input), + TargetKind::Html => bsnext_html::html_writer::HtmlWriter.input_to_str(input), }; let name = match target_kind { TargetKind::Yaml => "bslive.yml", TargetKind::Toml => todo!("toml missing"), TargetKind::Md => "bslive.md", + TargetKind::Html => "bslive.html", }; let next_path = cwd.join(name); tracing::info!( diff --git a/examples/html/playground.html b/examples/html/playground.html index dc67872..8f0d1c7 100644 --- a/examples/html/playground.html +++ b/examples/html/playground.html @@ -3,12 +3,12 @@ color: red; } - abc-shane { + abc-element { color: red; } - + \ No newline at end of file From 4958c8997cf37cca32e8d1918df720d4a881f1f7 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sun, 24 Nov 2024 12:00:24 +0000 Subject: [PATCH 7/8] updated tests --- examples/html/playground.html | 2 +- examples/markdown/playground.md | 15 ++++++++------- tests/playground.spec.ts | 24 +++++++++++++++++++++++- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/examples/html/playground.html b/examples/html/playground.html index 8f0d1c7..a234b1f 100644 --- a/examples/html/playground.html +++ b/examples/html/playground.html @@ -13,7 +13,7 @@