Skip to content

Commit

Permalink
Open browser after initial build for trunk serve.
Browse files Browse the repository at this point in the history
Related to #4
  • Loading branch information
thedodd committed Aug 25, 2020
1 parent f170147 commit 6d71287
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 25 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ futures = "0.3.5"
grass = "0.10.3"
nipper = "0.1.8"
notify = "4.0.15"
open = "1.4.0"
seahash = "4.0.1"
serde = { version="1", features=["derive"] }
structopt = "0.3.16"
Expand Down
33 changes: 13 additions & 20 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ const HREF_ATTR: &str = "href";
pub struct BuildSystem {
/// The `Cargo.toml` manifest of the app being built.
pub manifest: CargoManifest,
/// The source HTML document of the app being built.
///
/// When running in watch mode, this object can be swapped out if changes are detected on the
/// document itself.
pub target_html: Document,
/// The path to the source HTML document from which the output `index.html` will be built.
pub target_html_path: Arc<PathBuf>,
/// The parent directory of `target_html_path`.
Expand Down Expand Up @@ -70,13 +65,11 @@ impl BuildSystem {
.join(mode_segment);
let target_html_path = target_html_path.canonicalize()
.map_err(|err| anyhow!("failed to get canonical path of target HTML file: {}", err))?;
let target_html_raw = fs::read_to_string(&target_html_path).await?;
let target_html = Document::from(&target_html_raw);
let target_html_dir = target_html_path.parent()
.ok_or_else(|| anyhow!("failed to determine parent dir of target HTML file"))?
.to_owned();
Ok(Self{
manifest, target_html, release, public_url,
manifest, release, public_url,
target_html_path: Arc::new(target_html_path),
target_html_dir: Arc::new(target_html_dir),
dist: Arc::new(dist),
Expand All @@ -90,7 +83,7 @@ impl BuildSystem {
pub async fn build_app(&mut self) -> Result<()> {
// Update the contents of the source HTML.
let target_html_raw = fs::read_to_string(self.target_html_path.as_ref()).await?;
self.target_html = Document::from(&target_html_raw);
let mut target_html = Document::from(&target_html_raw);

// Spawn cargo build. It will run concurrently without polling.
// When ready, await to get the final output.
Expand All @@ -101,24 +94,24 @@ impl BuildSystem {
fs::create_dir_all(self.bindgen_out.as_ref()).await?;

// Begin processing source HTML assets. Asset pipeline handles are pushed to `self.pipelines`.
self.spawn_asset_pipelines().await?;
self.spawn_asset_pipelines(&mut target_html).await?;

// Spawn the wasm-bindgen call to perform that last leg of application setup.
let bindgen_file_name = cargo_build_handle.await?; // We need the `cargo build` output first.
let wasm_bindgen_output = self.spawn_wasm_bindgen_build(bindgen_file_name).await?;

// Finalize asset pipelines.
self.finalize_asset_pipelines().await;
self.insert_wasm_module(&wasm_bindgen_output);
self.finalize_asset_pipelines(&mut target_html).await;
self.insert_wasm_module(&wasm_bindgen_output, &mut target_html);

// Assemble a new output index.html file.
let output_html = self.target_html.html(); // TODO: prettify this output.
let output_html = target_html.html(); // TODO: prettify this output.
fs::write(format!("{}/index.html", self.dist.display()), output_html.as_bytes()).await?;
Ok(())
}

/// Finalize asset pipelines & prep the DOM for final output.
async fn finalize_asset_pipelines(&mut self) {
async fn finalize_asset_pipelines(&mut self, target_html: &mut Document) {
while let Some(asset_res) = self.pipelines.next().await {
// Unpack the asset pipeline result.
let asset = match asset_res {
Expand All @@ -129,22 +122,22 @@ impl BuildSystem {
}
};
// Update the DOM based on asset output.
let mut node = self.target_html.select(&format!("[{}={}]", TRUNK_ID, &asset.id));
let mut node = target_html.select(&format!("[{}={}]", TRUNK_ID, &asset.id));
node.remove_attr(TRUNK_ID);
node.remove_attr(HREF_ATTR);
node.set_attr(HREF_ATTR, &format!("{}{}", &self.public_url, &asset.file_name));
}
// Remove any additional trunk IDs from the DOM.
self.target_html.select(&format!("[{}]", TRUNK_ID)).remove_attr(TRUNK_ID);
target_html.select(&format!("[{}]", TRUNK_ID)).remove_attr(TRUNK_ID);
}

/// Insert the finalized WASM into the output HTML.
fn insert_wasm_module(&mut self, wasm: &WasmBindgenOutput) {
fn insert_wasm_module(&mut self, wasm: &WasmBindgenOutput, target_html: &mut Document) {
let script = format!(
r#"<script type="module">import init from '/{}';init('/{}');</script>"#,
&wasm.js_output, &wasm.wasm_output,
);
self.target_html.select("head").append_html(script);
target_html.select("head").append_html(script);
}

/// Spawn a cargo build process.
Expand Down Expand Up @@ -234,11 +227,11 @@ impl BuildSystem {
/// Assets are given an ID which corresponds to an ID added to the DOM. Once the processing
/// for the asset is finished, it will be able to update the DOM correctly based on its own
/// ID. All of these trunk specific IDs will be removed from the DOM before it is written.
async fn spawn_asset_pipelines(&mut self) -> Result<()> {
async fn spawn_asset_pipelines(&mut self, target_html: &mut Document) -> Result<()> {
println!("📦 spawning asset pipelines");

// Accumulate stylesheet assets to be processed.
let style_assets = self.target_html.select(r#"html head link"#)
let style_assets = target_html.select(r#"html head link"#)
.iter()
.filter_map(|node| {
// Be sure our link has an href to process, else skip.
Expand Down
26 changes: 21 additions & 5 deletions src/cmd/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;

use anyhow::Result;
use async_std::fs;
use async_std::task::spawn_local;
use async_std::task::{spawn, spawn_local, JoinHandle};
use structopt::StructOpt;
use tide::{Request, Response, Middleware, Next, StatusCode};
use tide::http::mime;
Expand Down Expand Up @@ -38,12 +38,26 @@ pub struct Serve {
impl Serve {
pub async fn run(self) -> Result<()> {
let (target, release, dist, public_url, ignore) = (
self.target, self.release, self.dist.clone(), self.public_url, self.ignore.unwrap_or_default(),
self.target.clone(), self.release, self.dist.clone(), self.public_url.clone(), self.ignore.clone().unwrap_or_default(),
);
let mut watcher = WatchSystem::new(target, release, dist, public_url, ignore).await?;
watcher.build().await;

// Spawn the watcher & the server.
let watch_handle = spawn_local(watcher.run());
let server_handle = self.spawn_server()?;

// Open the browser.
if let Err(err) = open::that(format!("http://localhost:{}", self.port)) {
eprintln!("error opening browser: {}", err);
}

server_handle.await;
watch_handle.await;
Ok(())
}

fn spawn_server(&self) -> Result<JoinHandle<()>> {
// Prep state.
let listen_addr = format!("0.0.0.0:{}", self.port);
let index = Arc::new(self.dist.join("index.html"));
Expand All @@ -55,9 +69,11 @@ impl Serve {

// Listen and serve.
println!("📡 {}", format!("listening at http://{}", &listen_addr));
app.listen(listen_addr).await?;
watch_handle.await;
Ok(())
Ok(spawn(async move {
if let Err(err) = app.listen(listen_addr).await {
eprintln!("{}", err);
}
}))
}
}

Expand Down

0 comments on commit 6d71287

Please sign in to comment.