Ravel pipes and directives reference
This is the reference for the implemented Ravel pipe commands and Markdown ravel-fence directives. The examples below are deliberately small and are mirrored by the Node tests named in Verification.
Two places pipes occur
An embedded substitution starts with an underscore and a matching quote. It resolves a chunk, then sends its value through a left-to-right pipe:
_"source | trim() | indent(2)"
The prior value is the implicit first input to an ordinary transform. A fenced chunk may also declare a definition pipeline, which transforms the completed chunk itself:
```pug {.ravel #page pipe="pug()"}
html
body hello
```Definition-transform implementations belong to a host or a separate transform package. Core receives them explicitly:
transformGraph(map, { transforms: { pug, markdown } });The core owns scheduling, resolution, diagnostics, and trace snapshots; it does not import Pug, Markdown, or another language compiler.
Ordinary transforms
Core provides these transforms. A host can register more, such as the capitalize, pug, and markdown examples in the tests.
| Command | Result |
|---|---|
concat() |
Leaves the incoming value unchanged. |
trim() |
Removes leading and trailing whitespace. |
normalize-eol() |
Converts CRLF and CR line endings to LF. |
indent(n) |
Prefixes nonempty lines with n spaces. |
dedent() |
Removes the smallest common indentation from nonempty lines. |
replace(search, replacement) |
Replaces all literal occurrences. |
quote-reference() |
Changes _(quote) starts into escaped literal text. |
indent(n) changes the value before it is placed into its caller, so it prefixes every nonempty line, including the first. Embedded substitutions also apply automatic use-site continuation indentation: later nonblank lines inherit the containing source line’s leading whitespace. See the chunk syntax guide for an example.
For example, if source is " value \n":
_"source | trim() | indent(2)"
produces " value".
Arguments are normally JSON-like literals. An ordinary transform may also take text(...) and ch(...) arguments; those are evaluated before the transform is invoked:
_"source | surround(text('['), ch('source'))"
Here a host-provided surround command receives the literal "[" and the value of source as its two arguments.
Value-producing commands: text and ch
Unlike ordinary transforms, these commands discard the incoming value and start with a new one.
text([string])
text produces its argument literally. It accepts zero or one string:
_"source | text('cool') | capitalize()" # Cool
_"source | text()" # empty string
_"|text('cool')|capitalize()" # Cool
The leading-pipe form has no starting chunk. It is valid only when the first command is text(...) or ch(...).
ch(expression)
ch evaluates a quoted Ravel chunk expression and makes that result the new incoming value. Its argument has no underscore prefix because ch marks it unambiguously as a chunk expression:
_"source | ch('other | trim()')"
_`|ch('|text("cool")|capitalize()')`
The second example starts the inner expression with text, then passes it to a host-provided capitalize transform. ch is also valid as an ordinary transform argument, as shown in the surround example above.
Delayed substitution: delay
delay preserves a safe placeholder through one or more definition-transform phases, then replaces it with a literal value or a chunk result. It is the only command with a restricted position:
_"|delay(ch('content.markdown | markdown()'), 1, 'RAVELSAFE')"
_"|delay(text('literal after phase one'), 1)"
_"|delay('another literal', 1)"
Its parameters are:
- A string,
text(...), orch(...)value. - Optional positive phase number; defaults to
1. - Optional alphanumeric safe symbol. Without it, Ravel creates one.
The ch(...) form defers evaluation of a chunk. A bare string or text(...) defers literal text. For a Pug/Markdown-shaped build, the Pug source contains a safe placeholder during the Pug phase; after Pug creates HTML, Ravel evaluates the Markdown chunk and replaces the marker.
delay must be the complete top-level expression. These are errors:
_"source | delay(ch('content'))"
_"|delay(ch('content'))|capitalize()"
_"|ch('|delay(ch("content"))')"
This prevents accidental delayed work inside an ordinary transform argument or inside a nested chunk pipeline. If a transform removes or duplicates the safe symbol, Ravel reports an error. Intermediate protected-input, transform-output, and fulfilled-output values remain available under program.trace.chunks[chunkId].
Derived chunks: emit
emit is a pipe tee: it leaves the incoming value unchanged and also declares an immutable, local derived chunk using the preceding part of the pipe.
_`widget | dedent() | emit('clean', {
"language": "typescript",
"tags": ["generated"]
})`
Inside guide::widget, this creates guide::widget:clean. The suffix may be:
| Suffix | Generated identity |
|---|---|
"minor" |
Same document/chunk, new minor, no type. |
"minor.type" |
Same document/chunk, new minor and type. |
".type" |
Same document/chunk/minor, new type. |
An emitted chunk cannot change document or base-chunk identity, overwrite an existing chunk, or be made from a document-less global chunk.
Directives in a ravel fence
Directives build graph structure and deliverables. They are written in a fenced block whose language is exactly ravel:
```ravel
in("shared.md")
create("program:stage.js", compose(
_"source.text",
pass(trim(), emit("observed.js")),
pipe(trim(), emit("min.js"), indent(2))
))
alias("public.js", _"program:stage.js")
out("dist/stage.js", _"public.js")
```Graph and output directives
| Directive | Meaning |
|---|---|
in("file.md") |
Loads and merges another Markdown or JSON Ravel Map before evaluation. |
create("name", compose(...)) |
Creates a document-local generated chunk. |
alias("name", _"target") |
Creates another document-local name for an existing chunk. |
out("path", _"target") |
Plans a named deliverable from a fully qualified target. |
create and alias names are local to the surrounding document. For example, in document guide, create("program:stage.js", ...) creates guide::program:stage.js. out qualifies a local reference with that same document before creating the deliverable.
compose steps
compose(...) starts with an empty accumulator and processes its steps in order:
| Step | Meaning |
|---|---|
_<quote>reference<quote> or append(_<quote>reference<quote>) |
Append the referenced chunk. |
newline(n) |
Set the separator before the next append to n newlines; default is one. |
pipe(commands...) |
Transform the accumulator and keep the transformed value. |
pass(commands...) |
Transform the accumulator only for nested emits, then keep the original value. |
The create example above demonstrates both pass and pipe. Given source text " value \n", it produces:
| Chunk | Value |
|---|---|
program:stage.js |
" value" |
program:observed.js |
"value" |
program:min.js |
"value" |
pass is useful for recording an observed derived form without changing the main composition. pipe changes the main accumulator after its commands run.
Verification
These examples correspond to tested behavior:
| Topic | Test |
|---|---|
Reference transforms and emit |
test/core-graph.test.mjs: “transforms references, expands emit chunks, and plans out deliverables”. |
create, alias, pipe, pass, and newline |
test/core-graph.test.mjs: “create imposes…” and “compose pipe transforms…”. |
| Definition phases and delayed substitution | test/core-graph.test.mjs: “definition pipelines execute around delayed substitutions…”. |
text, ch, command arguments, and text() |
test/core-graph.test.mjs: “text and ch reset a pipeline…”. |
Isolated delay validation |
test/core-graph.test.mjs: “delay is permitted only…”. |
| External Pug-like and Markdown-like transforms | test/host-node.test.mjs: “external language transforms run before delayed content is fulfilled”. |
| Markdown directive-fence translation | test/markdown-adapter.test.mjs: “ravel fences translate directives…”. |
Run the complete set with:
npm test