Metanorma: Aequitate Verum

Metanorma Site Generation (metanorma site)

Introduction

The site subcommand enables you to compile and publish multiple Metanorma documents as a cohesive document site. This functionality is particularly useful for:

  • Multiple documents belonging to the same series

  • Single documents published to multiple venues

  • Multiple parts of a document collection

Commands

Site generation

metanorma site generate [OPTIONS...] [SITE_MANIFEST_FILE]

If no SITE_MANIFEST_FILE is provided, the file metanorma.yml in the current directory is used.

CLI options

-c, --config CONFIG

File path for Metanorma site manifest YAML configuration (Default: metanorma.yml in the current directory) (Deprecated: use the SITE_MANIFEST_FILE argument instead)

-o, --output-dir DIR

Directory for generated site output, relative to the current working directory (Default: _site in current directory)

-s, --stylesheet FILE

Specify custom stylesheet for HTML output, relative to the current working directory

-t, --template-dir DIR

Specify custom Liquid templates for site design, relative to the current working directory

--output-filename-template TEMPLATE_STRING

Define output filenames using Liquid templating (Uses Relaton model for variable access)

--agree-to-terms

Accept third-party licensing terms

--no-agree-to-terms

Reject licensing terms

--skip-agree-to-terms

Skip license agreement prompt

--install-fonts

Install required fonts (Default)

--no-install-fonts

Do not install fonts

--skip-install-fonts

Skip font installation prompt

--continue-without-fonts

Proceed when fonts are missing

--no-continue-without-fonts

Abort when fonts are missing

--skip-continue-without-fonts

Skip missing fonts prompt

-S, --strict

Abort compilation on errors

--no-strict

Continue on errors

--skip-strict

Skip strict mode prompt

Configuration using site manifest

The site generation process can be configured using a site manifest. The site manifest file is a configuration file that is in YAML format:

metanorma:
  source:
    files: (1)
      - doc1.adoc
      - "series/*.adoc"
  collection:
    name: "Site Title"  (2)
    organization: "Publisher"  (3)
  template:
    path: "templates"  (4)
    stylesheet: "custom.css" (5)
    output_filename: ""  (6)
  1. metanorma.source.files: Array of files to process

  2. metanorma.collection.name: Site title

  3. metanorma.collection.organization: Site owner

  4. metanorma.template.path: Custom template directory (see Templates and styling)

  5. metanorma.template.stylesheet: Custom stylesheet

  6. metanorma.template.output_filename: Filename template (see Output filename templates)

By default, the file metanorma.yml located in the source directory would be used.

Templates and styling

General

Customization of the site’s HTML output is done using Liquid templates.

Metanorma uses the template files found in the specified template directory to generate the site’s index page and individual document entries.

Template directory structure

The path to the template directory can be specified via the CLI using the -t, --template-dir option, or in the site manifest under the metanorma.template.path key.

If the path is an absolute path, it is used as is.

If it is a relative path, its full path is calculated based on where it is defined.

  • If it is defined in the site manifest file, then the path is relative to the directory containing the manifest file.

  • If it is defined via the CLI, then the path is relative to the current working directory, i.e., the directory from which the command was run.

  • If no manifest file is present, the path is also relative to the current working directory. If a template directory is not specified, the default template directory is used, which is located in the Relaton CLI repository

The template directory must contain:

_index.liquid

Main template for the site’s index page

_document.liquid

Template for individual document entries

Note
The name of this file actually depends on how it is referenced in the index template. See official documentation on include (which is being deprecated) and render for more information.
CSS stylesheet

Specify the path to the stylesheet, of which the content can be referenced in templates using the css variable.

The path can be specified in the site manifest under the metanorma.template.stylesheet key, or via the CLI using the -s, --stylesheet option.

Path resolution follows the same rules as the template directory path.

For a more detailed example of what a template directory might look like, see the default templates in the Relaton CLI repository.

Template variables

