9.1 Site Configuration Language
| #lang camp/site | package: camp-lib |
A Camp site is a single website, organized as a Racket package.
A site has one or more collections, which are named groups of pages with a sort order, a mapping between source and output paths, and optional taxonomies for further organization.
A page is a single source document.
A feed is an RSS or Atom feed which includes all pages from a set of one or more collections. A site may specify zero feeds, one feed, or multiple feeds.
The #lang camp/site language provides a TOML-based configuration format for defining Camp sites. Files written in this language are parsed and validated against the site schema.
#lang camp/site # Required values ------ title = "Site Title" url = "https://example.com" founded = 1912-07-04 # Note no quotes authors = ["Me (me@example.com)"] # List of strings, must be in this format # Optional values ------ sources = ".mypage" # .md.rkt and .page.rkt are always recognized static-folder = "res" # default is "static" output-folder = "public" # default is "publish" deploy-script = "deploy.sh" default-render = "(camp-demo/render render-page)" # → string datum: list of module and a function identifier # function signature: Document, Context -> Xexpr # Collections ---------- [[collections]] name = "blog" source = "blog/*" output-paths = "blog/[yyyy]/[MM]/*/" render-with = "(camp-demo/render render-post)" # same as default-render order = "descending" sort-key = "date" taxonomies = ["tags", "series"] [[collections]] name = "pages" source = "pages/*" output-paths = "*/" render-with = "(camp-demo/render render-page)" sort-key = "title" order = "ascending" [[feeds]] filename = "feed.atom" # Extension .atom or .rss sets format collections = ["blog"] # List of collection names render-with = "(camp-demo/feeds feed-content)" # same as default-render
9.1.1 Required Fields
Field | Type | Description |
title | Site title | |
url | Base URL (must be valid) | |
founded | Founding date for tag URI generation | |
authors | Authors in "Name (email)" format |
procedure
(author-string? v) → boolean?
v : any/c
> (author-string? "Me (me@example.com)") #t
> (author-string? "Me (me@1.com)") #f
> (author-string? " (me@example.com)") #f
9.1.2 Optional Fields
Field | Type | Default | Description |
sources | ".md.rkt" | Source file extension | |
static-folder | "static" | Static assets directory | |
output-folder | "publish" | Build output directory | |
deploy-script | #f | Deployment script path | |
default-render | #f | Default render function |
9.1.3 Collections
Each [[collections]] entry defines a group of source documents:
Field | Type | Default | Description |
name | Collection identifier | ||
source | Location of sources | ||
output-paths | Defines output paths/URLs | ||
render-with | Render function specification | ||
taxonomies | (Optional) metadata keys | ||
sort-key | "date" | Metadata sort key | |
order | (or/c "ascending" "descending") | "descending" | Sort order |
procedure
(render-spec? v) → (or/c #f (listof module-path? symbol?))
v : any/c
In order to be valid, at site build time the identifier must be that of a function provided by the module, and the function must have the signature (-> document? context? xexpr?). This information is not checked by render-spec?, however.
> (render-spec? "(my-module render-func)") '(my-module render-func)
> (render-spec? "(\"mod.rkt\" func)") '("mod.rkt" func)
> (render-spec? "(100)") #f
9.1.4 Feeds
Each [[feeds]] entry defines an RSS or Atom feed:
Field | Type | Description |
filename | Output filename (.atom or .rss) | |
collections | Collection names to include | |
render-with | Feed content render function |
Each feed’s render-with value should identify a function with the same signature as page render functions:
(define (feed-content doc ctxt) ;; doc: the Punct document ;; ctxt: same context as page render functions (slug, url, collection, etc.) ;; Returns x-expression for the feed entry body `(article ,@(document-body doc) (p (a ((href ,(context-url ctxt))) "Read more..."))))
The ctxt argument is a context whch provides access to the page’s canonical URL, enabling feed content to include links back to the original page on your site.
procedure
(feed-filename? v) → boolean?
v : any/c
> (feed-filename? "posts.atom") #t
> (feed-filename? "blog.rss") #t
> (feed-filename? "comments") #f
9.1.5 Source/Output Path Mapping
Camp uses path patterns to map source files to output locations.
A source path pattern specifies where to find source documents within a collection (e.g., "blog/*"). An output path pattern specifies the URL structure for rendered pages, with support for slug substitution and date-based paths (e.g., "blog/[yyyy]/[MM]/*/").
An output path pattern specifies the folder/file structure (and thus the URL) for rendered pages, with support for slug substitution, date-based paths, and meta value interpolation. In output path patterns:
Any folder name consisting only of * will be replaced by the source’s slug.
Any name inside a pair of brackets [] will first be looked up as a key in the source’s metadata. If a matching meta key is found, the bracket is replaced by that value. Otherwise, the name is interpreted as a CLDR date format code and formatted using the source’s date meta.
If the pattern ends in a trailing slash /, the output file will be named "index.html". Otherwise the output is the name of the pattern’s final element with an added ".html" extension.
A pattern must contain at least one * or [] element.
procedure
(source-path-pattern? v) → boolean?
v : any/c
> (source-path-pattern? "writing/*") #t
> (source-path-pattern? "/writing/*") #f
> (source-path-pattern? "writing/") #f
> (source-path-pattern? "../writing/*") #f
procedure
(output-path-pattern? v) → boolean?
v : any/c
> (output-path-pattern? "posts/*/") #t
> (output-path-pattern? "posts/[YYYY]/*/") #t
> (output-path-pattern? "../posts/*/") #f
> (output-path-pattern? "posts/") #f
procedure
(format-output-path pattern slug date metas) → path?
pattern : output-path-pattern? slug : string? date : (or/c date-provider? #f) metas : (or/c hash? #f)
> (format-output-path "posts/*/" "hello-world" #f #f) #<path:posts/hello-world/index.html>
> (format-output-path "blog/[yyyy]/[MM]/*/" "my-post" (date 2025 1 15) #f) #<path:blog/2025/01/my-post/index.html>
> (format-output-path "newsletter/[issue]/*/" "my-post" #f (hasheq 'issue 42)) #<path:newsletter/42/my-post/index.html>
procedure
(file-extension? v) → boolean?
v : any/c
procedure
v : any/c
> (non-rkt-file-extension? ".myformat.rkt") #t
> (non-rkt-file-extension? ".rkt") #f
9.1.6 Site Configuration API
The following data types from camp underlie the site configuration language. They are represented as hash-views: hash tables with struct-like accessor functions. For more information on hash-views, see hash-view: Struct-like Views of Hashes.
hash-view
(hash-view site ( title url founded authors [sources #:default ....] [static-folder #:default ....] [output-folder #:default ....] collections [racket-collection #:default ....] [deploy-script #:default ....] [default-render #:default ....] [feeds #:default ....]))
title : string?
url : valid-url-string?
founded : date-provider?
authors : (listof string?)
sources : string? = ".md.rkt"
static-folder : string? = "static"
output-folder : string? = "publish"
collections : (listof collection?)
racket-collection : (or/c string? #f)
deploy-script : (or/c string? #f)
default-render : (or/c list? #f)
feeds : (listof feed-config?) = '()
Required fields are title, url, founded, authors, and collections.
The racket-collection field is set automatically by load-site from the package’s "info.rkt". It contains the Racket collection name (e.g., "myblog") and is #f if the site is not installed as a package.
hash-view
(hash-view collection ( name source output-paths render-with [order #:default ....] [sort-key #:default ....] taxonomies))
name : string?
source : source-path-pattern?
output-paths : output-path-pattern?
render-with : (or/c list? #f)
order : string? = "descending"
sort-key : string? = "date"
taxonomies : (listof string?)
Required fields are name, source, and output-paths.
hash-view
(hash-view feed-config ( filename collections render-with))
filename : string?
collections : (listof string?)
render-with : list?
procedure
mod-path :
(or/c path-string? module-path? (and/c hash? (λ (h) (hash-has-key? h 'path))))
A filesystem path to a "site.rkt" file
A module path like 'my-site/site
A hash containing a 'path key (such as a book configuration returned by load-book)—
the site is discovered from the package’s "info.rkt"
Calling load-site again in the same process reflects any changes saved to the configuration module in the meantime; the GUI app and raco camp serve rely on this to reload the site when its configuration changes.
When called in a live-reloading context (the GUI app, or raco camp serve with watching enabled), load-site also deletes any compiled bytecode ("compiled" folders) under the site’s directory, except within the output and static folders. Bytecode produced by raco setup prevents modules from being reloaded after edits, so a live session must load site modules from source; one-shot commands like raco camp build leave bytecode alone and use it as usual.
When searching by collection name, finds packages with a 'camp-site field in their "info.rkt" whose 'collection name matches.
(resolve-site-spec "site.rkt") ; path if file exists (resolve-site-spec "myblog") ; finds myblog package (resolve-site-spec 'myblog) ; same, with symbol
procedure
(file-path->site-path file-path) → path?
file-path : path-string?
Raises an error if the file is not in a package, no "info.rkt" exists, or the 'camp-site field is not defined.
procedure
file-path : path-string?
To load the associated site for a book:
(define mybook (load-book "path/to/my.book.rkt")) (define mysite (load-site book)) ; discovers site from package info.rkt