Skip to content

Commit

Permalink
feat(cli): add live reload to dev server (#193)
Browse files Browse the repository at this point in the history
* feat(cli): add live-reload to dev command

* chore: add changeset
  • Loading branch information
QuiiBz committed Oct 19, 2022
1 parent bfa85c7 commit b9f2623
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/khaki-doors-arrive.md
@@ -0,0 +1,5 @@
---
'@lagon/cli': patch
---

Add live-reload to dev server
80 changes: 80 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 packages/cli/Cargo.toml
Expand Up @@ -21,3 +21,4 @@ pathdiff = "0.2.1"
hyper-tls = { version = "0.5.0", features = ["vendored"] }
flume = "0.10.14"
chrono = "0.4.22"
notify = "5.0.0"
2 changes: 1 addition & 1 deletion packages/cli/src/commands/build.rs
Expand Up @@ -20,7 +20,7 @@ pub fn build(
};

let public_dir = validate_public_dir(public_dir)?;
let (index, assets) = bundle_function(file, client, public_dir)?;
let (index, assets) = bundle_function(&file, &client, &public_dir)?;

let end_progress = print_progress("Writting index.js...");

Expand Down
19 changes: 13 additions & 6 deletions packages/cli/src/commands/deploy.rs
Expand Up @@ -116,7 +116,8 @@ pub async fn deploy(
organization_id: organization.id.clone(),
})?;

create_deployment(function.id.clone(), file, client, public_dir, &config).await?;
create_deployment(function.id.clone(), &file, &client, &public_dir, &config)
.await?;
}
false => {
let name = Input::<String>::new()
Expand Down Expand Up @@ -147,8 +148,14 @@ pub async fn deploy(
organization_id: organization.id.clone(),
})?;

create_deployment(response.result.data.id, file, client, public_dir, &config)
.await?;
create_deployment(
response.result.data.id,
&file,
&client,
&public_dir,
&config,
)
.await?;
}
};

Expand All @@ -159,9 +166,9 @@ pub async fn deploy(

create_deployment(
function_config.function_id,
file,
client,
public_dir,
&file,
&client,
&public_dir,
&config,
)
.await
Expand Down
62 changes: 47 additions & 15 deletions packages/cli/src/commands/dev.rs
Expand Up @@ -7,22 +7,25 @@ use hyper::{Body, Request as HyperRequest, Response as HyperResponse, Server};
use lagon_runtime::http::{Request, Response, RunResult, StreamResult};
use lagon_runtime::isolate::{Isolate, IsolateOptions};
use lagon_runtime::runtime::{Runtime, RuntimeOptions};
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use std::collections::HashMap;
use std::convert::Infallible;
use std::io::Cursor;
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use std::{io, path::PathBuf};
use tokio::sync::Mutex;

use crate::utils::{bundle_function, input, success, validate_code_file, validate_public_dir};
use crate::utils::{
bundle_function, info, input, success, validate_code_file, validate_public_dir, FileCursor,
};

// This function is similar to packages/serverless/src/main.rs,
// expect that we don't have multiple deployments and such multiple
// threads to manage.
async fn handle_request(
req: HyperRequest<Body>,
index: Arc<Cursor<Vec<u8>>>,
assets: Arc<HashMap<String, Cursor<Vec<u8>>>>,
content: Arc<Mutex<(FileCursor, HashMap<String, FileCursor>)>>,
) -> Result<HyperResponse<Body>, Infallible> {
let mut url = req.uri().to_string();

Expand All @@ -37,6 +40,7 @@ async fn handle_request(
url.remove(0);

let (tx, rx) = flume::bounded(1);
let (index, assets) = content.lock().await.to_owned();

if let Some(asset) = assets.iter().find(|asset| *asset.0 == url) {
println!(" {}", input("Asset found"));
Expand Down Expand Up @@ -164,10 +168,9 @@ pub async fn dev(
};

let public_dir = validate_public_dir(public_dir)?;
let (index, assets) = bundle_function(file, client, public_dir)?;
let (index, assets) = bundle_function(&file, &client, &public_dir)?;

let index = Arc::new(index);
let assets = Arc::new(assets);
let content = Arc::new(Mutex::new((index, assets)));

let runtime = Runtime::new(RuntimeOptions::default());
let addr = format!(
Expand All @@ -178,16 +181,45 @@ pub async fn dev(
.parse()
.unwrap();

let server = Server::bind(&addr).serve(make_service_fn(move |_conn| {
let index = index.clone();
let assets = assets.clone();
let server_content = content.clone();

async move {
Ok::<_, Infallible>(service_fn(move |req| {
handle_request(req, index.clone(), assets.clone())
}))
let server =
Server::bind(&addr).serve(make_service_fn(move |_conn| {
let content = server_content.clone();

async move {
Ok::<_, Infallible>(service_fn(move |req| handle_request(req, content.clone())))
}
}));

let (tx, rx) = std::sync::mpsc::channel();
let mut watcher = RecommendedWatcher::new(
tx,
Config::default().with_poll_interval(Duration::from_secs(1)),
)
.unwrap();

watcher
.watch(file.parent().unwrap(), RecursiveMode::Recursive)
.unwrap();

let watcher_content = content.clone();

tokio::spawn(async move {
let content = watcher_content.clone();

for _ in rx {
// Clear the screen and put the cursor at first row & first col of the screen.
print!("\x1B[2J\x1B[1;1H");
println!("{}", info("Found change, updating..."));

let (index, assets) = bundle_function(&file, &client, &public_dir)?;

*content.lock().await = (index, assets);
}
}));

Ok::<(), io::Error>(())
});

println!();
println!("{}", success("Dev Server started!"));
Expand Down
18 changes: 9 additions & 9 deletions packages/cli/src/utils/deployments.rs
Expand Up @@ -16,7 +16,7 @@ use crate::utils::{debug, print_progress, success, TrpcClient};

use super::Config;

type FileCursor = Cursor<Vec<u8>>;
pub type FileCursor = Cursor<Vec<u8>>;

#[derive(Serialize, Deserialize, Debug)]
pub struct DeploymentConfig {
Expand Down Expand Up @@ -88,9 +88,9 @@ fn esbuild(file: &PathBuf) -> io::Result<FileCursor> {
}

pub fn bundle_function(
index: PathBuf,
client: Option<PathBuf>,
public_dir: PathBuf,
index: &PathBuf,
client: &Option<PathBuf>,
public_dir: &PathBuf,
) -> io::Result<(FileCursor, HashMap<String, FileCursor>)> {
if Command::new("esbuild").arg("--version").output().is_err() {
return Err(Error::new(
Expand All @@ -100,14 +100,14 @@ pub fn bundle_function(
}

let end_progress = print_progress("Bundling Function handler...");
let index_output = esbuild(&index)?;
let index_output = esbuild(index)?;
end_progress();

let mut assets = HashMap::<String, FileCursor>::new();

if let Some(client) = client {
let end_progress = print_progress("Bundling client file...");
let client_output = esbuild(&client)?;
let client_output = esbuild(client)?;
end_progress();

assets.insert(
Expand Down Expand Up @@ -184,9 +184,9 @@ struct DeployDeploymentResponse {

pub async fn create_deployment(
function_id: String,
file: PathBuf,
client: Option<PathBuf>,
public_dir: PathBuf,
file: &PathBuf,
client: &Option<PathBuf>,
public_dir: &PathBuf,
config: &Config,
) -> io::Result<()> {
let (index, assets) = bundle_function(file, client, public_dir)?;
Expand Down

0 comments on commit b9f2623

Please sign in to comment.