From ed930df81f42735cc41024133a5d87d50435fce9 Mon Sep 17 00:00:00 2001 From: wuyangfan <1102042793@qq.com> Date: Sun, 17 May 2026 12:42:00 +0800 Subject: [PATCH] fix(html): reserve toc.md and document toc.html Reject src/toc.md chapters so they cannot overwrite generated toc.html. Document toc.html and the reserved toc.md name. Fixes #3090. Co-authored-by: Cursor --- .../src/html_handlebars/hbs_renderer.rs | 4 ++-- guide/src/format/configuration/renderers.md | 20 +++++++++++++++++++ tests/testsuite/build.rs | 13 ++++++++++++ .../build/no_reserved_toc_filename/book.toml | 2 ++ .../no_reserved_toc_filename/src/SUMMARY.md | 3 +++ .../build/no_reserved_toc_filename/src/toc.md | 3 +++ 6 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 tests/testsuite/build/no_reserved_toc_filename/book.toml create mode 100644 tests/testsuite/build/no_reserved_toc_filename/src/SUMMARY.md create mode 100644 tests/testsuite/build/no_reserved_toc_filename/src/toc.md diff --git a/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs b/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs index 8edac3cace..a914d99f64 100644 --- a/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs +++ b/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs @@ -38,8 +38,8 @@ impl HtmlHandlebars { let ch = chapter_tree.chapter; let path = ch.path.as_ref().unwrap(); - // "print.html" is used for the print page. - if path == Path::new("print.md") { + // "print.html" and "toc.html" are used for special output pages. + if path == Path::new("print.md") || path == Path::new("toc.md") { bail!("{} is reserved for internal use", path.display()); }; diff --git a/guide/src/format/configuration/renderers.md b/guide/src/format/configuration/renderers.md index 22dfd425fb..c7c1ab45ce 100644 --- a/guide/src/format/configuration/renderers.md +++ b/guide/src/format/configuration/renderers.md @@ -173,6 +173,16 @@ The following configuration options are available: [custom domain]: https://docs.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site +#### `toc.html` and `toc.md` + +mdBook writes **`toc.html`** to the book output directory. +It contains the table of contents for environments that do not run JavaScript (including some crawlers). + +**`toc.md` is reserved** for internal use. +Do not create `src/toc.md` or add it to `SUMMARY.md`. +A chapter named `toc.md` would be rendered to `toc.html` and overwrite the generated table of contents. +If you try, the build fails with the error `toc.md is reserved for internal use`. + ### `[output.html.print]` The `[output.html.print]` table provides options for controlling the printable output. @@ -188,6 +198,16 @@ page-break = true # insert page-break after each chapter rendered. Defaults to `true`. - **page-break:** Insert page breaks between chapters. Defaults to `true`. +#### `print.html` and `print.md` + +When print support is enabled, mdBook writes **`print.html`** to the book output directory. +This file contains the entire book on a single page. +The print icon in the menu bar (see [Reading a book](../../guide/reading.md)) opens this file for printing. + +**`print.md` is reserved** for internal use. +Do not create `src/print.md` or add it to `SUMMARY.md`. +If you do, the build fails with the error `print.md is reserved for internal use`. + ### `[output.html.fold]` The `[output.html.fold]` table provides options for controlling folding of the chapter listing in the navigation sidebar. diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index 889a9e65ea..a1f3e6f0f9 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -55,6 +55,19 @@ ERROR Rendering failed }); } +#[test] +fn no_reserved_toc_filename() { + BookTest::from_dir("build/no_reserved_toc_filename").run("build", |cmd| { + cmd.expect_failure().expect_stderr(str![[r#" + INFO Book building has started + INFO Running the html backend +ERROR Rendering failed +[TAB]Caused by: toc.md is reserved for internal use + +"#]]); + }); +} + // Build without book.toml should be OK. #[test] fn book_toml_isnt_required() { diff --git a/tests/testsuite/build/no_reserved_toc_filename/book.toml b/tests/testsuite/build/no_reserved_toc_filename/book.toml new file mode 100644 index 0000000000..8975db626e --- /dev/null +++ b/tests/testsuite/build/no_reserved_toc_filename/book.toml @@ -0,0 +1,2 @@ +[book] +title = "no_reserved_toc_filename" diff --git a/tests/testsuite/build/no_reserved_toc_filename/src/SUMMARY.md b/tests/testsuite/build/no_reserved_toc_filename/src/SUMMARY.md new file mode 100644 index 0000000000..0f3901213b --- /dev/null +++ b/tests/testsuite/build/no_reserved_toc_filename/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +- [TOC](toc.md) diff --git a/tests/testsuite/build/no_reserved_toc_filename/src/toc.md b/tests/testsuite/build/no_reserved_toc_filename/src/toc.md new file mode 100644 index 0000000000..e6700b110e --- /dev/null +++ b/tests/testsuite/build/no_reserved_toc_filename/src/toc.md @@ -0,0 +1,3 @@ +# TOC + +This should not be allowed.