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
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# pkgdown (development version)

* `build_home()` now supports a `.pkgdownignore` file to exclude markdown files
from being rendered to HTML. The file can be placed in the package root,
`pkgdown/`, or `_pkgdown/` directories (@kyleGrealis, #2959).

# pkgdown 2.2.0

* Make `build_llm_docs()` more robust to the use of old Pandoc (@nanxstats, @galachad, #2952, #2954)
Expand Down
40 changes: 40 additions & 0 deletions R/build-home-md.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ package_mds <- function(path, in_dev = FALSE) {

# Remove files handled elsewhere
handled <- c("README.md", "LICENSE.md", "LICENCE.md", "NEWS.md")

# Append user-defined ignores from .pkgdownignore
user_ignores <- read_pkgdownignore(path)
if (length(user_ignores) > 0) {
cli::cli_inform("Ignoring: {user_ignores}")
}
handled <- c(handled, user_ignores)
# handled <- c(handled)
mds <- mds[!path_file(mds) %in% handled]

# Do not build 404 page if in-dev
Expand Down Expand Up @@ -59,3 +67,35 @@ render_md <- function(pkg, filename) {

invisible()
}

#' Read .pkgdownignore file
#'
#' Searches for .pkgdownignore in standard pkgdown config locations.
#' Returns filenames to exclude (one per line, with comments ignored).
#'
#' @param path Path to the package root
#' @return Character vector of filenames to exclude
#' @noRd
read_pkgdownignore <- function(path) {
# Check standard pkgdown config locations:
candidates <- c(
path(path, ".pkgdownignore"),
path(path, "_pkgdown", ".pkgdownignore"),
path(path, "pkgdown", ".pkgdownignore")
)

# Read output from all ignore files
ignore_paths <- candidates[file_exists(candidates)]
if (length(ignore_paths) == 0) {
return(character())
}

# Combine lines from all ignore files
lines <- unlist(lapply(ignore_paths, readLines))

# Remove comments & empty lines and trim whitespace
lines <- lines[!grepl("^\\s*#", lines)]
lines <- lines[nzchar(trimws(lines))]

unique(trimws(lines))
}
162 changes: 162 additions & 0 deletions tests/testthat/test-pkgdownignore.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
test_that("read_pkgdownignore returns empty when no file exists", {
pkg <- local_pkgdown_site()
expect_equal(read_pkgdownignore(pkg$src_path), character())
})

test_that("read_pkgdownignore reads from package root", {
pkg <- local_pkgdown_site()
write_lines(c("CLAUDE.md", "AGENTS.md"), path(pkg$src_path, ".pkgdownignore"))

result <- read_pkgdownignore(pkg$src_path)
expect_equal(result, c("CLAUDE.md", "AGENTS.md"))
})

test_that("read_pkgdownignore reads from pkgdown/ directory", {
pkg <- local_pkgdown_site()
dir_create(path(pkg$src_path, "pkgdown"))
write_lines("INTERNAL.md", path(pkg$src_path, "pkgdown", ".pkgdownignore"))

result <- read_pkgdownignore(pkg$src_path)
expect_equal(result, "INTERNAL.md")
})

test_that("read_pkgdownignore reads from _pkgdown/ directory", {
pkg <- local_pkgdown_site()
dir_create(path(pkg$src_path, "_pkgdown"))
write_lines("NOTES.md", path(pkg$src_path, "_pkgdown", ".pkgdownignore"))

result <- read_pkgdownignore(pkg$src_path)
expect_equal(result, "NOTES.md")
})

test_that("read_pkgdownignore combines files from multiple locations", {
pkg <- local_pkgdown_site()

# Root

write_lines("CLAUDE.md", path(pkg$src_path, ".pkgdownignore"))

# pkgdown/
dir_create(path(pkg$src_path, "pkgdown"))
write_lines("AGENTS.md", path(pkg$src_path, "pkgdown", ".pkgdownignore"))

result <- read_pkgdownignore(pkg$src_path)
expect_setequal(result, c("CLAUDE.md", "AGENTS.md"))
})

test_that("read_pkgdownignore ignores comments and empty lines", {
pkg <- local_pkgdown_site()
write_lines(
c(
"# This is a comment",
"CLAUDE.md",
"",
" # Indented comment",
"AGENTS.md",
" ",
"INTERNAL.md"
),
path(pkg$src_path, ".pkgdownignore")
)

result <- read_pkgdownignore(pkg$src_path)
expect_equal(result, c("CLAUDE.md", "AGENTS.md", "INTERNAL.md"))
})

test_that("read_pkgdownignore trims whitespace", {
pkg <- local_pkgdown_site()
write_lines(
c(" CLAUDE.md ", "\tAGENTS.md\t"),
path(pkg$src_path, ".pkgdownignore")
)

result <- read_pkgdownignore(pkg$src_path)
expect_equal(result, c("CLAUDE.md", "AGENTS.md"))
})

test_that("read_pkgdownignore deduplicates entries", {
pkg <- local_pkgdown_site()

# Same file in root and pkgdown/

write_lines("CLAUDE.md", path(pkg$src_path, ".pkgdownignore"))
dir_create(path(pkg$src_path, "pkgdown"))
write_lines("CLAUDE.md", path(pkg$src_path, "pkgdown", ".pkgdownignore"))

result <- read_pkgdownignore(pkg$src_path)
expect_equal(result, "CLAUDE.md")
})

test_that("package_mds excludes files listed in .pkgdownignore", {
pkg <- local_pkgdown_site()

# Create test markdown files
write_lines("# CLAUDE", path(pkg$src_path, "CLAUDE.md"))
write_lines("# AGENTS", path(pkg$src_path, "AGENTS.md"))
write_lines("# ROADMAP", path(pkg$src_path, "ROADMAP.md"))

# Create .pkgdownignore

write_lines(c("CLAUDE.md", "AGENTS.md"), path(pkg$src_path, ".pkgdownignore"))

result <- package_mds(pkg$src_path)
result_files <- path_file(result)

expect_false("CLAUDE.md" %in% result_files)
expect_false("AGENTS.md" %in% result_files)
expect_true("ROADMAP.md" %in% result_files)
})

test_that("package_mds excludes .github files listed in .pkgdownignore", {
pkg <- local_pkgdown_site()

# Create .github directory with markdown files
dir_create(path(pkg$src_path, ".github"))
write_lines("# Internal", path(pkg$src_path, ".github", "INTERNAL.md"))
write_lines(
"# Contributing",
path(pkg$src_path, ".github", "CONTRIBUTING.md")
)

# Ignore the internal one
write_lines("INTERNAL.md", path(pkg$src_path, ".pkgdownignore"))

result <- package_mds(pkg$src_path)
result_files <- path_file(result)

expect_false("INTERNAL.md" %in% result_files)
expect_true("CONTRIBUTING.md" %in% result_files)
})

test_that("package_mds works when .pkgdownignore is empty", {
pkg <- local_pkgdown_site()

# Create test file
write_lines("# ROADMAP", path(pkg$src_path, "ROADMAP.md"))

# Empty ignore file
file_create(path(pkg$src_path, ".pkgdownignore"))

result <- package_mds(pkg$src_path)
result_files <- path_file(result)

expect_true("ROADMAP.md" %in% result_files)
})

test_that("package_mds works when .pkgdownignore has only comments", {
pkg <- local_pkgdown_site()

# Create test file
write_lines("# ROADMAP", path(pkg$src_path, "ROADMAP.md"))

# Ignore file with only comments
write_lines(
c("# comment 1", "# comment 2"),
path(pkg$src_path, ".pkgdownignore")
)

result <- package_mds(pkg$src_path)
result_files <- path_file(result)

expect_true("ROADMAP.md" %in% result_files)
})
Loading