Skip to content

Commit

Permalink
Fixed bug with index page links.
Browse files Browse the repository at this point in the history
If there is no index page in the source directory,
mdbook uses the first chapter as the index page.
When doing so, mdbook doesn't render the page any differently.
This causes relative links on the page to break,
if the first chapter is within a sub-directory.

To fix this, a redirect to the first chapter is used
instead of the copy of the page.
  • Loading branch information
HU90m committed Jan 25, 2024
1 parent 090eba0 commit c667905
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 30 deletions.
52 changes: 26 additions & 26 deletions src/renderer/html_handlebars/hbs_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ impl HtmlHandlebars {
item: &BookItem,
mut ctx: RenderItemContext<'_>,
print_content: &mut String,
) -> Result<()> {
) -> Result<Option<PathBuf>> {
// FIXME: This should be made DRY-er and rely less on mutable state

let (ch, path) = match item {
BookItem::Chapter(ch) if !ch.is_draft_chapter() => (ch, ch.path.as_ref().unwrap()),
_ => return Ok(()),
_ => return Ok(None),
};

if let Some(ref edit_url_template) = ctx.html_config.edit_url_template {
Expand All @@ -58,7 +58,7 @@ impl HtmlHandlebars {

let fixed_content =
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path));
if !ctx.is_index && ctx.html_config.print.page_break {
if !ctx.is_first_chapter && ctx.html_config.print.page_break {
// Add page break between chapters
// See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before
// Add both two CSS properties because of the compatibility issue
Expand Down Expand Up @@ -120,22 +120,7 @@ impl HtmlHandlebars {
debug!("Creating {}", filepath.display());
utils::fs::write_file(&ctx.destination, &filepath, rendered.as_bytes())?;

if ctx.is_index {
ctx.data.insert("path".to_owned(), json!("index.md"));
ctx.data.insert("path_to_root".to_owned(), json!(""));
ctx.data.insert("is_index".to_owned(), json!(true));
let rendered_index = ctx.handlebars.render("index", &ctx.data)?;
let rendered_index = self.post_process(
rendered_index,
&ctx.html_config.playground,
&ctx.html_config.code,
ctx.edition,
);
debug!("Creating index.html from {}", ctx_path);
utils::fs::write_file(&ctx.destination, "index.html", rendered_index.as_bytes())?;
}

Ok(())
Ok(Some(filepath))
}

fn render_404(
Expand Down Expand Up @@ -484,7 +469,7 @@ impl Renderer for HtmlHandlebars {

fn render(&self, ctx: &RenderContext) -> Result<()> {
let book_config = &ctx.config.book;
let html_config = ctx.config.html_config().unwrap_or_default();
let mut html_config = ctx.config.html_config().unwrap_or_default();
let src_dir = ctx.root.join(&ctx.config.book.src);
let destination = &ctx.destination;
let book = &ctx.book;
Expand Down Expand Up @@ -535,21 +520,36 @@ impl Renderer for HtmlHandlebars {
fs::create_dir_all(destination)
.with_context(|| "Unexpected error when constructing destination path")?;

let mut is_index = true;
let mut seen_index = false;
let mut first_non_draft = None;
for item in book.iter() {
let ctx = RenderItemContext {
handlebars: &handlebars,
destination: destination.to_path_buf(),
data: data.clone(),
is_index,
is_first_chapter: first_non_draft.is_none(),
book_config: book_config.clone(),
html_config: html_config.clone(),
edition: ctx.config.rust.edition,
chapter_titles: &ctx.chapter_titles,
};
self.render_item(item, ctx, &mut print_content)?;
// Only the first non-draft chapter item should be treated as the "index"
is_index &= !matches!(item, BookItem::Chapter(ch) if !ch.is_draft_chapter());
if let Some(rendered_file) = self.render_item(item, ctx, &mut print_content)? {
seen_index |= rendered_file == Path::new("index.html");
if first_non_draft.is_none() {
first_non_draft = Some(rendered_file);
}
}
}
if !seen_index {
if let Some(first_file) = first_non_draft {
let redirect_to = first_file
.to_str()
.with_context(|| "Could not convert path to str")?
.to_string();
html_config
.redirect
.insert(String::from("/index.html"), redirect_to);
}
}

// Render 404 page
Expand Down Expand Up @@ -1053,7 +1053,7 @@ struct RenderItemContext<'a> {
handlebars: &'a Handlebars<'a>,
destination: PathBuf,
data: serde_json::Map<String, serde_json::Value>,
is_index: bool,
is_first_chapter: bool,
book_config: BookConfig,
html_config: HtmlConfig,
edition: Option<RustEdition>,
Expand Down
8 changes: 4 additions & 4 deletions tests/rendered_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ fn by_default_mdbook_use_index_preprocessor_to_convert_readme_to_index() {
}

#[test]
fn first_chapter_is_copied_as_index_even_if_not_first_elem() {
fn redirect_to_first_chapter_if_no_index() {
let temp = DummyBook::new().build().unwrap();
let mut cfg = Config::default();
cfg.set("book.src", "index_html_test")
Expand All @@ -476,9 +476,9 @@ fn first_chapter_is_copied_as_index_even_if_not_first_elem() {
md.build().unwrap();

let root = temp.path().join("book");
let chapter = fs::read_to_string(root.join("chapter_1.html")).expect("read chapter 1");
let index = fs::read_to_string(root.join("index.html")).expect("read index");
pretty_assertions::assert_eq!(chapter, index);

assert!(index.contains(r#"<meta http-equiv="refresh" content="0; URL=chapter_1.html">"#))
}

#[test]
Expand Down Expand Up @@ -874,7 +874,7 @@ fn custom_fonts() {
actual
};
let has_fonts_css = |path: &Path| -> bool {
let contents = fs::read_to_string(path.join("book/index.html")).unwrap();
let contents = fs::read_to_string(path.join("book/chapter_1.html")).unwrap();
contents.contains("fonts/fonts.css")
};

Expand Down

0 comments on commit c667905

Please sign in to comment.