Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,17 @@ rust-version.workspace = true

[dependencies]
anyhow.workspace = true
clap.workspace = true
clap_complete.workspace = true
clap.workspace = true
ignore.workspace = true
mdbook-core.workspace = true
mdbook-driver.workspace = true
mdbook-html.workspace = true
opener.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
tracing.workspace = true

# Watch feature
ignore = { workspace = true, optional = true }
notify = { workspace = true, optional = true }
notify-debouncer-mini = { workspace = true, optional = true }
pathdiff = { workspace = true, optional = true }
Expand All @@ -125,7 +125,7 @@ walkdir.workspace = true

[features]
default = ["watch", "serve", "search"]
watch = ["dep:notify", "dep:notify-debouncer-mini", "dep:ignore", "dep:pathdiff", "dep:walkdir"]
watch = ["dep:notify", "dep:notify-debouncer-mini", "dep:pathdiff", "dep:walkdir"]
serve = ["dep:futures-util", "dep:tokio", "dep:axum", "dep:tower-http"]
search = ["mdbook-html/search"]

Expand Down
1 change: 1 addition & 0 deletions crates/mdbook-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rust-version.workspace = true

[dependencies]
anyhow.workspace = true
ignore.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
Expand Down
46 changes: 37 additions & 9 deletions crates/mdbook-core/src/utils/fs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Filesystem utilities and helpers.

use anyhow::{Context, Result};
use ignore::gitignore::{Gitignore, GitignoreBuilder};
use std::fs;
use std::path::{Component, Path, PathBuf};
use tracing::debug;
Expand Down Expand Up @@ -96,12 +97,30 @@ pub fn copy_files_except_ext(
recursive: bool,
avoid_dir: Option<&PathBuf>,
ext_blacklist: &[&str],
) -> Result<()> {
let mut builder = GitignoreBuilder::new(from);
for ext in ext_blacklist {
builder.add_line(None, &format!("*.{ext}"))?;
}
let ignore = builder.build()?;

copy_files_except_ignored(from, to, recursive, avoid_dir, Some(&ignore))
}

