diff --git a/fixtures/TEST_QUARTO.qmd b/fixtures/TEST_QUARTO.qmd new file mode 100644 index 0000000000..7c4cfe08ca --- /dev/null +++ b/fixtures/TEST_QUARTO.qmd @@ -0,0 +1,11 @@ +--- +title: Sample +--- + +Real link: . + +```{.bash} +curl -O https://cdn.posit.co/r/rhel-10/pkgs/R-${R_VERSION}-1-1.$(arch).rpm +``` + +Inline `https://inline.example.com/code` should also be excluded. diff --git a/fixtures/TEST_RMARKDOWN.Rmd b/fixtures/TEST_RMARKDOWN.Rmd new file mode 100644 index 0000000000..cfc1ed0c20 --- /dev/null +++ b/fixtures/TEST_RMARKDOWN.Rmd @@ -0,0 +1,13 @@ +--- +title: Sample R Markdown +output: html_document +--- + +Real link: . + +```{r setup, include=FALSE} +# This URL should be ignored because it's inside a fenced code block +download.file("https://cdn.posit.co/r/rhel-10/pkgs/R-${R_VERSION}-1-1.rpm") +``` + +Inline `https://inline.example.com/rmd` should also be excluded. diff --git a/lychee-bin/src/config/mod.rs b/lychee-bin/src/config/mod.rs index 652c3f5faf..86b1979677 100644 --- a/lychee-bin/src/config/mod.rs +++ b/lychee-bin/src/config/mod.rs @@ -224,7 +224,7 @@ pub(crate) struct Config { /// This is useful when the default extensions are not enough and you don't /// want to provide a long list of inputs (e.g. file1.html, file2.md, etc.) /// - /// [default: md,mkd,mdx,mdown,mdwn,mkdn,mkdown,markdown,html,htm,css,txt,xml] + /// [default: md,markdown,mdx,qmd,rmd,mkd,mkdn,mdwn,mdown,mkdown,html,htm,css,txt,xml] #[arg(long, verbatim_doc_comment)] extensions: Option, diff --git a/lychee-bin/tests/cli.rs b/lychee-bin/tests/cli.rs index 2216751175..e6f7671762 100644 --- a/lychee-bin/tests/cli.rs +++ b/lychee-bin/tests/cli.rs @@ -1801,6 +1801,35 @@ The config file should contain every possible key for documentation purposes." .stdout(contains("http://127.0.0.1/inline")) .stdout(contains("http://127.0.0.1/bash")); } + + #[test] + fn test_quarto_treated_as_markdown() { + let input = fixtures_path!().join("TEST_QUARTO.qmd"); + + cargo_bin_cmd!() + .arg(input) + .arg("--dump") + .assert() + .success() + .stdout(contains("https://example.com")) + .stdout(contains("cdn.posit.co").not()) + .stdout(contains("inline.example.com").not()); + } + + #[test] + fn test_rmarkdown_treated_as_markdown() { + let input = fixtures_path!().join("TEST_RMARKDOWN.Rmd"); + + cargo_bin_cmd!() + .arg(input) + .arg("--dump") + .assert() + .success() + .stdout(contains("https://example.com")) + .stdout(contains("cdn.posit.co").not()) + .stdout(contains("inline.example.com").not()); + } + #[tokio::test] async fn test_verbatim_skipped_by_default_via_file() { let file = fixtures_path!().join("TEST_VERBATIM.html"); diff --git a/lychee-lib/src/types/file.rs b/lychee-lib/src/types/file.rs index 269ae43ba5..3d46c20ce4 100644 --- a/lychee-lib/src/types/file.rs +++ b/lychee-lib/src/types/file.rs @@ -140,8 +140,10 @@ impl std::fmt::Display for FileType { impl FileType { /// All known Markdown extensions + // NOTE: Stored in reverse-popularity order because the `Iterator` impl for + // `FileExtensions` pops from the end const MARKDOWN_EXTENSIONS: &'static [&'static str] = &[ - "markdown", "mkdown", "mkdn", "mdwn", "mdown", "mdx", "mkd", "md", + "mkdown", "mdown", "mdwn", "mkdn", "mkd", "rmd", "qmd", "mdx", "markdown", "md", ]; /// All known HTML extensions @@ -266,6 +268,9 @@ mod tests { assert_eq!(FileType::from("foo.md"), FileType::Markdown); assert_eq!(FileType::from("foo.MD"), FileType::Markdown); assert_eq!(FileType::from("foo.mdx"), FileType::Markdown); + assert_eq!(FileType::from("foo.qmd"), FileType::Markdown); + assert_eq!(FileType::from("foo.Rmd"), FileType::Markdown); + assert_eq!(FileType::from("foo.rmd"), FileType::Markdown); // Test that a file without an extension is considered plaintext assert_eq!(FileType::from("README"), FileType::Plaintext);