The index template (_index.liquid) has access to:

  • title - Collection title from configuration

  • author - Collection author/organization

  • date - Generation date

  • css - Stylesheet content

  • metanorma_v - Metanorma version (if available)

  • documents - Array of document entries

The document template (e.g., _document.liquid, via the document template variable) has access to:

  • docid.id - Document identifier

  • title - Document title

  • html - HTML URL path

  • pdf - PDF URL path

  • doc - Word document URL path

  • xml - XML URL path

  • rxl - Relaton XML URL path

  • uri - Document URI

  • doctype.type - Document type

  • edition.content - Edition information

  • docstatus.stage.value - Document stage

  • docstatus.stage.abbreviation - Abbreviaton for the document status

  • docstatus.substage.value - Document substage

  • revdate - Revision date

  • items - Array of document items (if applicable, e.g., when the document is itself a collection)

Example _index.liquid template
<!-- _index.liquid -->
<!DOCTYPE html>
<html>
  <head>
    <title>{{ title }}</title>
    <style>{{ css }}</style>
  </head>
  <body>
    <h1>{{ title }}</h1>
    <div class="documents">
      {% render 'document' for documents as document %}
    </div>
    <footer>Generated: {{ date }}</footer>
  </body>
</html>
Note
The example uses the render tag instead of include as this the recommended way to include templates in Liquid. The for parameter allows iteration through the documents collection while maintaining proper variable scoping.
Example _document.liquid template
<!-- _document.liquid -->
<div class="document">
  <h2><a href="{{ document.html }}">{{ document.docid.id }}</a></h2>
  <p class="title">{{ document.title }}</p>
  <p class="status">Status: {{ document.docstatus.stage }}</p>
  {% if document.pdf %}
    <a href="{{ document.pdf }}">PDF</a>
  {% endif %}
</div>

For a more detailed example of what a template might look like, see the default Liquid templates.

Output filename templates

General

Using Liquid templating, Metanorma allows for custom output filenames across all output formats.

For the list of all variables available for filename templates (which is separate from those available in the HTML templates), see the Available variables section.

Default behavior

When an empty string is passed as the template (or when no template is specified), the system generates a normalized version of the document identifier.

For a document with identifier "ISO/IEC FDIS 12345-3", the default output would be:

iso-iec-fdis-12345-3

The generated document files would be named accordingly:

_site/
├── index.html
├── documents.xml
└── documents/
    ├── iso-iec-fdis-12345-3.html
    ├── iso-iec-fdis-12345-3.doc
    ├── iso-iec-fdis-12345-3.pdf
    ├── iso-iec-fdis-12345-3.rxl
    └── iso-iec-fdis-12345-3.xml

Available variables

Templates can access document metadata through the document object:

  • docidentifier - Full document identifier

  • language - Document language code

  • edition - Edition number

  • doctype - Document type

  • docnumber - Document number

  • partnumber - Part number (if applicable)

Example using liquid filters
metanorma:
  template:
    output_filename: "{{ document.docidentifier | downcase | replace: '/' , '-' }}"
Example using conditionals
metanorma:
  template:
    output_filename: |
      {%- if document.doctype == 'international-standard' -%}
        iso-
      {%- else -%}
        std-
      {%- endif -%}
      {{- document.docnumber -}}
      {%- if document.partnumber %}-{{ document.partnumber }}{% endif %}

This demonstrates:

Template filters

Common Liquid filters available:

downcase

Convert to lowercase

replace

String replacement

strip

Remove leading/trailing whitespace

For the full list of available Liquid filters, see Liquid Filters.

Error handling

  • Invalid syntax raises Liquid::SyntaxError

  • Missing variables are replaced with empty strings

Example with missing variable:
# Template: "{{ nonexistent }}_{{ document.language }}"
# Result: "_en"

Site structure

Generation process

  1. Scans source directory for Metanorma documents

  2. Compiles each document with specified options

  3. Creates site directory structure

  4. Generates collection index (documents.xml)

  5. Creates HTML index page

  6. Copies assets and compiled documents

Output directory structure

_site/              (1)
├── index.html      (2)
├── documents.xml   (3)
└── documents/      (4)
    ├── doc1.html
    ├── doc1.pdf
    ├── doc1.rxl
    ├── doc1.xml
    ├── doc2.html
    ├── doc2.pdf
    ├── doc2.rxl
    └── doc2.xml
  1. Default output directory

  2. Main site index

  3. Collection index

  4. Compiled documents

Best practices

Organize documents in logical directory structures

Place each document in a separate directory to avoid conflicts.

sources/
├── doc1/
│   └── main.adoc
├── doc2/
│   └── main.adoc
└── doc3/
    ├── main.adoc
    └── annex.adoc
Use consistent naming conventions in templates

Use the same name for the document template file as the references in the template files.

<!-- _index.liquid --> (1)
...
{% render 'document' for documents as document %} (2)
...

<!-- _document.liquid -->     (2) (3)
...
<h1>{{ document.title }}</h1> (3)
...
  1. The name of the index template file is always going to be _index.liquid. As of writing, this is not configurable.

  2. The name of the document template file should match the reference in the index template.

  3. The name of the document template file should match the reference in the document template itself.

Include error handling for optional metadata fields
default filter

Use Liquid’s default filter to handle missing metadata fields.

<h1>{{ document.title | default: "Untitled" }}</h1>
conditional logic

Use conditional logic to handle optional metadata fields.

{% if document.partnumber %}
  <p>Part {{ document.partnumber }}</p>
{% endif %}
Use whitespace trimming for whitespace-sensitive templates

This is particularly important when working with output filename templates, which can be sensitive to whitespace. Use Liquid’s whitespace control to manage whitespace in templates.

TIPS: When in doubt, always use whitespace control to ensure consistent filename template rendering.
metanorma:
  template:
    output_filename: |
      {%- if document.doctype == 'standard' -%}
        std-{{- document.docnumber -}}
      {%- else -%}
        doc-{{- document.docidentifier | downcase -}}
      {%- endif -%}

Examples

Minimal configuration

Uses the default configuration file (metanorma.yml) in the current directory, and outputs to the default directory (_site).

metanorma site generate

Basic site generation (given directory)

Uses the default configuration file (metanorma.yml) in the ./sources directory, and outputs to the specified directory (./output).

metanorma site generate ./sources -o output

Basic site generation (given site manifest file)

Uses the specified configuration file (./sources/project-a/my-manifest.yml), and outputs to the specified directory (./output).

metanorma site generate ./sources/project-a/my-manifest.yml -o output

Custom naming with metadata

Using the CLI:

> metanorma site generate \
  --output-filename-template \
  "{{ document.docidentifier }}-{{ document.version }}"

Alternatively, using the site manifest:

# metanorma.yml
metanorma:
  template:
    output_filename: "{{document.docidentifier}}-{{document.version}}"

Complex configuration

Longer template expressions are best defined in the site manifest, which allows for a more readable multiline format:

metanorma:
  source:
    files:
      - "doc*/main.adoc"
      - "collection*/metanorma.yml"
  collection:
    name: "Technical Documentation"
    organization: "Acme Corp."
  template:
    stylesheet: "assets/stylesheets/site_index.css"
    template_dir: "templates"
    output_filename: |
      {% if document.doctype == 'standard' -%}
        std-{{ document.docnumber -}}
      {%- else -%}
        doc-{{ document.docidentifier | downcase -}}
      {%- endif %}

CI/CD pipeline

For scripting and automation, use the --agree-to-terms option to bypass the license agreement prompt:

> metanorma site generate --agree-to-terms

See Continuous Integration and Continuous Deployment for more detailed examples on how to integrate Metanorma site generation into your CI/CD pipeline.