Redirect to translation index page in serve command

This commit is contained in:
Ruin0x11 2020-08-28 01:36:22 -07:00
parent 8869c2cf06
commit 85ab4d39cd
4 changed files with 100 additions and 72 deletions

1
Cargo.lock generated
View File

@ -789,6 +789,7 @@ dependencies = [
"futures-util", "futures-util",
"gitignore", "gitignore",
"handlebars", "handlebars",
"http",
"lazy_static", "lazy_static",
"log", "log",
"memchr", "memchr",

View File

@ -21,6 +21,7 @@ chrono = "0.4"
clap = "2.24" clap = "2.24"
env_logger = "0.7.1" env_logger = "0.7.1"
handlebars = "4.0" handlebars = "4.0"
http = "0.2.4"
lazy_static = "1.0" lazy_static = "1.0"
log = "0.4" log = "0.4"
memchr = "2.0" memchr = "2.0"

View File

@ -4,6 +4,7 @@ use crate::{get_book_dir, get_build_opts, open};
use clap::{App, Arg, ArgMatches, SubCommand}; use clap::{App, Arg, ArgMatches, SubCommand};
use futures_util::sink::SinkExt; use futures_util::sink::SinkExt;
use futures_util::StreamExt; use futures_util::StreamExt;
use http::Uri;
use mdbook::errors::*; use mdbook::errors::*;
use mdbook::utils; use mdbook::utils;
use mdbook::utils::fs::get_404_output_file; use mdbook::utils::fs::get_404_output_file;
@ -60,7 +61,7 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
pub fn execute(args: &ArgMatches) -> Result<()> { pub fn execute(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args); let book_dir = get_book_dir(args);
let build_opts = get_build_opts(args); let build_opts = get_build_opts(args);
let mut book = MDBook::load_with_build_opts(&book_dir, build_opts)?; let mut book = MDBook::load_with_build_opts(&book_dir, build_opts.clone())?;
let port = args.value_of("port").unwrap(); let port = args.value_of("port").unwrap();
let hostname = args.value_of("hostname").unwrap(); let hostname = args.value_of("hostname").unwrap();
@ -82,6 +83,18 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
update_config(&mut book); update_config(&mut book);
book.build()?; book.build()?;
let language: Option<String> = match build_opts.language_ident {
// index.html will be at the root directory.
Some(_) => None,
None => match book.config.language.default_language() {
// If book has translations, index.html will be under src/en/ or
// similar.
Some(lang_ident) => Some(lang_ident.clone()),
// If not, it will be at the root.
None => None,
}
};
let sockaddr: SocketAddr = address let sockaddr: SocketAddr = address
.to_socket_addrs()? .to_socket_addrs()?
.next() .next()
@ -100,7 +113,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
let reload_tx = tx.clone(); let reload_tx = tx.clone();
let thread_handle = std::thread::spawn(move || { let thread_handle = std::thread::spawn(move || {
serve(build_dir, sockaddr, reload_tx, &file_404); serve(build_dir, sockaddr, reload_tx, &file_404, language);
}); });
let serving_url = format!("http://{}", address); let serving_url = format!("http://{}", address);
@ -140,6 +153,7 @@ async fn serve(
address: SocketAddr, address: SocketAddr,
reload_tx: broadcast::Sender<Message>, reload_tx: broadcast::Sender<Message>,
file_404: &str, file_404: &str,
language: Option<String>,
) { ) {
// A warp Filter which captures `reload_tx` and provides an `rx` copy to // A warp Filter which captures `reload_tx` and provides an `rx` copy to
// receive reload messages. // receive reload messages.
@ -166,7 +180,6 @@ async fn serve(
// The fallback route for 404 errors // The fallback route for 404 errors
let fallback_route = warp::fs::file(build_dir.join(file_404)) let fallback_route = warp::fs::file(build_dir.join(file_404))
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND)); .map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));
let routes = livereload.or(book_route).or(fallback_route);
std::panic::set_hook(Box::new(move |panic_info| { std::panic::set_hook(Box::new(move |panic_info| {
// exit if serve panics // exit if serve panics
@ -174,5 +187,16 @@ async fn serve(
std::process::exit(1); std::process::exit(1);
})); }));
warp::serve(routes).run(address).await; if let Some(lang_ident) = language {
// Redirect root to the default translation directory, if serving a localized book.
// BUG: This can't be `/{lang_ident}`, or the static assets won't get loaded.
let index_for_language = format!("/{}/index.html", lang_ident).parse::<Uri>().unwrap();
let redirect_to_index = warp::path::end().map(move || warp::redirect(index_for_language.clone()));
let routes = livereload.or(redirect_to_index).or(book_route).or(fallback_route);
warp::serve(routes).run(address).await;
}
else {
let routes = livereload.or(book_route).or(fallback_route);
warp::serve(routes).run(address).await;
};
} }

