Documentation / moth-template-files

moth-template-files

moth-template-files brings Markdown that already lives inside Moth's template world.

Write the content directly. Import the folded result as content. Reach for a normal .moth file when the job stops being content-shaped.

Choose the explanation level: moth-template-files files

moth-template-files files

A .mtf file is Markdown that lives inside Moth's template world.

Write prose directly


        # Getting started

        This is **bold** text.

        [p: This paragraph is styled by the @html builder helper.]
    

No imports, no declarations, no frontmatter. Content only.

Import the result


    import @docs/intro {content as intro}

    [: [intro] ]
    

The content constant is the only thing a .mtf file exports.

A .mtf file is a Moth template file. The compiler prepares it as an implicit compile-time Markdown template body and exposes one generated content constant.

Implicit template structure

The compiler builds each .mtf file as if its whole body were the body of an implicit compile-time Markdown template:


        content #String = [$md:
            ...entire .mtf file body...
        ]
    

The compiler constructs this structure directly from the token stream. It does not prepend wrapper source text to the file.

Exported declaration

A .mtf file exposes exactly one generated constant: content #String. The content is compile-time folded HTML text. No other declaration is exported.

File is content-oriented

A .mtf file is not a normal declaration file. It does not contain authored imports, declarations, frontmatter or metadata. It is a template body that produces a folded string.

Comments and punctuation

Because the file body is a template body, -- is content text, not a Moth comment. Backslashes follow template-body formatter behaviour. Backticks are Markdown inline-code delimiters, not raw string syntax.

Source file path and module ownership

A .mtf file belongs to the module that contains its directory. It can be imported by same-module files and re-exported through the module root's export: block.

Target and builder availability

Moth template files are available in HTML project builds. The .mtf body sees a restricted flat compile-time scope provided by the builder. See Template scope.

Related concepts

Choose the explanation level: Implicit Markdown

Implicit Markdown

Moth template files use a small Markdown subset, not full CommonMark.

What works

  • Headings with #
  • Paragraphs separated by blank lines
  • *italic* and **bold**
  • Links
  • Unordered and ordered lists
  • Inline code with single backticks

What does not work

  • Fenced code blocks (triple backticks). Use codeblock helpers instead.
  • Pipe tables. Use structured lists instead.
  • Raw HTML is escaped. Use $raw or $html directives for raw output.

Moth template files use Moth's $md Markdown flavour. This is intentionally smaller than full CommonMark.

Supported Markdown

  • Headings: #, ##, ### and deeper
  • Paragraphs: separated by blank lines
  • Emphasis: *italic* and **bold**
  • Moth-aware links: @path (label)
  • Unordered lists: - item
  • Ordered lists: 1. item
  • Inline code: paired isolated single backticks on the same line

HTML escaping

Moth template files escape HTML characters in Markdown text. Raw HTML tags such as <div> are escaped as &lt;div&gt; in the output. Use $raw or $html directives when raw HTML output is needed.

Not supported

  • Fenced code blocks (triple backtick blocks) are not rendered as <pre><code>. They appear as literal text. Use a codeblock helper with a $code(...) directive instead.
  • Markdown pipe tables are not rendered as <table>. They appear as literal text. Use a structured list or a page-owned table component instead.
  • Full CommonMark is not claimed. Moth's $md is a small, deliberate subset.

Backticks are inline-code delimiters

In $md, backticks delimit inline code, not raw strings. Empty spans, repeated backtick runs, unmatched backticks, multiline spans, variable-length delimiters and fenced code blocks are not part of the $md flavour.

Links use Moth-aware syntax

Use @/docs/packages (Packages) for internal routes or @https://example.com (Example) for external links. Ordinary [label](path) Markdown links belong to Plain Markdown and aren't parsed as links by a moth template's $md formatter.

-- is content

Because the file body is a template body, -- is body text, not a Moth comment. It renders as literal -- in the output.

Backslashes follow body behaviour

Backslashes in the template body follow formatter behaviour. They are not escape characters in the same sense as normal Moth source.

Code examples

Use codeblock and $code(...) where the helper is available through the content environment. See Template scope for what is visible inside a .mtf file.

Related concepts

Choose the explanation level: Content imports

Content imports

Import moth template content with normal extensionless imports.

Import and use


    import @docs/intro {content as intro}

    [: [intro] ]
    

The content constant is a compile-time string. Insert it wherever you want.

Do not include the extension

import @docs/intro.mtf is rejected. Use import @docs/intro.

Moth template content is imported with normal extensionless source import syntax. The imported content constant is a compile-time string.

Import syntax


    import @docs/intro

    [: [intro.content] ]
    

A namespace import exposes content as a field. A grouped import brings it directly:


    import @docs/intro {content as intro_content}

    [: [intro_content] ]
    

Aliasing content

Use as to give the imported content a local name. This is the standard form when multiple content files are imported into the same page.

Compile-time string semantics

The imported content is a #String compile-time constant. It has already folded by the time the importing file uses it. It is not a runtime value.

Use in a page or template

Insert the imported content into a template body:


    import @docs/intro {content as intro}

    [: [intro] ]
    

The caller decides where to insert it. Imported content does not become a page fragment automatically.

Direct extension imports are rejected


    import @docs/intro.mtf {content}
    

This is rejected with MOTH-IMPORT-0024. Use import @docs/intro without the .mtf extension.

Relative path resolution

Content imports follow the same path resolution rules as normal source imports. @intro resolves from the importing file's owning module root. @docs/intro resolves from the entry root. @./... has no supported meaning.

Import from root or helper files

A .mtf file can be imported from a module root, a normal source file, or both. The import path resolves the same way regardless of the importing file's role.

Imported content does not become a page fragment

Importing a .mtf file and inserting its content into a template does not create a page fragment. The caller controls where the content appears. Only direct top-level templates in the entry-selected module root become page fragments.

Visibility and module boundary

A .mtf file's content is subject to the same module visibility rules as other declarations. Cross-module imports can only access content if the module root's export: block re-exports it.


    export:
        import @intro {
            content as intro,
        }
    ;
    

Related concepts

Choose the explanation level: Template scope

Template scope

A .mtf file sees compile-time constants from its module root and @html helpers.

A constant capture


    -- docs/@docs.moth
    export:
        site_name #= "Moth"
    ;
    

        -- docs/intro.mtf
        # Welcome to [site_name]
    

The compile-time constant is inserted into the Markdown at that position.

A nested $raw override


        [$raw: 
Raw HTML here
]

Nested templates default to $md, but an explicit directive overrides that.

A .mtf file sees a small compile-time scope. The implicit template body can access constants from its module root and builder-provided helpers.

Same-directory module root constants

Exported compile-time constants and const records from the same-directory module root are visible inside a .mtf file. This is the primary way to parameterise moth template content.


    -- docs/@docs.moth
    export:
        site_name #= "Moth"
    ;
    

        -- docs/intro.mtf
        # Welcome to [site_name]
    

Builder-provided compile-time helpers

Exported compile-time constants and const records from @html are visible flat inside a .mtf file. This includes helpers such as p, codeblock and other HTML builder constants.


        -- docs/intro.mtf
        [p: This paragraph uses the @html p helper.]

        [codeblock, $code("moth"): value = 42]
    

Collision semantics

@html constants and same-directory module root constants don't shadow each other. If both surfaces expose the same visible name, compilation fails with a visible-name collision diagnostic. The collision identifies both sources where location data is available.

Authors resolve collisions by renaming the module-root export.


    -- docs/@docs.moth
    export:
        -- INVALID when this root is paired with a .mtf file because @html also exposes p.
        p #= [: Custom paragraph]
    ;
    

    -- docs/@docs.moth
    export:
        my_p #= [: Custom paragraph]
    ;
    

The collision occurs when the same-directory root and implicit @html surface are combined for a .mtf body.

This follows the same no-shadowing model as ordinary source visibility.

Implementation gap

The accepted collision model rejects same-name imports from both surfaces with a visible-name diagnostic. The current compiler resolves some collisions through local-over-HTML precedence rather than diagnosing them. Stage B of the language documentation plan removes the overwrite path and registers both surfaces through one visible-name registry. See Progress for the recorded drift.

Nested templates

Nested templates inside a .mtf body are valid. A nested template with no explicit directive defaults to $md. Any explicit directive (such as $raw, $fresh, $html, $code, $css or $escape_html) overrides the moth template Markdown default for that nested template.


        [$raw: 
Raw HTML output
]

Constants versus runtime values

Only compile-time constants are visible inside a .mtf file. Runtime bindings, runtime functions, types, traits, methods, external runtime APIs and the generated self content constant are not visible.

A reference to a runtime value is rejected with MOTH-RULE-0034 or MOTH-RULE-0053.

No runtime capture

Moth template bodies are const-required. There is no runtime capture. The entire body must fold at compile time.

Allowed template directives

All template directives available in the compile-time template environment are valid inside .mtf. This includes $md, $raw, $fresh, $html, $code, $css and $escape_html.

Child-template formatting boundary

Child templates are opaque barriers to the parent formatter. A child template's output is not inspected by the parent $md formatter. Template delimiters cannot pair across child boundaries.

Literal delimiter output

To output literal template delimiters, use string insertion:


        ["["]
        ["]"]
        ["[literal]"]
    

Content helpers

Content helpers such as codeblock work only where the builder environment provides them. Do not assume arbitrary normal source imports are available inside .mtf.

Related concepts

Choose the explanation level: moth-template-files limits

moth-template-files limits

Moth template files are the wrong tool when the job stops being content-shaped.

Use .moth instead when

  • You need runtime values or control flow.
  • You need imports or declarations.
  • You need page fragments or slot composition.

Use .md instead when

  • You need plain Markdown with no Moth syntax.
  • You need fenced code blocks or pipe tables.
  • You need raw HTML preserved.

Moth template files are intentionally small. These are the confirmed restrictions and implementation gaps.

Confirmed restrictions

  • Implicit const-required evaluation: the entire .mtf body must fold at compile time. There is no runtime path.
  • No normal top-level declarations: a .mtf file does not contain authored imports, functions, structs, choices, type aliases or trait declarations. Text that looks like a declaration is template body content, not a compiled declaration.
  • No runtime bindings or calls: runtime values are not visible. A reference to a runtime name is rejected with MOTH-RULE-0034 or MOTH-RULE-0053.
  • No fenced code blocks: triple-backtick blocks are literal text in $md, not <pre><code>. Use codeblock helpers instead.
  • No pipe tables: Markdown pipe table syntax is literal text in $md, not <table>. Use structured lists or page-owned table components.
  • No raw backtick strings: backticks are Markdown inline-code delimiters, not raw string syntax.
  • No stored named inserts: the implementation gap for stored named inserts remains. Named inserts passed as loose contributions are rejected.
  • Template and Markdown child boundaries: child templates are opaque barriers to the parent formatter. Delimiters cannot pair across child boundaries.
  • Unsupported runtime control flow: template if and loop are available as template directives, but ordinary runtime control flow is not part of the const-required moth template body.

Differences from normal .moth

  • A .moth file can contain imports, declarations, runtime code and page fragments. A .mtf file cannot.
  • A .moth file has normal Moth source scope. A .mtf file has a restricted flat compile-time scope.
  • A .moth file can produce page fragments. A .mtf file produces one content #String constant.

Differences from plain .md

  • A .mtf file is parsed as Moth template syntax. A .md file is not.
  • A .mtf file can access compile-time constants and use nested templates. A .md file cannot.
  • A .mtf file uses the $md renderer. A .md file uses the plain Markdown renderer (CommonMark-compatible with GFM extensions).
  • A .mtf file escapes raw HTML. A .md file preserves raw HTML.

When to choose normal Templates instead

Use a .moth template when:

  • runtime values or control flow are needed
  • full slot and wrapper composition is needed
  • the content is part of broader program logic
  • imports or declarations are needed in the same file

Implementation gaps

  • Stored named inserts remain an implementation gap. See the migration ledger for details.

Related concepts