/// Copies all files of a directory to another one except the files that are
/// ignored by the passed [`Gitignore`]
pub fn copy_files_except_ignored(
from: &Path,
to: &Path,
recursive: bool,
avoid_dir: Option<&PathBuf>,
ignore: Option<&Gitignore>,
) -> Result<()> {
debug!(
"Copying all files from {} to {} (blacklist: {:?}), avoiding {:?}",
from.display(),
to.display(),
ext_blacklist,
ignore,
avoid_dir
);

Expand All @@ -119,6 +138,14 @@ pub fn copy_files_except_ext(
let entry_file_name = entry.file_name().unwrap();
let target_file_path = to.join(entry_file_name);

// Check if it is in the blacklist
if let Some(ignore) = ignore {
let path = entry.as_path();
if ignore.matched(path, path.is_dir()).is_ignore() {
continue;
}
}

// If the entry is a dir and the recursive option is enabled, call itself
if metadata.is_dir() && recursive {
if entry == to.as_os_str() {
Expand All @@ -131,19 +158,20 @@ pub fn copy_files_except_ext(
}
}

if let Some(ignore) = ignore {
let path = entry.as_path();
if ignore.matched(path, path.is_dir()).is_ignore() {
continue;
}
}

// check if output dir already exists
if !target_file_path.exists() {
fs::create_dir(&target_file_path)?;
}

copy_files_except_ext(&entry, &target_file_path, true, avoid_dir, ext_blacklist)?;
copy_files_except_ignored(&entry, &target_file_path, true, avoid_dir, ignore)?;
} else if metadata.is_file() {
// Check if it is in the blacklist
if let Some(ext) = entry.extension() {
if ext_blacklist.contains(&ext.to_str().unwrap()) {
continue;
}
}
debug!("Copying {entry:?} to {target_file_path:?}");
copy(&entry, &target_file_path)?;
}
Expand Down Expand Up @@ -247,7 +275,7 @@ mod tests {
if let Err(e) =
copy_files_except_ext(tmp.path(), &tmp.path().join("output"), true, None, &["md"])
{
panic!("Error while executing the function:\n{e:?}");
panic!("Error while executing the function:\n{:?}", e);
}

// Check if the correct files where created
Expand Down
1 change: 1 addition & 0 deletions crates/mdbook-html/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ font-awesome-as-a-crate.workspace = true
handlebars.workspace = true
hex.workspace = true
html5ever.workspace = true
ignore.workspace = true
indexmap.workspace = true
mdbook-core.workspace = true
mdbook-markdown.workspace = true
Expand Down
19 changes: 18 additions & 1 deletion crates/mdbook-html/src/html_handlebars/hbs_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::theme::Theme;
use crate::utils::ToUrlPath;
use anyhow::{Context, Result, bail};
use handlebars::Handlebars;
use ignore::gitignore::GitignoreBuilder;
use mdbook_core::book::{Book, BookItem, Chapter};
use mdbook_core::config::{BookConfig, Config, HtmlConfig};
use mdbook_core::utils::fs;
Expand Down Expand Up @@ -444,7 +445,23 @@ impl Renderer for HtmlHandlebars {
.context("Unable to emit redirects")?;

// Copy all remaining files, avoid a recursive copy from/to the book build dir
fs::copy_files_except_ext(&src_dir, destination, true, Some(&build_dir), &["md"])?;
let mut builder = GitignoreBuilder::new(&src_dir);
let mdbook_ignore = src_dir.join(".mdbookignore");
if mdbook_ignore.exists() {
if let Some(err) = builder.add(mdbook_ignore) {
warn!("Unable to load '.mdbookignore' file: {}", err);
}
}
builder.add_line(None, "*.md")?;
let ignore = builder.build()?;

fs::copy_files_except_ignored(
&src_dir,
destination,
true,
Some(&build_dir),
Some(&ignore),
)?;

info!("HTML book written to `{}`", destination.display());

Expand Down
14 changes: 13 additions & 1 deletion guide/src/format/configuration/renderers.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,19 @@ This will generate an HTML page which will automatically redirect to the given l

When fragment redirects are specified, the page must use JavaScript to redirect to the correct location. This is useful if you rename or move a section header. Fragment redirects work with existing pages and deleted pages.

## Markdown renderer
### `.mdbookignore`

You can use a `.mdbookignore` file to exclude files from the build process.
The file is placed in the `src` directory of your book and has the same format as
[`.gitignore`](https://git-scm.com/docs/gitignore) files.

For example:
```
*.rs
/target/
```

## Markdown Renderer

The Markdown renderer will run preprocessors and then output the resulting
Markdown. This is mostly useful for debugging preprocessors, especially in
Expand Down
1 change: 1 addition & 0 deletions tests/testsuite/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod includes;
mod index;
mod init;
mod markdown;
mod mdbookignore;
mod playground;
mod preprocessor;
mod print;
Expand Down
13 changes: 13 additions & 0 deletions tests/testsuite/mdbookignore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use crate::prelude::BookTest;

// Simple smoke test that mdbookignore works.
#[test]
fn ignore_file_is_respected() {
let mut test = BookTest::from_dir("mdbookignore/simple");
test.run("build", |_| ());

assert!(test.dir.join("book/index.html").exists());
assert!(test.dir.join("book/normal_file").exists());
assert!(!test.dir.join("book/ignored_file").exists());
assert!(!test.dir.join("book/.mdbookignore").exists());
}
3 changes: 3 additions & 0 deletions tests/testsuite/mdbookignore/simple/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Basic book

This GUI test book is the default book with a single chapter.
2 changes: 2 additions & 0 deletions tests/testsuite/mdbookignore/simple/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[book]
title = "mdbookignore"
2 changes: 2 additions & 0 deletions tests/testsuite/mdbookignore/simple/src/.mdbookignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.mdbookignore
ignored_file
3 changes: 3 additions & 0 deletions tests/testsuite/mdbookignore/simple/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Summary

- [Chapter 1](./chapter_1.md)
1 change: 1 addition & 0 deletions tests/testsuite/mdbookignore/simple/src/chapter_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Chapter 1
1 change: 1 addition & 0 deletions tests/testsuite/mdbookignore/simple/src/ignored_file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file should not be copied!
1 change: 1 addition & 0 deletions tests/testsuite/mdbookignore/simple/src/normal_file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file should be copied!