packages feed

seihou-cli-0.4.0.0: help/templating.md

TEMPLATING

The Template strategy is the middle rung of Seihou's four generation
strategies. It is dispatched by the .tpl file extension and gives a step
two powers: replace {{variable.name}} with the resolved value, and gate
regions of text on a boolean expression via {{#if ...}} blocks.

Templates stay intentionally dumb. Anything richer -- loops, arithmetic,
derived values, multi-way branching -- belongs to the DhallText strategy,
which evaluates a full Dhall expression after placeholder substitution.
When a template starts fighting you, switch the step to dhall-text rather
than contort the template.

PLACEHOLDERS

  Syntax:

    {{variable.name}}      standard form
    {{ variable.name }}    surrounding whitespace inside the braces is ignored
    \{{variable.name}}     literal "{{variable.name}}" in output (escape)

  Variable names match [a-zA-Z][a-zA-Z0-9._-]*. Dots are part of the
  name, not structural.

  Coercion from the resolved value to text:

    VText  "hello"       -> hello
    VBool  True          -> true
    VBool  False         -> false
    VInt   42            -> 42
    VList  ["a", "b"]    -> a,b         (comma-separated, recursive)

  Placeholder substitution runs in three positions:

    Template bodies   the .tpl file contents
    Destination path  the `dest` field of a step
    Shell commands    the `run` and `workDir` fields of command steps

ESCAPING {{

  \{{ is consumed and emits a literal {{ in the output. There is no
  corresponding \}} escape -- unmatched closers are emitted verbatim.

    Use \{{name}} for placeholders   ->   Use {{name}} for placeholders

  The escape applies to placeholder syntax only. There is no escape
  for block tags; a template cannot emit a literal {{#if sequence.
  Use the DhallText strategy when that is required.

CONDITIONAL BLOCKS

  Inline blocks gate regions of a template body on a boolean expression:

    {{#if EXPR}} ... {{/if}}
    {{#if EXPR}} ... {{#else}} ... {{/if}}

  Blocks nest to arbitrary depth; a {{/if}} always pairs with its
  nearest open {{#if}}.

  Only the selected branch is expanded. The untaken branch's text is
  discarded before it reaches the placeholder engine. Unresolved
  placeholders and malformed sub-expressions inside the untaken branch
  therefore do not produce errors. This is deliberate: it lets authors
  gate optional features without having to declare every variable
  referenced inside the gate.

  Conditional blocks work in template bodies only. They do NOT work in
  `dest`, `run`, or `workDir`; those positions accept placeholders only
  and will treat {{#if}} as a placeholder name that fails to resolve.
  The dhall-text and structured strategies express conditionals with
  Dhall's own if/then/else after placeholder substitution.

EXPRESSION LANGUAGE

  The grammar used by {{#if ...}} (and by step-level `when` clauses):

    expr     = or_expr
    or_expr  = and_expr  ("||" and_expr)*
    and_expr = not_expr  ("&&" not_expr)*
    not_expr = "!" atom  |  atom
    atom     = "IsSet" NAME
             | "Eq"    NAME VALUE
             | "(" expr ")"
             | "true"  |  "false"
    VALUE    = "quoted string"  |  bareWord

  IsSet foo        true iff foo has a resolved value.
  Eq foo VALUE     true iff foo's value equals VALUE. Values are
                   type-sensitive: a bool variable compares against
                   bare-word true/false, not against quoted "true".
                   A text variable compares against a quoted string.
  && || !          the usual boolean operators.
  ( ... )          parentheses group sub-expressions.
  true / false     literal constants.

STANDALONE TAG WHITESPACE

  A block tag is standalone on its line when everything before it on
  the line is spaces or tabs (or the line starts with the tag) AND
  everything after it is spaces or tabs followed by a newline (or the
  template ends). A standalone tag absorbs its leading indent and one
  trailing newline. A tag that shares a line with other content keeps
  all surrounding whitespace verbatim.

  So the readable form:

    prev
      {{#if Eq flag true}}
      body
      {{/if}}
    next

  emits `prev\n  body\nnext\n` when flag = True and `prev\nnext\n`
  when flag = False -- no blank-line cruft either way. The body's
  indentation is preserved; the tag lines' indentation is not.

  Only ASCII space (0x20) and tab (0x09) qualify. NBSP and other
  Unicode whitespace do NOT trigger the trim.

  A blank line INSIDE a block body survives the trim, so to emit a
  deliberate blank separator at the boundary of a gated region, put
  the blank inside the block body rather than outside it.

ERRORS

  The engine reports five error kinds, each with a line number:

    UnresolvedPlaceholder   {{name}} references a variable with no value.
    MalformedPlaceholder    {{ opened on a line without a closing }}.
    UnterminatedIf          {{#if}} opener with no matching {{/if}}.
    OrphanBlockToken        {{/if}} or {{#else}} outside any open {{#if}}.
    MalformedIfExpression   The expression inside {{#if ...}} failed to parse.

  Block-level errors point at the source line of the offending tag.
  Placeholder errors inside a taken branch report a line number measured
  in the post-expansion text, which may drift from the source line by
  the number of newlines absorbed by standalone-trim; the variable name
  and relative position are still informative.

COMMON PATTERNS

  Optional single line:

    nativeBuildInputs = [
      pkgs.cabal-install
      {{#if Eq nix.postgresql true}}
      pkgs.postgresql
      {{/if}}
    ];

  If / else with a default (kept on one line so the output is one line):

    runtime = "{{#if IsSet docker.registry}}{{docker.registry}}/{{project.name}}{{#else}}{{project.name}}{{/if}}";

  Version-gated content (Eq does string-equal for text variables):

    {{#if Eq ghc.version "ghc912"}}
      -- ghc912-specific build flags
      ghc-options: -Wno-x-partial
    {{/if}}

WHEN TO USE DHALL-TEXT INSTEAD

  Switch the step's strategy from `template` to `dhall-text` (freeform
  Text) or `structured` (JSON / YAML with record-level composition)
  when you need to loop over a list, compute a derived value (for
  example, major.minor from ghc.version), choose among more than a
  handful of discrete cases, or merge structured data across modules.
  Both strategies substitute {{variable}} placeholders first, then
  evaluate the resulting Dhall expression -- so you still get variable
  interpolation on the way in and the full power of Dhall on the way out.

FURTHER READING

  docs/user/templating.md            full reference (this topic condensed)
  docs/user/module-authoring.md      variables, steps, and strategies in context
  docs/user/config-and-variables.md  how variables are resolved