View File

@ -428,81 +428,83 @@ function playground_text(playground) {
var languageToggleButton = document.getElementById('language-toggle'); var languageToggleButton = document.getElementById('language-toggle');
var languagePopup = document.getElementById('language-list'); var languagePopup = document.getElementById('language-list');
function showLanguages() { if (languageToggleButton !== null) {
languagePopup.style.display = 'block'; function showLanguages() {
languageToggleButton.setAttribute('aria-expanded', true); languagePopup.style.display = 'block';
} languageToggleButton.setAttribute('aria-expanded', true);
function hideLanguages() {
languagePopup.style.display = 'none';
languageToggleButton.setAttribute('aria-expanded', false);
languageToggleButton.focus();
}
function set_language(language) {
console.log("Set language " + language)
}
languageToggleButton.addEventListener('click', function () {
if (languagePopup.style.display === 'block') {
hideLanguages();
} else {
showLanguages();
} }
});
languagePopup.addEventListener('click', function (e) { function hideLanguages() {
var language = e.target.id || e.target.parentElement.id; languagePopup.style.display = 'none';
set_language(language); languageToggleButton.setAttribute('aria-expanded', false);
}); languageToggleButton.focus();
languagePopup.addEventListener('focusout', function(e) {
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
if (!!e.relatedTarget && !languageToggleButton.contains(e.relatedTarget) && !languagePopup.contains(e.relatedTarget)) {
hideLanguages();
} }
});
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 function set_language(language) {
document.addEventListener('click', function(e) { console.log("Set language " + language)
if (languagePopup.style.display === 'block' && !languageToggleButton.contains(e.target) && !languagePopup.contains(e.target)) {
hideLanguages();
} }
});
document.addEventListener('keydown', function (e) { languageToggleButton.addEventListener('click', function () {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } if (languagePopup.style.display === 'block') {
if (!languagePopup.contains(e.target)) { return; }
switch (e.key) {
case 'Escape':
e.preventDefault();
hideLanguages(); hideLanguages();
break; } else {
case 'ArrowUp': showLanguages();
e.preventDefault(); }
var li = document.activeElement.parentElement; });
if (li && li.previousElementSibling) {
li.previousElementSibling.querySelector('button').focus(); languagePopup.addEventListener('click', function (e) {
} var language = e.target.id || e.target.parentElement.id;
break; set_language(language);
case 'ArrowDown': });
e.preventDefault();
var li = document.activeElement.parentElement; languagePopup.addEventListener('focusout', function(e) {
if (li && li.nextElementSibling) { // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
li.nextElementSibling.querySelector('button').focus(); if (!!e.relatedTarget && !languageToggleButton.contains(e.relatedTarget) && !languagePopup.contains(e.relatedTarget)) {
} hideLanguages();
break; }
case 'Home': });
e.preventDefault();
languagePopup.querySelector('li:first-child button').focus(); // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
break; document.addEventListener('click', function(e) {
case 'End': if (languagePopup.style.display === 'block' && !languageToggleButton.contains(e.target) && !languagePopup.contains(e.target)) {
e.preventDefault(); hideLanguages();
languagePopup.querySelector('li:last-child button').focus(); }
break; });
}
}); document.addEventListener('keydown', function (e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
if (!languagePopup.contains(e.target)) { return; }
switch (e.key) {
case 'Escape':
e.preventDefault();
hideLanguages();
break;
case 'ArrowUp':
e.preventDefault();
var li = document.activeElement.parentElement;
if (li && li.previousElementSibling) {
li.previousElementSibling.querySelector('button').focus();
}
break;
case 'ArrowDown':
e.preventDefault();
var li = document.activeElement.parentElement;
if (li && li.nextElementSibling) {
li.nextElementSibling.querySelector('button').focus();
}
break;
case 'Home':
e.preventDefault();
languagePopup.querySelector('li:first-child button').focus();
break;
case 'End':
e.preventDefault();
languagePopup.querySelector('li:last-child button').focus();
break;
}
});
}
})(); })();
(function sidebar() { (function sidebar() {