Skip to content
Merged
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
65 changes: 19 additions & 46 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ references:
doi: 10.32614/R.manuals
version: '>= 4.1.0'
- type: software
title: cli
abstract: 'cli: Helpers for Developing Command Line Interfaces'
title: 'cli: Helpers for Developing Command Line Interfaces'
notes: Imports
url: https://cli.r-lib.org
repository: https://CRAN.R-project.org/package=cli
Expand All @@ -119,8 +118,7 @@ references:
doi: 10.32614/CRAN.package.cli
version: '>= 2.0.0'
- type: software
title: desc
abstract: 'desc: Manipulate DESCRIPTION Files'
title: 'desc: Manipulate DESCRIPTION Files'
notes: Imports
url: https://desc.r-lib.org/
repository: https://CRAN.R-project.org/package=desc
Expand All @@ -133,12 +131,11 @@ references:
- family-names: Hester
given-names: Jim
email: james.f.hester@gmail.com
year: '2026'
year: '2023'
doi: 10.32614/CRAN.package.desc
version: '>= 1.3.0'
- type: software
title: jsonlite
abstract: 'jsonlite: A Simple and Robust JSON Parser and Generator for R'
title: 'jsonlite: A Simple and Robust JSON Parser and Generator for R'
notes: Imports
url: https://jeroen.r-universe.dev/jsonlite
repository: https://CRAN.R-project.org/package=jsonlite
Expand All @@ -147,12 +144,11 @@ references:
given-names: Jeroen
email: jeroenooms@gmail.com
orcid: https://orcid.org/0000-0002-4035-0289
year: '2026'
year: '2025'
doi: 10.32614/CRAN.package.jsonlite
version: '>= 1.7.2'
- type: software
title: jsonvalidate
abstract: 'jsonvalidate: Validate ''JSON'' Schema'
title: 'jsonvalidate: Validate ''JSON'' Schema'
notes: Imports
url: https://docs.ropensci.org/jsonvalidate/
repository: https://CRAN.R-project.org/package=jsonvalidate
Expand All @@ -166,25 +162,11 @@ references:
given-names: Mathias
- family-names: Poberezkin
given-names: Evgeny
year: '2026'
year: '2025'
doi: 10.32614/CRAN.package.jsonvalidate
version: '>= 1.1.0'
- type: software
title: tools
abstract: 'R: A Language and Environment for Statistical Computing'
notes: Imports
authors:
- name: R Core Team
website: https://ror.org/02zz1nj61
institution:
name: R Foundation for Statistical Computing
website: https://ror.org/05qewa988
address: Vienna, Austria
year: '2026'
doi: 10.32614/R.manuals
- type: software
title: utils
abstract: 'R: A Language and Environment for Statistical Computing'
title: 'R: A Language and Environment for Statistical Computing'
notes: Imports
authors:
- name: R Core Team
Expand All @@ -196,8 +178,7 @@ references:
year: '2026'
doi: 10.32614/R.manuals
- type: software
title: yaml
abstract: 'yaml: Methods to Convert R Data to YAML and Back'
title: 'yaml: Methods to Convert R Data to YAML and Back'
notes: Imports
url: https://yaml.r-lib.org
repository: https://CRAN.R-project.org/package=yaml
Expand All @@ -206,12 +187,11 @@ references:
given-names: Jeremy
- family-names: Simonov
given-names: Kirill
year: '2026'
year: '2025'
doi: 10.32614/CRAN.package.yaml
version: '>= 2.2.1'
- type: software
title: bibtex
abstract: 'bibtex: Bibtex Parser'
title: 'bibtex: Bibtex Parser'
notes: Suggests
url: https://docs.ropensci.org/bibtex/
repository: https://CRAN.R-project.org/package=bibtex
Expand All @@ -232,8 +212,7 @@ references:
doi: 10.32614/CRAN.package.bibtex
version: '>= 0.5.0'
- type: software
title: knitr
abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R'
title: 'knitr: A General-Purpose Package for Dynamic Report Generation in R'
notes: Suggests
url: https://yihui.org/knitr/
repository: https://CRAN.R-project.org/package=knitr
Expand All @@ -242,11 +221,10 @@ references:
given-names: Yihui
email: xie@yihui.name
orcid: https://orcid.org/0000-0003-0645-5666
year: '2026'
year: '2025'
doi: 10.32614/CRAN.package.knitr
- type: software
title: lifecycle
abstract: 'lifecycle: Manage the Life Cycle of your Package Functions'
title: 'lifecycle: Manage the Life Cycle of your Package Functions'
notes: Suggests
url: https://lifecycle.r-lib.org/
repository: https://CRAN.R-project.org/package=lifecycle
Expand All @@ -261,8 +239,7 @@ references:
year: '2026'
doi: 10.32614/CRAN.package.lifecycle
- type: software
title: quarto
abstract: 'quarto: R Interface to ''Quarto'' Markdown Publishing System'
title: 'quarto: R Interface to ''Quarto'' Markdown Publishing System'
notes: Suggests
url: https://quarto-dev.github.io/quarto-r/
repository: https://CRAN.R-project.org/package=quarto
Expand All @@ -278,8 +255,7 @@ references:
year: '2026'
doi: 10.32614/CRAN.package.quarto
- type: software
title: rmarkdown
abstract: 'rmarkdown: Dynamic Documents for R'
title: 'rmarkdown: Dynamic Documents for R'
notes: Suggests
url: https://pkgs.rstudio.com/rmarkdown/
repository: https://CRAN.R-project.org/package=rmarkdown
Expand Down Expand Up @@ -322,8 +298,7 @@ references:
year: '2026'
doi: 10.32614/CRAN.package.rmarkdown
- type: software
title: testthat
abstract: 'testthat: Unit Testing for R'
title: 'testthat: Unit Testing for R'
notes: Suggests
url: https://testthat.r-lib.org
repository: https://CRAN.R-project.org/package=testthat
Expand All @@ -335,8 +310,7 @@ references:
doi: 10.32614/CRAN.package.testthat
version: '>= 3.3.0'
- type: software
title: usethis
abstract: 'usethis: Automate Package and Project Setup'
title: 'usethis: Automate Package and Project Setup'
notes: Suggests
url: https://usethis.r-lib.org
repository: https://CRAN.R-project.org/package=usethis
Expand All @@ -360,8 +334,7 @@ references:
year: '2026'
doi: 10.32614/CRAN.package.usethis
- type: software
title: withr
abstract: 'withr: Run Code ''With'' Temporarily Modified Global State'
title: 'withr: Run Code ''With'' Temporarily Modified Global State'
notes: Suggests
url: https://withr.r-lib.org
repository: https://CRAN.R-project.org/package=withr
Expand Down
5 changes: 3 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# cffr (development version)

- `as_cff.bibentry()` now selects the first `issn` of each `bibentry()`.
- `cff_create()` no longer creates duplicate identifiers when adding inferred
CRAN metadata.
- `cff_create()` no longer creates duplicate identifiers when merging inferred CRAN metadata with `CITATION` metadata.
- `cff_create()` now adds inferred CRAN DOIs only to packages available from CRAN. Dependency references retain extracted titles, abstracts, authors and citation years while adding their declared scope and version constraint, plus URLs and repository information from installed package metadata; a release date takes precedence over a citation year when available (#114).
- `cff_read_citation()` now makes every field supplied in `meta`, including custom `DESCRIPTION` fields, available to `CITATION` files and no longer retries with metadata from the **base** package when reading fails (#114).

# cffr 1.4.2

Expand Down
19 changes: 17 additions & 2 deletions R/cff-create.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
#' generated metadata.
#' @param gh_keywords A logical value. If `TRUE` and the package is hosted on
#' GitHub, add the repository topics as keywords.
#' @param dependencies A logical value. If `TRUE`, add the package dependencies
#' to the `references` CFF key.
#' @param dependencies A logical value. If `TRUE`, add installed package
#' dependencies to the `references` CFF key. See **Details**.
#' @param authors_roles Roles to be considered as authors of the package when
#' generating the `CITATION.cff` file. See **Details**.
#'
Expand All @@ -33,6 +33,21 @@
#' present in your package, \CRANpkg{cffr} auto-generates a
#' `preferred-citation` key using the information provided in that file.
#'
#' When `inst/CITATION` is present, every field from the package's
#' `DESCRIPTION`, including custom fields, is made available to that file
#' through its `meta` object. If the file cannot be evaluated with those
#' metadata, `cff_create()` does not retry with metadata from another package.
#'
#' When `dependencies = TRUE`, `cff_create()` starts from the first reference
#' returned by [utils::citation()] for each installed dependency. Citation
#' fields such as title, abstract, authors and year are preserved. Each
#' reference is adapted to the CFF software-reference type and enriched with
#' the dependency scope and version constraint from the source `DESCRIPTION`,
#' URLs and repository information from the installed dependency's
#' `DESCRIPTION` and the canonical CRAN DOI only when the dependency is
#' available from CRAN. A release date, when present, determines the year;
#' otherwise the citation year is retained.
#'
#' By default, only persons whose role in the `DESCRIPTION` file of the package
#' is author (`"aut"`) or maintainer (`"cre"`) are considered package authors.
#' The default setting can be controlled with the `authors_roles` argument. See
Expand Down
32 changes: 16 additions & 16 deletions R/cff-read.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#'
#' @param path A path to a file.
#' @param encoding Encoding to be assumed for `path`. See [base::readLines()].
#' @param meta A list of package metadata as obtained by
#' @param meta A package metadata object as obtained by
#' [utils::packageDescription()] or `NULL` (the default). See **Details**.
#' @param ... Arguments passed to other functions, for example to
#' [yaml::read_yaml()] or [bibtex::read.bib()].
Expand All @@ -27,11 +27,13 @@
#' @return
#' - `cff_read_cff_citation()` and `cff_read_description()` return an object
#' with class `cff`.
#' - `cff_read_citation()` and `cff_read_bib()` return an object of classes
#' - `cff_read_bib()` returns an object of classes
#' [`cff_ref_lst, cff`][cff_ref_lst] as defined by the
#' `definitions.reference` specified in the following guide:
#' ```{r child = "man/chunks/schema-guide.Rmd"}
#' ```
#' - `cff_read_citation()` returns the same classes as `cff_read_bib()`, or
#' `NULL` when the file cannot be evaluated.
#'
#' Learn more about the \CRANpkg{cffr} class system in [cff_class].
#'
Expand All @@ -42,8 +44,13 @@
#'
#' Section 1.9 CITATION files of *Writing R Extensions* (R Core Team 2026)
#' specifies how to create dynamic `CITATION` files using a `meta` object.
#' Therefore, the `meta` argument in [cff_read_citation()] may be needed to
#' read some files correctly.
#' [cff_read_citation()] makes every field supplied in `meta`, including custom
#' `DESCRIPTION` fields, available to the `CITATION` file. When `cff_create()`
#' reads a package `CITATION` file, it constructs `meta` from all fields in that
#' package's `DESCRIPTION`. With `meta = NULL`, only default encoding metadata
#' is supplied. If the file cannot be evaluated with those metadata,
#' `cff_read_citation()` returns `NULL`; it does not substitute metadata from
#' another package.
#'
#' @references
#' R Core Team (2026). *Writing R Extensions*.
Expand Down Expand Up @@ -216,20 +223,13 @@ cff_read_citation <- function(path, meta = NULL, ...) {
new_meta <- clean_package_meta(meta)
the_cit <- try(cff_read_citation_file(path, meta = new_meta), silent = TRUE)

# If there is an error, try again.
# If there is an error, return no citation.
if (inherits(the_cit, "try-error")) {
cli::cli_alert_warning(paste0(
"Could not read {.file {path}} with the provided {.arg meta}. ",
'Trying {.code utils::packageDescription("base")}.'
cli::cli_alert_danger(paste0(
"Cannot read {.file {path}} with the provided {.arg meta}. ",
"Returning {.code NULL}."
))
new_meta <- packageDescription("base")
the_cit <- try(cff_read_citation_file(path, meta = new_meta), silent = TRUE)
if (inherits(the_cit, "try-error")) {
cli::cli_alert_danger(
"Cannot read {.file {path}}. Returning {.code NULL}."
)
return(NULL)
}
return(NULL)
}
tocff <- as_cff(the_cit)

Expand Down
2 changes: 1 addition & 1 deletion R/cff-write.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#' primarily for its side effect of writing a `CITATION.cff` file.
#'
#' @details
#' For details of `authors_roles`, see [cff_create()].
#' For details of `authors_roles` and dependency extraction, see [cff_create()].
#'
#' The `x` argument identifies the metadata source. It does not determine the
#' output directory. This allows you to create a `CITATION.cff` from a package
Expand Down
10 changes: 7 additions & 3 deletions R/utils-cff-read.R
Original file line number Diff line number Diff line change
Expand Up @@ -384,11 +384,15 @@ get_desc_sha <- function(pkg) {
}

get_desc_doi <- function(pkg) {
pkg <- pkg$get("Package")
if (is.null(search_on_repos(pkg))) {
package <- pkg$get("Package")
repository <- clean_str(pkg$get("Repository"))
on_cran <- identical(repository, "CRAN") ||
identical(search_on_repos(package), cran_package_url(package))

if (!on_cran) {
return(NULL)
}

doi <- paste0("10.32614/CRAN.package.", pkg)
doi <- paste0("10.32614/CRAN.package.", package)
clean_str(doi)
}
14 changes: 6 additions & 8 deletions R/utils-create.R
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ cff_dependency_reference <- function(dep) {
cff_dependency_citation <- function(package) {
if (package == "R") {
mod <- as_cff(cff_citation())[[1]]
mod$year <- format(Sys.Date(), "%Y")
return(mod)
}

Expand All @@ -175,10 +174,6 @@ cff_dependency_citation <- function(package) {
return(NULL)
}

# Simplify the `cff` object to avoid cluttering the output.
mod$abstract <- mod$title
mod$title <- package

if (is_cran_dependency(package)) {
mod$doi <- paste0("10.32614/CRAN.package.", package)
}
Expand All @@ -191,10 +186,13 @@ cff_citation <- function(...) {
}

is_cran_dependency <- function(package) {
avail <- get_avail_on_init()
base_packages <- rownames(installed.packages(priority = "base"))
repository <- search_on_repos(package)

all(package %in% avail$Package, !package %in% base_packages)
all(
!package %in% base_packages,
identical(repository, cran_package_url(package))
)
}

cff_dependency_desc_fields <- function(mod, package) {
Expand All @@ -215,7 +213,7 @@ cff_dependency_year <- function(mod) {
date_rel <- mod[["date-released"]]

if (is.null(date_rel)) {
return(format(Sys.Date(), "%Y"))
return(mod[["year"]])
}

format(as.Date(date_rel), "%Y")
Expand Down
4 changes: 2 additions & 2 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ clean_package_meta <- function(meta) {
pkg <- desc::desc(tmp)
pkg$coerce_authors_at_r()
# Extract package data.
meta <- pkg$get(desc::cran_valid_fields)
meta <- pkg$get(pkg$fields())

# Clean missing values and drop empty fields.
meta <- drop_null(lapply(meta, clean_str))
Expand All @@ -308,7 +308,7 @@ desc_to_meta <- function(x) {
my_meta$coerce_authors_at_r()

# Convert to a list.
my_meta_l <- my_meta$get(desc::cran_valid_fields)
my_meta_l <- my_meta$get(my_meta$fields())
my_meta_l <- as.list(my_meta_l)
v_nas <- vapply(my_meta_l, is.na, logical(1))
my_meta_l <- my_meta_l[!v_nas]
Expand Down
Loading