Skip to content
This repository has been archived by the owner on Aug 3, 2023. It is now read-only.

Commit

Permalink
Xortive malonso/hotreload + windows ampersand escape fix (#451)
Browse files Browse the repository at this point in the history
* changed to use old script id instead of sessions

watch_paths added to wrangler.toml

* added watch option to wranglerjs

* hotreload demo

some refactoring/code cleanup work

added session id to message

* add support for running wranglerjs in watch mode, utilizing webpack's
watcher

* check for debug flag to use localhost for preview server

* remove watch module + refs

* added deps

* please the borrow checker

* cleanup

* fix temp file

* update querystring params, use sessions instead of old_id/new_id

* remove wranglerjs console logs

* finalize communication with the fiddle, add port + session support

* move watcher inside thread closure to have the correct lifetime

* s/build_and_watch/watch_and_build, put watch_and_build inside its own module

* add cooldown period for fs events

* don't swallow tx errors from watch_and_build

* fix wrangler build --watch

* made messaging use message crate, removed some println!

* cargo fmt

* Add message when ignoring stale first change

* Check origin header and close if invalid

* separate out fiddle message server into its own module

* fixed debug save_origins, cargo_fmt, added safe_origins log message

* updated preview url

* fix origins

* don't use hotreload host for normal open command

* remove allowing connections from msg is is confusing

* cargo fmt

* fix origin check and add localhost check

* better errors

* add build_and_watch

* webpack watching works -- code is littered with debug statements and leaves threads open, need to fix that

* add guardedcommand for running wranglerjs in --watch while having it die with the main thread

* cargo fmt

* DRY open browser function

* please clippy

* namespace query params

* cargo fmt

* use info@ for fiddle messenger logs

* better log messages for wranglerjs + use info@ for watching temp file message as to not confuse the user

* fix double build bug

* cleanup fiddle messenger: use ws::Result instead of alias

* Keep trying port+1 so you can have multiple previews open

* also watch entry point for rust

* better update message

* update preview service url

* Use strict equality check in wranglerjs/index.js

Co-Authored-By: Sven Sauleau <[email protected]>

* Hide editor on wrangler preview without watch

* Better messaging for preview update message

* use NonRecursive watcher for watch paths that always point to files

* bind to port 0 to allow the OS to assign us a port, instead of using our
own logic for picking a port

* cargo fmt

* Make open_browser more robust

* cargo fmt

* disable wrangler build --watch for now

* PR feedback: namespacing, command matching, remove unnecessary unwraps

* fix(preview): escape & in windows URL

* fix(style): appease cargo fmt

* fix(clippy): unecessary if let to is_some

* fix(watch): recurisve plz k thx

* fix(depr): ... -> ..=

* fix(preview): one last recursive
  • Loading branch information
ashleygwilliams authored and xortive committed Aug 16, 2019
1 parent 3acc885 commit fee35f2
Show file tree
Hide file tree
Showing 15 changed files with 856 additions and 253 deletions.
232 changes: 225 additions & 7 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ base64 = "0.10.1"
lazy_static = "1.3.0"
text_io = "0.1.7"
exitfailure = "0.5.1"
notify = "4.0.12"
ws = "0.9.0"

[dev-dependencies]
assert_cmd = "0.11.1"
Expand Down
11 changes: 7 additions & 4 deletions src/commands/build/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
pub mod wranglerjs;

mod watch;
pub use watch::watch_and_build;

use crate::settings::project::{Project, ProjectType};
use crate::terminal::message;
use crate::{commands, install};

use std::path::PathBuf;
use std::process::Command;

use crate::terminal::message;

pub fn build(project: &Project) -> Result<(), failure::Error> {
let project_type = &project.project_type;
match project_type {
Expand All @@ -18,7 +21,7 @@ pub fn build(project: &Project) -> Result<(), failure::Error> {
let binary_path = install::install(tool_name, "rustwasm")?.binary(tool_name)?;
let args = ["build", "--target", "no-modules"];

let command = command(&args, binary_path);
let command = command(&args, &binary_path);
let command_name = format!("{:?}", command);

commands::run(command, &command_name)?;
Expand All @@ -31,7 +34,7 @@ pub fn build(project: &Project) -> Result<(), failure::Error> {
Ok(())
}

fn command(args: &[&str], binary_path: PathBuf) -> Command {
pub fn command(args: &[&str], binary_path: &PathBuf) -> Command {
message::working("Compiling your project to WebAssembly...");

let mut c = if cfg!(target_os = "windows") {
Expand Down
89 changes: 89 additions & 0 deletions src/commands/build/watch/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
mod watcher;
pub use watcher::wait_for_changes;

use crate::commands::build::{command, wranglerjs};
use crate::commands::publish::Package;
use crate::settings::project::{Project, ProjectType};
use crate::terminal::message;
use crate::{commands, install};

use notify::{self, RecursiveMode, Watcher};
use std::env;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

pub const COOLDOWN_PERIOD: Duration = Duration::from_millis(2000);

/// watch a project for changes and re-build it when necessary,
/// outputting a build event to tx.
pub fn watch_and_build(
project: &Project,
tx: Option<mpsc::Sender<()>>,
) -> Result<(), failure::Error> {
let project_type = &project.project_type;
match project_type {
ProjectType::JavaScript => {
let package = Package::new("./")?;
let entry = package.main()?;
thread::spawn(move || {
let (watcher_tx, watcher_rx) = mpsc::channel();
let mut watcher = notify::watcher(watcher_tx, Duration::from_secs(1)).unwrap();

watcher.watch(&entry, RecursiveMode::Recursive).unwrap();
message::info(&format!("watching {:?}", &entry));

loop {
match wait_for_changes(&watcher_rx, COOLDOWN_PERIOD) {
Ok(_path) => {
if let Some(tx) = tx.clone() {
tx.send(()).expect("--watch change message failed to send");
}
}
Err(_) => message::user_error("Something went wrong while watching."),
}
}
});
}
ProjectType::Rust => {
let tool_name = "wasm-pack";
let binary_path = install::install(tool_name, "rustwasm")?.binary(tool_name)?;
let args = ["build", "--target", "no-modules"];

let package = Package::new("./")?;
let entry = package.main()?;

thread::spawn(move || {
let (watcher_tx, watcher_rx) = mpsc::channel();
let mut watcher = notify::watcher(watcher_tx, Duration::from_secs(1)).unwrap();

let mut path = env::current_dir().expect("current dir");
path.push("src");

watcher.watch(&path, RecursiveMode::Recursive).unwrap();
watcher.watch(&entry, RecursiveMode::Recursive).unwrap();
message::info(&format!("watching {:?} and {:?}", &path, &entry));

loop {
match wait_for_changes(&watcher_rx, COOLDOWN_PERIOD) {
Ok(_path) => {
let command = command(&args, &binary_path);
let command_name = format!("{:?}", command);
if commands::run(command, &command_name).is_ok() {
if let Some(tx) = tx.clone() {
tx.send(()).expect("--watch change message failed to send");
}
}
}
Err(_) => message::user_error("Something went wrong while watching."),
}
}
});
}
ProjectType::Webpack => {
wranglerjs::run_build_and_watch(project, tx)?;
}
}

Ok(())
}
50 changes: 50 additions & 0 deletions src/commands/build/watch/watcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use notify::DebouncedEvent;
use std::path::PathBuf;
use std::sync::mpsc::Receiver;
use std::time::Duration;

use failure::{format_err, Error};

use crate::terminal::message;
use log::info;

///Add cooldown for all types of events to watching logic
pub fn wait_for_changes(
rx: &Receiver<DebouncedEvent>,
cooldown: Duration,
) -> Result<PathBuf, Error> {
loop {
let event = rx.recv()?;
match get_changed_path_from_event(event) {
Ok(Some(path)) => {
message::working("Detected changes...");
//wait for cooldown
while let Ok(_) = rx.recv_timeout(cooldown) {
message::working("Detected change during cooldown...");
}
message::working("Cooldown over, propogating changes...");
return Ok(path);
}
Ok(None) => {
continue; //was an event type we don't care about, continue
}
Err(error) => {
message::user_error(&format!("WatchError {:?}", error));
continue;
}
};
}
}

fn get_changed_path_from_event(event: DebouncedEvent) -> Result<Option<PathBuf>, Error> {
info!("Detected Event {:?}", event);
match event {
DebouncedEvent::Error(error, _) => Err(format_err!("{:?}", error)),
DebouncedEvent::NoticeWrite(path) => Ok(Some(path)),
DebouncedEvent::Write(path) => Ok(Some(path)),
DebouncedEvent::NoticeRemove(path) => Ok(Some(path)),
DebouncedEvent::Remove(path) => Ok(Some(path)),
DebouncedEvent::Create(path) => Ok(Some(path)),
_ => Ok(None),
}
}
97 changes: 80 additions & 17 deletions src/commands/build/wranglerjs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
mod bundle;
pub mod output;

use crate::commands::build::watch::wait_for_changes;
use crate::commands::build::watch::COOLDOWN_PERIOD;

use crate::commands::publish::package::Package;
use crate::install;
use crate::util;
pub use bundle::Bundle;
use fs2::FileExt;
use log::info;
Expand All @@ -17,9 +21,13 @@ use std::path::{Path, PathBuf};
use std::process::Command;

use crate::settings::project::Project;

use crate::terminal::message;

use notify::{self, RecursiveMode, Watcher};
use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::time::Duration;

// Run the underlying {wranglerjs} executable.
//
// In Rust we create a virtual file, pass the pass to {wranglerjs}, run the
Expand All @@ -39,26 +47,81 @@ pub fn run_build(project: &Project) -> Result<(), failure::Error> {
let wranglerjs_output: WranglerjsOutput =
serde_json::from_str(&output).expect("could not parse wranglerjs output");

if wranglerjs_output.has_errors() {
message::user_error(wranglerjs_output.get_errors().as_str());
failure::bail!("Webpack returned an error");
}
write_wranglerjs_output(&bundle, &wranglerjs_output)
} else {
failure::bail!("failed to execute `{:?}`: exited with {}", command, status)
}
}

bundle
.write(&wranglerjs_output)
.expect("could not write bundle to disk");
pub fn run_build_and_watch(
project: &Project,
tx: Option<Sender<()>>,
) -> Result<(), failure::Error> {
let (mut command, temp_file, bundle) = setup_build(project)?;
command.arg("--watch=1");

info!("Running {:?} in watch mode", command);

//Turbofish the result of the closure so we can use ?
thread::spawn::<_, Result<(), failure::Error>>(move || {
let _command_guard = util::GuardedCommand::spawn(command);

let (watcher_tx, watcher_rx) = channel();
let mut watcher = notify::watcher(watcher_tx, Duration::from_secs(1))?;

watcher.watch(&temp_file, RecursiveMode::Recursive)?;

info!("watching temp file {:?}", &temp_file);

let mut is_first = true;

loop {
match wait_for_changes(&watcher_rx, COOLDOWN_PERIOD) {
Ok(_) => {
if is_first {
is_first = false;
message::info("Ignoring stale first change");
//skip the first change event
//so we don't do a refresh immediately
continue;
}

let output = fs::read_to_string(&temp_file).expect("could not retrieve ouput");
let wranglerjs_output: WranglerjsOutput =
serde_json::from_str(&output).expect("could not parse wranglerjs output");

if write_wranglerjs_output(&bundle, &wranglerjs_output).is_ok() {
if let Some(tx) = tx.clone() {
tx.send(()).expect("--watch change message failed to send");
}
}
}
Err(_) => message::user_error("Something went wrong while watching."),
}
}
});

let msg = format!(
"Built successfully, built project size is {}",
wranglerjs_output.project_size()
);
Ok(())
}

fs::remove_file(temp_file)?;
message::success(&msg);
Ok(())
} else {
failure::bail!("failed to execute `{:?}`: exited with {}", command, status)
fn write_wranglerjs_output(
bundle: &Bundle,
output: &WranglerjsOutput,
) -> Result<(), failure::Error> {
if output.has_errors() {
message::user_error(output.get_errors().as_str());
failure::bail!("Webpack returned an error");
}

bundle.write(output)?;

let msg = format!(
"Built successfully, built project size is {}",
output.project_size()
);

message::success(&msg);
Ok(())
}

//setup a build to run wranglerjs, return the command, the ipc temp file, and the bundle
Expand Down
4 changes: 2 additions & 2 deletions src/commands/build/wranglerjs/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ impl WranglerjsOutput {
};

match compressed_size {
WARN_THRESHOLD...MAX_PROJECT_SIZE => format!("{}. {2} Your built project is {} away from reaching the 1MiB size limit. {2}", human_size, human_leftover.expect("failed to get leftover bytes"), emoji::WARN),
0...MAX_BEFORE_WARN => format!("{}.", human_size),
WARN_THRESHOLD..=MAX_PROJECT_SIZE => format!("{}. {2} Your built project is {} away from reaching the 1MiB size limit. {2}", human_size, human_leftover.expect("failed to get leftover bytes"), emoji::WARN),
0..=MAX_BEFORE_WARN => format!("{}.", human_size),
_ => format!("{}. {1} Your built project has grown past the 1MiB size limit and may fail to deploy. {1}", human_size, emoji::WARN)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod whoami;

pub use self::config::global_config;
pub use build::build;
pub use build::watch_and_build;
pub use generate::generate;
pub use init::init;
pub use publish::preview::preview;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/publish/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod preview;
mod route;
mod upload_form;

use package::Package;
pub use package::Package;
use route::Route;
use upload_form::build_script_upload_form;

Expand Down
Loading

0 comments on commit fee35f2

Please sign in to comment.