The goal of prefab is to make it easier for you to set up files and directories you need for a given project. You can think of the project scaffolding–like README files and directory structures–as a theme you want to apply to a new or existing project directory.
For example, r_analysis() is a prefab theme shipped with
this package to provide scaffolding for a simple R data analysis
project. To understand what it does, simply call the theme to show the
outline of steps:
library(prefab)
r_analysis()
#> <theme> 5 steps
#> • run → fs::dir_create('data_raw')
#> • run → fs::dir_create('data_processed')
#> • Writing 'main.R' (skip)
#> • Writing 'README.md' (skip)
#> • Writing '.gitignore' (union)
#> ℹ Apply with `use_theme()` or `create_project()`Nothing happens to the file system until you pass the theme to
use_theme() or create_project(). Above, the
r_analysis() theme indicated that it contains five steps:
two create directories, and three write files. File steps also contain a
merge strategy shown in () for handling pre-existing
destination files.
Pass the theme to create_project() to create a new
project folder with that scaffolding.
create_project(tempfile("my-analysis-project"), r_analysis())
#> ✔ Running fs::dir_create('data_raw')
#> ✔ Running fs::dir_create('data_processed')
#> ✔ Writing 'main.R' (new)
#> ✔ Writing 'README.md' (new)
#> ✔ Writing '.gitignore' (new)That created a directory called ~/my-analysis-project
and added two new subdirectories and three new files. Then (assuming you
are using Positron or Rstudio) it opened the project in a new
session.
Instead of creating a new project directory, you can also apply a
theme to your current project with use_theme():
+ and argumentsA prefab theme is a function that returns an ordered list of steps
that modify files and paths, or run other functions Because themes are
just functions, they can have arguments and you can compose them with
+, concatenating their steps.
use_theme(r_analysis(data_dirs = FALSE) + claude_r_analysis())
#> ✔ Running fs::dir_create('data_raw')
#> ✔ Running fs::dir_create('data_processed')
#> ✔ Writing 'main.R' (new)
#> ✔ Writing 'README.md' (new)
#> ✔ Writing '.gitignore' (new)
#> ✔ Writing '.claude/rules/r_analysis.md' (new)
#> ✔ Writing '.gitignore' (union)Steps run left to right.
There are three ways to build your own theme:
From a directory of files. Arrange template files in
a folder and theme_from_dir() turns them into a theme. For
example, put your desired template files in the folder
~/my-project-structure:
Use theme_from_dir() to turn them into a theme, and then
apply them to a new or existing project:
my_theme <- theme_from_dir("~/my-project-structure")
use_theme(my_theme)
#> ✔ Running fs::dir_create('data')
#> ✔ Writing '.gitignore' (new)
#> ✔ Writing 'R/analysis.R' (new)
#> ✔ Writing 'README.md' (new)An optional _prefab.yml sidecar controls per-file merge
strategies and template data.
By composing existing themes. Combine and extend
built-in or custom themes with +:
Themes are combined left to right.
From steps. Build themes programmatically with
step_file(), step_text(), and
step_run():
A prefab theme is a function that returns an ordered list of steps that modify files and paths, or run other functions.
The three step types are:
step_file(source, dest) – deploy a filestep_text(content, dest) – deploy inline textstep_run(fn, ...) – execute a function for its side
effectsmy_analysis_theme <- function() {
ignore_lines <- c(".Rproj.user", ".Rhistory", ".RData")
new_theme(
step_file("~/my-templates/main.R", "main.R", strategy = "skip"),
step_text(ignore_lines, ".gitignore", strategy = "union"),
step_run(fs::dir_create, "data", .label = "fs::dir_create")
)
}Because themes are just functions, parameters give you conditional
behavior for free. NULL arguments to
new_theme() are silently dropped, so
if (cond) step(...) works naturally.
my_analysis_theme <- function(use_data_dir = TRUE, extra_ignores = character(0)) {
ignore_lines <- c(".Rproj.user", ".Rhistory", ".RData", extra_ignores)
new_theme(
step_file("~/my-templates/main.R", "main.R", strategy = "skip"),
step_text(ignore_lines, ".gitignore", strategy = "union"),
if (use_data_dir) step_run(fs::dir_create, "data", .label = "fs::dir_create")
)
}from_dir() and from_package() create
step-builders that resolve source paths relative to a directory or an
installed R package:
# Resolve from a local directory
from_templates <- from_dir("~/my-templates")
from_templates("header.md", "README.md", strategy = "skip")
# Resolve from an installed package's inst/ directory
from_my_package <- from_package("my_package")
from_my_package("r_analysis/main.R", "main.R", strategy = "skip")from_dir() resolves its path to absolute at creation
time, so later working directory changes do not affect it.
from_package() works with both installed packages and
devtools::load_all().
Every file step declares a strategy for handling
pre-existing destination files. Strategies are what make
use_theme() safe to re-run.
| Strategy | Behavior | Idempotent | Typical use |
|---|---|---|---|
"overwrite" |
Replace the file entirely | Yes | Managed config files |
"skip" |
Do nothing if file exists | Yes | Starter files users will edit |
"union" |
Append lines not already present | Yes | .gitignore, .Rbuildignore |
"append" |
Append all content unconditionally | No | Rare; prefer "union" |
"merge_json" |
Recursively merge JSON objects | Yes | .claude/settings.json |
Guidelines for choosing:
"overwrite"."skip"."union"."merge_json" (objects merge
key-by-key, arrays are union-merged, scalar collisions preserve the
destination value)."append" unless you want duplicate content on
re-runs.File steps support {{var}} interpolation when
data is non-NULL:
from_templates <- from_dir("~/my-templates")
new_theme(
# data = list() enables rendering with auto-discovered variables only
from_templates("README.md", "README.md", strategy = "skip", data = list()),
# Explicit variables supplement or override auto-context
from_templates("CITATION.md", "CITATION.md",
data = list(org_name = "Acme Corp"))
)Auto-discovered variables (built once per use_theme()
call):
| Variable | Source |
|---|---|
project_dir |
Name of the project root directory |
package_name |
Package field from DESCRIPTION, or
project_dir |
year |
Current year |
date |
Current date (YYYY-MM-DD) |
A template file like:
# {{project_dir}}
Created {{date}} by {{org_name}}.
is rendered by merging explicit data on top of
auto-context. Explicit values win on collision. If a variable is
referenced but not available, rendering fails with an informative
error.
step_text() does not support data – inline
content can interpolate R variables directly.
theme_code()theme_code() prints R code that reproduces a theme –
useful for understanding built-in themes or as a starting point for
customization:
theme_code(claude_r_analysis())
#> new_theme(
#>
#> from_package("prefab")("claude/settings.json", ".claude/settings.json", strategy = "merge_json"),
#>
#> from_package("prefab")("claude/rules/r_analysis.md", ".claude/rules/r_analysis.md"),
#>
#> step_text(c(".Rproj.user", ".Rhistory", ".RData", ".DS_Store"), ".gitignore", strategy = "union")
#> )Copy the output, edit it into your own theme function, and swap out or add steps. The code is also returned invisibly as a string.