Made `mdbook watch` and `mdbook serve` rebuild the book again (#508)
Made `mdbook watch` and `mdbook serve` rebuild the book again
This commit is contained in:
parent
d69bc9c7c3
commit
396426662d
|
@ -3,12 +3,11 @@ extern crate staticfile;
|
||||||
extern crate ws;
|
extern crate ws;
|
||||||
|
|
||||||
use std;
|
use std;
|
||||||
use std::path::PathBuf;
|
|
||||||
use self::iron::{status, AfterMiddleware, Chain, Iron, IronError, IronResult, Request, Response,
|
use self::iron::{status, AfterMiddleware, Chain, Iron, IronError, IronResult, Request, Response,
|
||||||
Set};
|
Set};
|
||||||
use clap::{App, ArgMatches, SubCommand};
|
use clap::{App, ArgMatches, SubCommand};
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
use mdbook::errors::Result;
|
use mdbook::errors::*;
|
||||||
use {get_book_dir, open};
|
use {get_book_dir, open};
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
use watch;
|
use watch;
|
||||||
|
@ -18,28 +17,21 @@ struct ErrorRecover;
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
SubCommand::with_name("serve")
|
SubCommand::with_name("serve")
|
||||||
.about(
|
.about("Serve the book at http://localhost:3000. Rebuild and reload on change.")
|
||||||
"Serve the book at http://localhost:3000. Rebuild and reload on change.",
|
|
||||||
)
|
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"[dir] 'A directory for your book{n}(Defaults to \
|
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
|
||||||
Current Directory when omitted)'",
|
|
||||||
)
|
|
||||||
.arg_from_usage(
|
|
||||||
"-d, --dest-dir=[dest-dir] 'The output directory for \
|
|
||||||
your book{n}(Defaults to ./book when omitted)'",
|
|
||||||
)
|
)
|
||||||
.arg_from_usage("-p, --port=[port] 'Use another port{n}(Defaults to 3000)'")
|
.arg_from_usage("-p, --port=[port] 'Use another port{n}(Defaults to 3000)'")
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"-w, --websocket-port=[ws-port] 'Use another port for the \
|
"-w, --websocket-port=[ws-port] 'Use another port for the websocket connection \
|
||||||
websocket connection (livereload){n}(Defaults to 3001)'",
|
(livereload){n}(Defaults to 3001)'",
|
||||||
)
|
)
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"-i, --interface=[interface] 'Interface to listen on{n}(Defaults to localhost)'",
|
"-i, --interface=[interface] 'Interface to listen on{n}(Defaults to localhost)'",
|
||||||
)
|
)
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"-a, --address=[address] 'Address that the browser can reach the \
|
"-a, --address=[address] 'Address that the browser can reach the websocket server \
|
||||||
websocket server from{n}(Defaults to the interface address)'",
|
from{n}(Defaults to the interface address)'",
|
||||||
)
|
)
|
||||||
.arg_from_usage("-o, --open 'Open the book server in a web browser'")
|
.arg_from_usage("-o, --open 'Open the book server in a web browser'")
|
||||||
}
|
}
|
||||||
|
@ -51,10 +43,6 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let mut book = MDBook::load(&book_dir)?;
|
let mut book = MDBook::load(&book_dir)?;
|
||||||
|
|
||||||
if let Some(dest_dir) = args.value_of("dest-dir") {
|
|
||||||
book.config.build.build_dir = PathBuf::from(dest_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
let port = args.value_of("port").unwrap_or("3000");
|
let port = args.value_of("port").unwrap_or("3000");
|
||||||
let ws_port = args.value_of("websocket-port").unwrap_or("3001");
|
let ws_port = args.value_of("websocket-port").unwrap_or("3001");
|
||||||
let interface = args.value_of("interface").unwrap_or("localhost");
|
let interface = args.value_of("interface").unwrap_or("localhost");
|
||||||
|
@ -80,18 +68,19 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
}}
|
}}
|
||||||
</script>
|
</script>
|
||||||
"#,
|
"#,
|
||||||
public_address,
|
public_address, ws_port, RELOAD_COMMAND
|
||||||
ws_port,
|
|
||||||
RELOAD_COMMAND
|
|
||||||
));
|
));
|
||||||
|
|
||||||
book.build()?;
|
book.build()?;
|
||||||
|
|
||||||
let mut chain = Chain::new(staticfile::Static::new(book.get_destination()));
|
let mut chain = Chain::new(staticfile::Static::new(book.get_destination()));
|
||||||
chain.link_after(ErrorRecover);
|
chain.link_after(ErrorRecover);
|
||||||
let _iron = Iron::new(chain).http(&*address).unwrap();
|
let _iron = Iron::new(chain)
|
||||||
|
.http(&*address)
|
||||||
|
.chain_err(|| "Unable to launch the server")?;
|
||||||
|
|
||||||
let ws_server = ws::WebSocket::new(|_| |_| Ok(())).unwrap();
|
let ws_server =
|
||||||
|
ws::WebSocket::new(|_| |_| Ok(())).chain_err(|| "Unable to start the websocket")?;
|
||||||
|
|
||||||
let broadcaster = ws_server.broadcaster();
|
let broadcaster = ws_server.broadcaster();
|
||||||
|
|
||||||
|
@ -107,9 +96,9 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
watch::trigger_on_change(&mut book, move |path, book| {
|
watch::trigger_on_change(&mut book, move |path, book_dir| {
|
||||||
println!("File changed: {:?}\nBuilding book...\n", path);
|
println!("File changed: {:?}\nBuilding book...\n", path);
|
||||||
match book.build() {
|
match MDBook::load(&book_dir).and_then(|mut b| b.build()) {
|
||||||
Err(e) => println!("Error while building: {:?}", e),
|
Err(e) => println!("Error while building: {:?}", e),
|
||||||
_ => broadcaster.send(RELOAD_COMMAND).unwrap(),
|
_ => broadcaster.send(RELOAD_COMMAND).unwrap(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
extern crate notify;
|
extern crate notify;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use self::notify::Watcher;
|
use self::notify::Watcher;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
@ -15,12 +15,7 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
.about("Watch the files for changes")
|
.about("Watch the files for changes")
|
||||||
.arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
|
.arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"-d, --dest-dir=[dest-dir] 'The output directory for \
|
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
|
||||||
your book{n}(Defaults to ./book when omitted)'",
|
|
||||||
)
|
|
||||||
.arg_from_usage(
|
|
||||||
"[dir] 'A directory for your book{n}(Defaults to \
|
|
||||||
Current Directory when omitted)'",
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,30 +24,28 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let mut book = MDBook::load(&book_dir)?;
|
let mut book = MDBook::load(&book_dir)?;
|
||||||
|
|
||||||
if let Some(dest_dir) = args.value_of("dest-dir") {
|
|
||||||
book.config.build.build_dir = PathBuf::from(dest_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.is_present("open") {
|
if args.is_present("open") {
|
||||||
book.build()?;
|
book.build()?;
|
||||||
open(book.get_destination().join("index.html"));
|
open(book.get_destination().join("index.html"));
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger_on_change(&mut book, |path, book| {
|
trigger_on_change(&book, |path, book_dir| {
|
||||||
println!("File changed: {:?}\nBuilding book...\n", path);
|
println!("File changed: {:?}\nBuilding book...\n", path);
|
||||||
if let Err(e) = book.build() {
|
let result = MDBook::load(&book_dir).and_then(|mut b| b.build());
|
||||||
println!("Error while building: {:?}", e);
|
|
||||||
|
if let Err(e) = result {
|
||||||
|
println!("Error while building: {}", e);
|
||||||
}
|
}
|
||||||
println!("");
|
println!();
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calls the closure when a book source file is changed. This is blocking!
|
/// Calls the closure when a book source file is changed, blocking indefinitely.
|
||||||
pub fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
|
pub fn trigger_on_change<F>(book: &MDBook, closure: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Path, &mut MDBook) -> (),
|
F: Fn(&Path, &Path),
|
||||||
{
|
{
|
||||||
use self::notify::RecursiveMode::*;
|
use self::notify::RecursiveMode::*;
|
||||||
use self::notify::DebouncedEvent::*;
|
use self::notify::DebouncedEvent::*;
|
||||||
|
@ -64,48 +57,29 @@ where
|
||||||
Ok(w) => w,
|
Ok(w) => w,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
||||||
::std::process::exit(0)
|
::std::process::exit(1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the source directory to the watcher
|
// Add the source directory to the watcher
|
||||||
if let Err(e) = watcher.watch(book.source_dir(), Recursive) {
|
if let Err(e) = watcher.watch(book.source_dir(), Recursive) {
|
||||||
println!("Error while watching {:?}:\n {:?}", book.source_dir(), e);
|
println!("Error while watching {:?}:\n {:?}", book.source_dir(), e);
|
||||||
::std::process::exit(0);
|
::std::process::exit(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the theme directory to the watcher
|
let _ = watcher.watch(book.theme_dir(), Recursive);
|
||||||
watcher.watch(book.theme_dir(), Recursive)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
// Add the book.{json,toml} file to the watcher if it exists, because it's not
|
// Add the book.toml file to the watcher if it exists
|
||||||
// located in the source directory
|
let _ = watcher.watch(book.root.join("book.toml"), NonRecursive);
|
||||||
if watcher.watch(book.root.join("book.json"), NonRecursive)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
// do nothing if book.json is not found
|
|
||||||
}
|
|
||||||
if watcher.watch(book.root.join("book.toml"), NonRecursive)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
// do nothing if book.toml is not found
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("\nListening for changes...\n");
|
println!("\nListening for changes...\n");
|
||||||
|
|
||||||
loop {
|
for event in rx.recv() {
|
||||||
match rx.recv() {
|
|
||||||
Ok(event) => {
|
|
||||||
match event {
|
match event {
|
||||||
Create(path) | Write(path) | Remove(path) | Rename(_, path) => {
|
Create(path) | Write(path) | Remove(path) | Rename(_, path) => {
|
||||||
closure(&path, book);
|
closure(&path, &book.root);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
|
||||||
println!("An error occured: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue