Nimja Template Engine

repository·master·Indexed 42 days ago

https://github.com/enthus1ast/nimja

A high-performance, statically typed template engine for the Nim programming language, inspired by Jinja2 and Twig. Nimja compiles templates directly into Nim code at compile-time, providing features such as template inheritance via 'extends', partials via 'importnimja', and full access to Nim's syntax and control structures. It supports custom output types, hot code reloading, and targeted block rendering via the 'blockToRender' parameter.

Tokens
2.5K
Snippets
12
Records
15
Agent score
3%

What's inside Nimja

  1. Overview of Nimja Template Engine

    master
    Nimja is a typed and compiled template engine for Nim, inspired by Jinja2 and Twig. It transforms templates into Nim code at compile-time, allowing you to use the full power of the Nim language directly within your templates. This results in extremely high performance, as templates are executed as native code.
  2. Use template inheritance with extends

    master

    A child template can extend a master template using the {% extends "path/to/master.nimja" %} syntax. The master template defines placeholder {% block name %} tags, which the child template fills by defining blocks with the same name.

    Rules for extends:

    1. extends must be the first token in the template (comments and strings are allowed before it).
    2. If a template extends another, all content in the child template must be wrapped in a {% block %}.
    3. The master template can declare variables that become visible in the child template.
    {# child.nimja #}
    {% extends "partials/_master.nimja" %}
    
    {% block content %}
      I AM CONTENT
    {% endblock %}
    
    {% block footer %}
      ...The footer..
    {% endblock %}
  3. Use baseDir with template compilation functions

    master

    To ensure template imports (like {% importnimja ... %}) work correctly, especially when your templates are used in different modules, always provide the baseDir parameter to compilation functions.

    It is discouraged to use absolute paths with tmplf. Instead, use a relative path and specify the baseDir using getScriptDir() or a similar mechanism.

    Recommended Pattern:

    compileTemplateFile("some/template.nimja", baseDir = getScriptDir())
    compileTemplateFile("templates/index.nimja", baseDir = getScriptDir())
  4. How templates and Nim code interact

    master

    Because Nimja transforms templates into Nim code, templates have access to all variables passed into the rendering procedure. Furthermore, you can write arbitrary Nim code inside template tags.

    Syntax Mapping:

    • {{ myObj.myVar }} becomes $(myObj.myVar)
    • {% myExpression.inc() %} becomes myExpression.inc()
    • {# comment #} is a template comment.

    Custom Output Types: The return variable of a template procedure can be a string, a Rope, or any object that implements the &=(obj: YourObj, str: string) procedure.

    proc foo(ss: string, ii: int): string =
      compileTemplateStr(
        """example{% if ii == 1%}{{ss}}{%endif%}{% var myvar = 1 %}{% myvar.inc %}"""
      )
  5. Configure VSCode Syntax Highlighting for .nimja files

    master

    Since .nimja files are not natively recognized by VSCode, you can associate them with the html language mode to enable syntax coloring and formatting. Add the following to your VSCode settings.json:

    "files.associations": {
      "*.nwt": "html",
      "*.nimja": "html"
    }
  6. Compile Nimja templates

    master

    Because Nimja is a compiled engine, you must recompile your application whenever you change a .nimja template file.

    To ensure Nim detects changes in template files, use the -f (force) flag:

    nim c -f -r yourfile.nim
  7. Enable Hot Code Reloading for Templates

    master

    Nimja supports hot code reloading (HCR) to allow you to modify templates and your template-rendering logic without restarting your host application.

    To implement this:

    1. Import nimja/hcrutils in your host application.
    2. Use a ChangeWatcher to monitor your templates directory.
    3. Call asyncCheck cw.recompile() when a change is detected to recompile your template module (e.g., tmpls.nim).

    Note: When moving to production, comment out the asyncCheck cw.recompile() line.

    import nimja/hcrutils
    import jester, os
    
    var cw = newChangeWatcher(@[getAppDir() / "templates/"])
    asyncCheck cw.recompile()
  8. Run the Nimja playground in a browser

    master

    The Nimja playground demonstrates that Nimja can run on the JavaScript target. The current implementation is a simple demonstration that replaces the innerHTML of the document.

    To run the playground locally:

    1. Compile the jsplayground.nim file to JavaScript using the Nim compiler: nim js jsplayground.nim
    2. Open the resulting index.html file in your web browser.
    nim js jsplayground.nim
  9. Debug Nimja Template Compilation

    master

    You can use specific Nim compiler flags to inspect the internal state of Nimja's template engine during development:

    • -d:dumpNwtAst: Dumps the NwtAst.
    • -d:dumpNwtAstPretty: Dumps the NwtAst as pretty-printed JSON.
    • -d:dumpNwtMacro: Dumps the generated Nim macros.
    • -d:nwtCacheOff: Disables the NwtNode cache.
    • -d:noPreallocatedString: Prevents preallocating the output string.
    • -d:noCondenseStrings: Disables string condensing.
    nim c -d:dumpNwtAst -r yourfile.nim
    nim c -d:dumpNwtAstPretty -r yourfile.nim
    nim c -d:dumpNwtMacro -r yourfile.nim
  10. Use nimjautils for enhanced loop control

    master

    The nimja/nimjautils module provides a .loop() method for iterators, yielding a Loop object that provides metadata about the current iteration.

    Loop object fields:

    • index0: Current index (starts at 0).
    • index: Current index (starts at 1).
    • revindex0: Reverse index (starts at 0 from the end).
    • revindex: Reverse index (starts at 1 from the end).
    • length: Total length of the sequence.
    • first: Boolean, true if first iteration.
    • last: Boolean, true if last iteration.
    • previtem: Option containing the previous item.
    • nextitem: Option containing the next item.
    • cycle(@[...]): Returns a value that cycles through the provided array.
    {% for (loop, row) in rows.loop() %}
        <li class="{{ loop.cycle(@["odd", "even"]) }}">{{row}}</li>
    {% endfor %}
  11. Render specific template blocks

    master
    As of version 0.10.0, all template rendering procedures (tmpls, tmplf, compileTemplateString, and compileTemplateFile) support a blockToRender parameter. When this parameter is set, Nimja will only render the specified block from the template, which is useful for implementing features like HTMX partial updates.
  12. Core Template Compilation Procedures

    master

    Nimja provides three primary procedures to transform templates into Nim code. These procedures generate the body of a procedure or iterator, typically appending to a result variable.

    • compileTemplateStr(str: string): Compiles a template string into Nim AST.
    • compileTemplateFile(path: string): Compiles the content of a file into Nim AST.
    • tmpls(str: string) and tmplf(path: string): Shorthand versions for inlining a render call directly.

    Important: When using importnimja or extends, you must provide the baseDir parameter (often using getScriptDir()) so the compiler can locate the referenced files.

    import nimja/parser
    import os
    
    # Using compileTemplateFile in a procedure
    proc myRenderProc(someParam: string): string =
      compileTemplateFile("myFile.html", baseDir = getScriptDir())
    
    # Using tmpls for inlining
    let leet = 1337
    echo tmpls("foo {{leet}}")
    
    # Using tmplf for inlining a file
    echo tmplf("templates" / "myfile.nimja", baseDir = getScriptDir())