{"id":13458110,"url":"https://github.com/enthus1ast/nimja","last_synced_at":"2025-04-06T20:08:46.230Z","repository":{"id":40694772,"uuid":"394036943","full_name":"enthus1ast/nimja","owner":"enthus1ast","description":"typed and compiled template engine inspired by jinja2, twig and onionhammer/nim-templates for Nim.","archived":false,"fork":false,"pushed_at":"2025-02-23T17:24:50.000Z","size":1433,"stargazers_count":191,"open_issues_count":12,"forks_count":12,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-04T00:11:22.526Z","etag":null,"topics":["compile-time","compiled","html","html-templates","jinja2","nim","nim-lang","template-engine","twig","typed"],"latest_commit_sha":null,"homepage":"","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/enthus1ast.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-08T18:13:49.000Z","updated_at":"2025-03-30T17:46:33.000Z","dependencies_parsed_at":"2024-01-06T12:04:03.211Z","dependency_job_id":"491cda84-e412-43c6-88ef-78e866cc9fc9","html_url":"https://github.com/enthus1ast/nimja","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enthus1ast%2Fnimja","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enthus1ast%2Fnimja/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enthus1ast%2Fnimja/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enthus1ast%2Fnimja/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/enthus1ast","download_url":"https://codeload.github.com/enthus1ast/nimja/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247543589,"owners_count":20955865,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compile-time","compiled","html","html-templates","jinja2","nim","nim-lang","template-engine","twig","typed"],"created_at":"2024-07-31T09:00:44.880Z","updated_at":"2025-04-06T20:08:46.201Z","avatar_url":"https://github.com/enthus1ast.png","language":"Nim","funding_links":[],"categories":["Template engine","Web"],"sub_categories":["Template Engines"],"readme":"Nimja Template Engine\r\n=====================\r\n\r\n\u003cp align=\"center\"\u003e\r\n  \u003cimg style=\"max-width: 100%\" src=\"https://user-images.githubusercontent.com/13794470/133277541-01de699e-9699-4d8f-b65c-595bc309a1ee.png\"\u003e\r\n\u003c/p\u003e\r\n\r\n\r\ntyped and compiled template engine inspired by [jinja2](https://jinja.palletsprojects.com/), [twig](https://twig.symfony.com/) and [onionhammer/nim-templates](https://github.com/onionhammer/nim-templates) for Nim.\r\n\r\n[Support Nimja / Buy me a coffie 💌](https://donate.stripe.com/8wMdUqd7ieGdfYIbII)\r\n\r\nFEATURES\r\n========\r\n\r\n[![test](https://github.com/enthus1ast/nimja/actions/workflows/test.yml/badge.svg)](https://github.com/enthus1ast/nimja/actions/workflows/test.yml)\r\n\r\n- compiled\r\n- statically typed\r\n- extends (a master template)\r\n- control structures (if elif else / case / for / while)\r\n- import other templates\r\n- most nim code is valid in the templates\r\n- very fast:\r\n```\r\n# https://github.com/enthus1ast/dekao/blob/master/bench.nim\r\n# nim c --gc:arc -d:release -d:danger -d:lto --opt:speed -r bench.nim\r\nname ................. min time  avg time  std dv   runs\r\ndekao ................ 0.105 ms  0.117 ms  ±0.013  x1000\r\nkarax ................ 0.126 ms  0.132 ms  ±0.008  x1000\r\nhtmlgen .............. 0.021 ms  0.023 ms  ±0.004  x1000\r\nnimja ................ 0.016 ms  0.017 ms  ±0.001  x1000 \u003c--\r\nnimja iterator ....... 0.008 ms  0.009 ms  ±0.001  x1000 \u003c--\r\nscf .................. 0.023 ms  0.024 ms  ±0.003  x1000\r\nnim-mustache ......... 0.745 ms  0.790 ms  ±0.056  x1000\r\n```\r\n\r\nDOCUMENTATION\r\n=============\r\n\r\n- this readme\r\n- [generated nim docs](https://enthus1ast.github.io/nimja/nimja.html)\r\n- [my blogpost about Nimja](https://blog.code0.xyz/posts/nimja/)\r\n\r\n\r\n\r\nMOTIVATING EXAMPLE\r\n==================\r\n\r\n- [this example is in the example folder](https://github.com/enthus1ast/nimja/tree/master/examples/fromReadme)\r\n- [and a more complete prologue and jester example](https://github.com/enthus1ast/nimja/tree/master/examples/prologue)\r\n- [an example howto load templates from a shared library (dll, so)](https://github.com/enthus1ast/nimja/tree/master/examples/dynlib)\r\n\r\nserver.nim\r\n\r\n```nim\r\nimport asynchttpserver, asyncdispatch\r\nimport nimja/parser\r\nimport os, random # os and random are later used in the templates, so imported here\r\n\r\ntype\r\n  User = object\r\n    name: string\r\n    lastname: string\r\n    age: int\r\n\r\nproc renderIndex(title: string, users: seq[User]): string =\r\n  ## the `index.nimja` template is transformed to nim code.\r\n  ## so it can access all variables like `title` and `users`\r\n  ## the return variable could be `string` or `Rope` or\r\n  ## anything which has a `\u0026=`(obj: YourObj, str: string) proc.\r\n  compileTemplateFile(\"index.nimja\", baseDir = getScriptDir())\r\n\r\nproc main {.async.} =\r\n  var server = newAsyncHttpServer()\r\n\r\n  proc cb(req: Request) {.async.} =\r\n\r\n    # in the templates we can later loop trough this sequence\r\n    let users: seq[User] = @[\r\n      User(name: \"Katja\", lastname: \"Kopylevych\", age: 32),\r\n      User(name: \"David\", lastname: \"Krause\", age: 32),\r\n    ]\r\n    await req.respond(Http200, renderIndex(\"index\", users))\r\n\r\n  server.listen Port(8080)\r\n  while true:\r\n    if server.shouldAcceptRequest():\r\n      await server.acceptRequest(cb)\r\n    else:\r\n      poll()\r\n\r\nasyncCheck main()\r\nrunForever()\r\n```\r\n\r\nindex.nimja:\r\n\r\n```twig\r\n{% extends partials/_master.nimja%}\r\n{#\r\n  extends uses the master.nimja template as the \"base\".\r\n  All the `block`s that are defined in the master.nimja are filled\r\n  with blocks from this template.\r\n\r\n  If the templates extends another, all content HAVE TO be in a block.\r\n\r\n  blocks can have arbitrary names\r\n\r\n  extend must be the first token in the template,\r\n  only comments `{# Some foo #}` and strings are permitted to come before it.\r\n#}\r\n\r\n\r\n{% block content %}\r\n  {# A random loop to show off. #}\r\n  {# Data is defined here for demo purpose, but could come frome database etc.. #}\r\n  \u003ch1\u003eRandom links\u003c/h1\u003e\r\n  {% const links = [\r\n    (title: \"google\", target: \"https://google.de\"),\r\n    (title: \"fefe\", target: \"https://blog.fefe.de\")]\r\n  %}\r\n  {% for (ii, item) in links.pairs() %}\r\n    {{ii}} \u003ca href=\"{{item.target}}\"\u003eThis is a link to: {{item.title}}\u003c/a\u003e\u003cbr\u003e\r\n  {% endfor %}\r\n\r\n  \u003ch1\u003eMembers\u003c/h1\u003e\r\n    {# `users` was a param to the `renderIndex` proc #}\r\n    {% for (idx, user) in users.pairs %}\r\n        \u003ca href=\"/users/{{idx}}\"\u003e{% importnimja \"./partials/_user.nimja\" %}\u003c/a\u003e\u003cbr\u003e\r\n    {% endfor %}\r\n{% endblock %}\r\n\r\n{% block footer %}\r\n  {#\r\n    we can call arbitraty nim code in the templates.\r\n    Here we pick a random user from users.\r\n  #}\r\n  {% var user = users.sample() %}\r\n\r\n  {#\r\n    imported templates have access to all variables declared in the parent.\r\n    So `user` is usable in \"./partials/user.nimja\"\r\n  #}\r\n  This INDEX was presented by.... {% importnimja \"./partials/_user.nimja\" %}\r\n{% endblock footer %} {# the 'footer' in endblock is completely optional #}\r\n```\r\n\r\nmaster.nimja\r\n```twig\r\n{#\r\n\r\n  This template is later expanded from the index.nimja template.\r\n  All blocks are filled by the blocks from index.nimja\r\n\r\n  Variables are also useable.\r\n #}\r\n\u003chtml\u003e\r\n\u003chead\u003e\r\n  \u003ctitle\u003e{{title}}\u003c/title\u003e\r\n\u003c/head\u003e\r\n\u003cbody\u003e\r\n\r\n\u003cstyle\u003e\r\nbody {\r\n  background-color: aqua;\r\n  color: red;\r\n}\r\n\u003c/style\u003e\r\n\r\n{# The master can declare a variable that is later visible in the child template #}\r\n{% var aVarFromMaster = \"aVarFromMaster\" %}\r\n\r\n{# We import templates to keep the master small #}\r\n{% importnimja \"partials/_menu.nimja\" %}\r\n\r\n\u003ch1\u003e{{title}}\u003c/h1\u003e\r\n\r\n{# This block is filled from the child templates #}\r\n{%block content%}{%endblock%}\r\n\r\n\r\n{#\r\n  If the block contains content and is NOT overwritten later.\r\n  The content from the master is rendered\r\n#}\r\n{% block onlyMasterBlock %}Only Master Block{% endblock %}\r\n\r\n\u003cfooter\u003e\r\n  {% block footer %}{% endblock %}\r\n\u003c/footer\u003e\r\n\r\n\u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\npartials/_menu.nimja:\r\n```twig\r\n\u003ca href=\"/\"\u003eindex\u003c/a\u003e\r\n```\r\n\r\npartials/_user.nimja:\r\n```twig\r\nUser: {{user.name}} {{user.lastname}} age: {{user.age}}\r\n```\r\n\r\nBasic Syntax\r\n============\r\n\r\n- `{{ myObj.myVar }}` --transformed-to---\u003e  `$(myObj.myVar)`\r\n- {% myExpression.inc() %} --transformed-to---\u003e `myExpression.inc()`\r\n- {# a comment #}\r\n\r\n\r\n\r\nHow?\r\n====\r\n\r\nnimja transforms templates to nim code on compilation,\r\nso you can write arbitrary nim code.\r\n```nim\r\nproc foo(ss: string, ii: int): string =\r\n  compileTemplateStr(\r\n    \"\"\"example{% if ii == 1%}{{ss}}{%endif%}{% var myvar = 1 %}{% myvar.inc %}\"\"\"\r\n  )\r\n```\r\nis transformed to:\r\n\r\n```nim\r\nproc foo(ss: string; ii: int): string =\r\n  result \u0026= \"example\"\r\n  if ii == 1:\r\n    result \u0026= ss\r\n  var myvar = 1\r\n  inc(myvar, 1)\r\n```\r\n\r\nthis means you have the full power of nim in your templates.\r\n\r\n\r\nUSAGE\r\n=====\r\n\r\nthere are only three relevant procedures:\r\n\r\n- `compileTemplateStr(str: string)`\r\n  compiles a template string to nim ast\r\n- `compileTemplateFile(path: string)`\r\n  compiles the content of a file to nim ast\r\n- `getScriptDir()`\r\n  returns the path to your current project, on compiletime.\r\n\r\ncompileTemplateFile\r\n-------------------\r\n\r\n`compileTemplateFile` transforms the given file into the nim code.\r\nyou should use it like so:\r\n\r\n```nim\r\nimport os # for `/`\r\nproc myRenderProc(someParam: string): string =\r\n  compileTemplateFile(\"myFile.html\", baseDir = getScriptDir())\r\n\r\necho myRenderProc(\"test123\")\r\n```\r\n\r\n`compileTemplateFile` can also generate an iterator body, for details look at the\r\niteratior section.\r\n\r\n`compileTemplateFile` (also `compileTemplateString`) generates the body of a proc/iterator so it generates\r\nassign calls to a variable. The default is `result`.\r\nIf you want it to use another variable set it in `varname`\r\n\r\nalso look at:\r\n- [tmplf](#tmpls--tmplf) (inline version of this)\r\n- [tmpls](#tmpls--tmplf)\r\n- [compileTemplateStr](#compiletemplatestr)\r\n\r\n\r\ncompileTemplateStr\r\n-------------------\r\n\r\n`compileTemplateStr` compiles the given string into nim code.\r\n\r\n\r\n```nim\r\nproc myRenderProc(someParam: string): string =\r\n  compileTemplateStr(\"some nimja code {{someParam}}\", baseDir = getScriptDir())\r\n\r\necho myRenderProc(\"test123\")\r\n```\r\n\r\n`compileTemplateStr` can also generate an iterator body, for details look at the\r\niteratior section.\r\n\r\n\r\n`compileTemplateString` (also `compileTemplateFile`) generates the body of a proc/iterator so it generates\r\nassign calls to a variable. The default is `result`.\r\nIf you want it to use another variable set it in `varname`\r\n\r\n`baseDir` is needed when you want to import/extend templates!\r\n\r\nA context can be supplied to the `compileTemplateString` (also `compileTemplateFile`), to override variable names:\r\n\r\n```nim\r\nblock:\r\n  type\r\n    Rax = object\r\n      aa: string\r\n      bb: float\r\n  var rax = Rax(aa: \"aaaa\", bb: 13.37)\r\n  var foo = 123\r\n  proc render(): string =\r\n    compileTemplateString(\"{{node.bb}}{{baa}}\", {node: rax, baa: foo})\r\n```\r\n\r\nPlease note, currently the context **cannot be** procs/funcs etc.\r\n\r\nalso look at:\r\n- [tmpls](#tmpls--tmplf) (inline version of this)\r\n- [tmplf](#tmpls--tmplf)\r\n- [compileTemplateFile](#compiletemplatefile)\r\n\r\nif / elif / else\r\n-----------------\r\n\r\n```twig\r\n{% if aa == 1 %}\r\n  aa is: one\r\n{% elif aa == 2 %}\r\n  aa is: two\r\n{% else %}\r\n  aa is something else\r\n{% endif %}\r\n```\r\n\r\nwhen / elif / else\r\n-----------------\r\n\r\n`when` is the compile time if statement.\r\nIt has the same semantic than if\r\n\r\n```twig\r\n{% when declared(isDeclared) %}\r\n  isDeclared\r\n{% elif true == true %}\r\n  true\r\n{% else %}\r\n  something else\r\n{% endwhen %}\r\n```\r\n\r\ncase / of / else\r\n-----------------\r\n(Since Nimja 0.8.1)\r\n\r\n`case` has the same semantic as the [nim case statement](https://nim-lang.org/docs/tut1.html#control-flow-statements-case-statement).\r\nUse `case` for example if you want to make sure that all cases are handled.\r\nIf not all cases are covered, an error is generated.\r\n\r\n\r\n```twig\r\n{%- case str -%}\r\n{%- of \"foo\" -%}\r\n  foo\r\n{%- of \"baa\" -%}\r\n  baa\r\n{%- of \"baz\" -%}\r\n  baz\r\n{%- else -%}\r\n  nothing\r\n{%- endcase -%}\r\n```\r\n\r\ntmpls / tmplf\r\n-------------\r\n\r\n`compileTemplateStr` and `compileTemplateFile` both need a surrounding proc.\r\n`tmpls` (template str) and `tmplf` (template file) are a shorthand for these\r\nsituations where you want to inline a render call.\r\n\r\n```nim\r\nlet leet = 1337\r\necho tmpls(\"foo {{leet}}\")\r\necho tmplf(\"templates\" / \"myfile.nimja\", baseDir = getScriptDir())\r\n```\r\n\r\nA context can be supplied to the template, to override variable names:\r\n\r\n```nim\r\nblock:\r\n  type\r\n    Rax = object\r\n      aa: string\r\n      bb: float\r\n  var rax = Rax(aa: \"aaaa\", bb: 13.37)\r\n  var foo = 123\r\n  tmpls(\"{{node.bb}}{{baa}}\", {node: rax, baa: foo})\r\n```\r\n\r\nPlease note, currently the context **cannot be** procs/funcs etc.\r\n\r\n\r\n\r\nfor\r\n---\r\n\r\n```twig\r\n{% for (cnt, elem) in @[\"foo\", \"baa\", \"baz\"].pairs() %}\r\n  {{cnt}} -\u003e {{elem}}\r\n{% endfor %}\r\n```\r\n\r\n```twig\r\n{% for elem in someObj.someIter() %}\r\n  {# `elem` is accessible from the \"some/template.nimja\" #}\r\n  {# see importnimja section for more info #}\r\n  {% importnimja \"some/template.nimja\" %}\r\n{% endfor %}\r\n```\r\n\r\nwhile\r\n----\r\n\r\n```twig\r\n{% while isTrue() %}\r\n  still true\r\n{% endwhile %}\r\n```\r\n\r\n```twig\r\n{% var idx = 0 %}\r\n{% while idx \u003c 10 %}\r\n  still true\r\n  {% idx.inc %}\r\n{% endwhile %}\r\n```\r\n\r\ncomments\r\n-------\r\n\r\n```twig\r\n{# single line comment #}\r\n{#\r\n  multi\r\n  line\r\n  comment\r\n#}\r\n{# {% var idx = 0 %} #}\r\n```\r\n\r\n\"to string\" / output\r\n--------------------\r\n\r\ndeclare your own `$` before you call\r\n`compileTemplateStr()` or `compileTemplateFile()`\r\nfor your custom objects.\r\nFor complex types it is recommend to use the method described in the `importnimja` section.\r\n```twig\r\n{{myVar}}\r\n{{someProc()}}\r\n```\r\n\r\nimportnimja\r\n---------\r\n\r\nimport the content of another template.\r\nThe imported template has access to the parents variables.\r\nSo it's a valid strategy to have a \"partial\" template that for example\r\ncan render an object or a defined type.\r\nThen include the template wherever you need it:\r\n\r\nbest practice is to have a `partials` folder,\r\nand every partial template begins with an underscore \"_\"\r\nall templates are partial that do not extend another\r\ntemplate and therefore can be included.\r\n\r\nThis way you create reusable template blocks to use all over your webpage.\r\n\r\n(Since Nimja 0.9.0) If you import other templates, make sure to use the `baseDir` param with \r\n`tmpls`, `tmplf`, `compileTemplateString` and `compileTemplateFile`.\r\n\r\n(Since Nimja 0.10.0) You can also use the [template fragment](#template-fragments) feature.\r\n\r\n\r\npartials/_user.nimja:\r\n```twig\r\n\u003cdiv class=\"col-3\"\u003e\r\n  \u003ch2\u003e{{user.name}}\u003c/h2\u003e\r\n  \u003cul\u003e\r\n    \u003cli\u003eAge: {{user.age}}\u003c/li\u003e\r\n    \u003cli\u003eLastname: {{user.lastname}}\u003c/li\u003e\r\n  \u003c/ul\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\npartials/_users.nimja:\r\n```twig\r\n\u003cdiv class=\"row\"\u003e\r\n  {% for user in users: %}\r\n    {% importnimja \"partials/_user.nimja\" %}\r\n  {% endfor %}\r\n\u003c/div\u003e\r\n```\r\n\r\nextends\r\n-------\r\n\r\na child template can extend a master template.\r\nSo that placeholder blocks in the master are filled\r\nwith content from the child.\r\n\r\nBasically the rendered template chooses its \"sourrounding\" template.\r\n\r\n(Since Nimja 0.9.0) If you extend other templates, make sure to use the `baseDir` param with \r\n`tmpls`, `tmplf`, `compileTemplateString` and `compileTemplateFile`.\r\n\r\npartials/_master.nimja\r\n```twig\r\n\u003chtml\u003e\r\n\u003cbody\u003e\r\nA lot of boilerplate\r\n{% block content %}{% endblock %}\r\n\u003chr\u003e\r\n{% block footer %}{% endblock %}\r\n\u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\nchild.nimja\r\n```\r\n{% extends \"partials/_master.nimja\" %}\r\n{% block content %}I AM CONTENT{% endblock %}\r\n{% block footer %}...The footer..{% endblock %}\r\n```\r\n\r\nif the child.nimja is compiled then rendered like so:\r\n\r\n```nim\r\nproc renderChild(): string =\r\n  compileTemplateFile(\"child.nimja\", baseDir = getScriptDir())\r\n\r\necho renderChild()\r\n```\r\n\r\noutput:\r\n```html\r\n\u003chtml\u003e\r\n\u003cbody\u003e\r\nA lot of boilerplate\r\nI AM CONTENT\r\n\u003chr\u003e\r\n...The footer..\r\n\u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\nscope\r\n--------\r\n\r\nA `scope` has the same semantic as a nim block.\r\nVariables declared inside a scope are not visible\r\non the outside. You can use this for code hygiene.\r\n\r\n```twig\r\n{% scope %}\r\n  {% let httpMethod = \"POST\" %}\r\n  {{ httpMethod }}\r\n{% endscope %}\r\n{# httpMethod is not accesible any more, you can define it again. #}\r\n{% let httpMethod = \"FOO\" %}\r\n{{ httpMethod }}\r\n```\r\n\r\nYou can break out of a named scope prematurely\r\n\r\n```\r\n{%- scope foo -%}\r\n  foo\r\n  {%- break foo -%}\r\n  baa\r\n{%- endscope -%}\r\n```\r\n\r\nin this case only \"foo\" is printed.\r\n\r\n`self` variable\r\n===============\r\n\r\nJinja describes them like so, we can do the same:\r\n\r\nYou can't define multiple {% block %} tags with the same name in the same template.\r\nThis limitation exists because a block tag works in \"both\" directions.\r\nThat is, a block tag doesn't just provide a placeholder to fill - it also defines the content that fills the placeholder in the parent.\r\nIf there were two similarly-named {% block %} tags in a template, that template's parent wouldn't know which one of the blocks content to use.\r\n\r\nIf you want to print a block multiple times, you can, however, use the special self variable and call the block with that name:\r\n\r\n```twig\r\n\u003ctitle\u003e{% block title %}{% endblock %}\u003c/title\u003e\r\n\u003ch1\u003e{{ self.title }}\u003c/h1\u003e\r\n{% block body %}{% endblock %}\r\n```\r\n\r\nTo change the `specialSelf` variable name compile with eg.:\r\n\r\n```\r\nnim c -d:specialSelf=\"blocks.\" file.nim\r\n```\r\n\r\n\r\nprocedures (macro)\r\n========\r\n\r\nProcedures can be defined like so:\r\n\r\n```twig\r\n{% proc foo(): string = %}\r\n  baa\r\n{% endproc %}\r\n{{ foo() }}\r\n```\r\n\r\n```twig\r\n{% proc input(name: string, value=\"\", ttype=\"text\"): string = %}\r\n    \u003cinput type=\"{{ ttype }}\" value=\"{{ value }}\" name=\"{{ name }}\"\u003e\r\n{% endproc %}\r\n{{ input(\"name\", \"value\", ttype=\"text\") }}\r\n```\r\n\r\nFunc's have the same semantic as nim funcs, they are not allowed to have a side effect.\r\n\r\n```twig\r\n{% func foo(): string = %}\r\n  baa\r\n{% endfunc %}\r\n{{ foo() }}\r\n```\r\n\r\n`macro` is an alias for `proc`\r\n\r\n```twig\r\n{% macro textarea(name, value=\"\", rows=10, cols=40): string = %}\r\n    \u003ctextarea name=\"{{ name }}\" rows=\"{{ rows }}\" cols=\"{{ cols\r\n        }}\"\u003e{{ value }}\u003c/textarea\u003e\r\n{% endmacro %}\r\n{{ textarea(\"name\", \"value\") }}\r\n```\r\n\r\nfor `{{func}}` `{{proc}}` and `{{macro}}` either the `{{end}}` tag or\r\nthe `{{endfunc}}` `{{endproc}}` `{{endmacro}}` are valid closing tags.\r\n\r\nImporting func/proc/macro from a file\r\n------------------------------------\r\n\r\nImporting works like any other ordinary Nimja templates with `ìmportnimja`.\r\nGood practice is to define procs with the \"whitespacecontrol\":\r\n\r\nmyMacros.nimja\r\n```\r\n{%- proc foo(): string = %}foo{% end -%}\r\n{%- proc baa(): string = %}baa{% end -%}\r\n```\r\n\r\nmyTemplate.nimja\r\n```\r\n{% importnimja \"myMacros.nimja\" %}\r\n```\r\n\r\nWhen a template `extends` another template, `importnimja` statements must be\r\nin a `block` they cannot stand on their own.\r\nIt might be a good idea to import these \"library templates\" in\r\nthe extended template (eg.: master.nimja).\r\n\r\nIterator\r\n========\r\n\r\nExpanded template bodies can also be created as an iterator,\r\ntherefore the generated strings are not concatenated to the result\r\n`result \u0026= \"my string\"` but are yielded.\r\n\r\nThis could be used for streaming templates, or to save memory when a big template is rendered and the http server can send data in chunks:\r\n\r\n```nim\r\niterator yourIter(yourParams: bool): string =\r\n  compileTemplateString(\"{%for idx in 0 .. 100%}{{idx}}{%endfor%}\", iter = true)\r\n\r\nfor elem in yourIter(true):\r\n  echo elem\r\n```\r\n\r\nWhitespace Control\r\n==================\r\n\r\n```twig\r\n###############\r\n{% if true %}\r\n  \u003cli\u003e   {{foo}}   \u003c/li\u003e\r\n{% endif %}\r\n###############\r\n```\r\nis expanded to:\r\n\r\n```html\r\n###############\r\n\r\n  \u003cli\u003e   FOO   \u003c/li\u003e\r\n\r\n###############\r\n```\r\n\r\nthe nimja template control statements leave their newline and whitespace when rendered.\r\nTo fix this you can annotate them with \"-\":\r\n\r\n```twig\r\n###############\r\n{% if true -%}\r\n  \u003cli\u003e   {{-foo-}}   \u003c/li\u003e\r\n{%- endif %}\r\n###############\r\n```\r\n\r\n```html\r\n###############\r\n\u003cli\u003eFOO\u003c/li\u003e\r\n###############\r\n```\r\n\r\nTemplate Fragments\r\n==================\r\n\r\n(Since Nimja 0.10.0)\r\n\r\nAll the render procs (`tmpls`, `tmplf`, `compileTemplateString` and `compileTemplateFile`).\r\nHave a parameter `blockToRender`.\r\n\r\n```\r\nproc render(): string =\r\n    compileTemplateStr(\"not rendered{% block foo %}foo{% endblock %}\", \r\n        blockToRender = \"foo\", baseDir = getScriptDir())\r\n\r\ntmpls(\"not rendered{% block foo %}foo{% endblock %}\", blockToRender = \"foo\", baseDir = getScriptDir())\r\n\r\n\r\nproc render(): string =\r\n    compileTemplateFile(\"template.nimja\", \r\n        blockToRender = \"foo\", baseDir = getScriptDir())\r\n\r\ntmplf(\"template.nimja\", blockToRender = \"foo\", baseDir = getScriptDir())\r\n\r\n```\r\n\r\nIf set only this block of a larger template is rendered.\r\n\r\nImagine this example where we use htmx:\r\n\r\npage.nimja\r\n```twig\r\n\u003chtml\u003e\r\n    \u003cheader\u003e\r\n        \u003cscript src=\"https://unpkg.com/htmx.org@2.0.2\"\u003e\u003c/script\u003e\r\n    \u003c/header\u003e\r\n    \u003cbody\u003e\r\n        {% block content %}\r\n            \u003ch1\u003eHello\u003c/h1\u003e\r\n            {% block button %}\r\n              \u003cdiv id=\"buttons\"\u003e\r\n                \u003cbutton hx-post=\"/clicked/up\" hx-target=\"#buttons\" hx-swap=\"outerHTML\" {% if button == 10%}disabled{% endif %} \u003e\r\n                  UP Click Me {{button}}\r\n                \u003c/button\u003e\r\n                {%- if button == 10%}you klicked enough!!{% endif -%}\r\n              \u003c/div\u003e\r\n            {% endblock %}\r\n        {% endblock %}\r\n    \u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\n\r\nWhen you render the whole page, everything is rendered.\r\nGood for the first visit of the site.\r\n\r\n```nim\r\ntmplf(\"page.nimja\", baseDir = getScriptDir())\r\n```\r\n\r\nWhen you later want to update the button with htmx, you need only the \"block button\"\r\n\r\n```nim\r\ntmplf(\"page.nimja\", blockToRender = \"button\" baseDir = getScriptDir())\r\n```\r\n\r\nthen only this part is rendered:\r\n\r\n```twig\r\n{% block button %}\r\n  \u003cdiv id=\"buttons\"\u003e\r\n    \u003cbutton hx-post=\"/clicked/up\" hx-target=\"#buttons\" hx-swap=\"outerHTML\" {% if button == 10%}disabled{% endif %} \u003e\r\n      UP Click Me {{button}}\r\n    \u003c/button\u003e\r\n    {%- if button == 10%}you klicked enough!!{% endif -%}\r\n  \u003c/div\u003e\r\n{% endblock %} \r\n```\r\n\r\nYou can also use this to get rid of some of your partials templates.\r\nFor example, with this you can combine partials and extended templates:\r\n\r\nuser.nimja\r\n```twig\r\n{% extends partials/_base.nimja %}\r\n{% block content %}\r\n    Information about the user.\r\n\r\n    {% block user %}\r\n        \u003cdiv id=\"user\"\u003e\r\n            {{user.firstName}}\r\n            {{user.lastName}}\r\n        \u003c/div\u003e\r\n    {% endblock %}\r\n\r\n    also visit \u003ca href=\"/users/\"\u003eother user\u003c/a\u003e!\r\n{% endblock %}\r\n```\r\n\r\nSo a \"users detail page\" would render the whole thing.\r\n\r\n```nim\r\ntmplf(\"user.nimja\", baseDir = getScriptDir())\r\n```\r\n                               \r\nBut if you want to display a user somewhere else, you can just render the `user` block:\r\n\r\nuserlist.nimja\r\n```twig\r\n\u003cul\u003e\r\n    {% for user in db.getUsers %}\r\n        \u003cli\u003e\r\n            {% importnimja \"user.nimja\" \"user\" %}\r\n        \u003c/li\u003e\r\n    {% endfor %}\r\n\u003c/ul\u003e\r\n```\r\n\r\nNimjautils\r\n==========\r\n\r\nThe optional `nimjautils` module, implements some convenient procedures.\r\n\r\n```nim\r\nimport nimja/nimjautils\r\n```\r\n\r\nMainly:\r\n\r\nLoop variable/iterator\r\n-------------\r\n\r\nyields a `Loop` object with every item.\r\nInside the loop body you have access to the following fields.\r\nUnlike jinja2 or twig where the loop variable is implicitly bound and available, we must use the `loop()` iterator explicity.\r\n\r\n```twig\r\n{% for (loop, row) in rows.loop() %}\r\n    {{ loop.index0 }} {# which elemen (start from 0) #}\r\n    {{ loop.index }} {# which element (start from 1) #}\r\n    {{ loop.revindex0 }} {# which element, counted from the end (last one is 0) #}\r\n    {{ loop.revindex }} {# which element, counted from the end (last one is 1) #}\r\n    {{ loop.length }} {# the length of the seq, (same as mySeq.len()) #}\r\n    {% if loop.first %}The first item{% endif %} {# if this is the first loop iteration #}\r\n    {% if loop.last %}The last item{% endif %} {# if this is the last loop iteration #}\r\n    {% if loop.previtem.isSome() %}{{ loop.previtem.get() }}{% endif %} {# get the item from the last loop iteration #}\r\n    {% if loop.nextitem.isSome() %}{{ loop.nextitem.get() }}{% endif %} {# get the item from the next loop iteration #}\r\n    \u003cli class=\"{{ loop.cycle(@[\"odd\", \"even\"]) }}\"\u003e{{row}}\u003c/li\u003e\r\n{% endfor %}\r\n```\r\n\r\n~~however, the element you iterate over must match the Concept `Loopable`.~~ https://github.com/enthus1ast/nimja/issues/23\r\nThis means you can propably not use `loop()` with an iterator, since they do not have a `len()` and `[]`\r\n\r\nCycle\r\n-----\r\n\r\nwithin a loop you can cycle through elements:\r\n\r\n```twig\r\n{% for (loop, row) in rows.loop() %}\r\n    \u003cli class=\"{{ loop.cycle(@[\"odd\", \"even\"]) }}\"\u003e{{ row }}\u003c/li\u003e\r\n{% endfor %}\r\n```\r\n\r\n'~' (tilde)\r\n----------\r\n\r\nConverts all operands into strings and concatenates them.\r\nlike: `$aa \u0026 $bb`\r\n\r\n```twig\r\n{{ \"Hello \" ~ name ~ \"!\" }}\r\n```\r\n\r\nwould return (assuming name is set to 'Nim') Hello Nim!.\r\n\r\nincludeRaw\r\n----------\r\nIncludes the content of a file literally without any parsing\r\nGood for documentation etc..\r\n\r\n```nim\r\nproc test(): string =\r\n  let path = (\"tests/basic\" / \"includeRawT.txt\", baseDir = getScriptDir())\r\n  compileTemplateStr(\"\"\"pre{{ includeRaw(path) }}suf\"\"\")\r\n```\r\n\r\nraw strings\r\n-----------\r\nto include raw strings, or nimja code itself to a template (for documentation purpose),\r\nyou could use this construct `{{\"raw code\"}}`\r\n\r\n```nim\r\nproc foo(): string =\r\n  compileTemplateStr(\"\"\"\r\n    foo {{\"{%if true%}baa{%endif%}\"}}\r\n  \"\"\")\r\n```\r\nthis would then be rendered like so:\r\n\r\n```\r\nfoo {%if true%}baa{%endif%}\r\n```\r\n\r\nincludeRawStatic\r\n----------------\r\nIncludes the content of a file literally without any parsing, on compiletime.\r\nThis means it is included into the executable.\r\n\r\nincludeStaticAsDataurl\r\n----------------------\r\nIncludes the content of a file on compile time, it is converted to a data url.\r\nEg:\r\n\r\n```html\r\n  \u003cimg src=\"{{includeStaticAsDataurl(getScriptDir() / \"logo.jpg\")}}\"\u003e\r\n```\r\n\r\n  is transformed to:\r\n\r\n```html\r\n  \u003cimg src=\"data:image/jpeg;charset=utf-8;base64,/9j/4AAQSkZJRg...\"/\u003e\r\n```\r\n\r\n\r\ntruncate\r\n--------\r\n\r\ntruncates a string to \"num\" characters.\r\nwhen the string was truncated it appends the `suf` to the text.\r\nif `preserveWords` is true it will not cut words in half but\r\nthe output string could be shorter than `num` characters.\r\n\r\n```nim\r\nproc truncate*(str: string, num: Natural, preserveWords = true, suf = \"...\"): string\r\n```\r\n\r\n```nim\r\nlet lorem = \"Lorem ipsum, dolor sit amet consectetur adipisicing elit. Rem voluptates odio tempore voluptas beatae eum consequatur laudantium totam. Delectus fuga eveniet ab cum nulla aperiam iste ducimus odio fugit voluptas.\"\r\n\r\nproc test(lorem: string): string =\r\n  compileTemplateStr(\"{{lorem.truncate(65)}}\")\r\nassert test(lorem) == \"Lorem ipsum, dolor sit amet consectetur adipisicing elit. Rem...\"\r\n```\r\n\r\nnl2br\r\n-----\r\n\r\nConverts newline to `\u003cbr\u003e`.\r\nIf keepNL == true, the one `\\n` is replaced by `\u003cbr\u003e\\n` thus keeping the newlines.\r\n\r\n```nim\r\nfunc nl2br*(str: string, keepNl = true): string =\r\n```\r\n\r\n```nim\r\nassert \"foo\\nbaa\".nl2br == \"foo\u003cbr\u003e\\nbaa\"\r\n```\r\n\r\nspaceless\r\n---------\r\n\r\nRemoves unneeded whitespaces between html tags,\r\nwarning, this is NOT smart. So it will destroy `\u003ctextarea\u003e` and `\u003cpre\u003e` content!\r\n\r\n```nim\r\n  check \"\u003cfoo\u003e\\n\\nbaa  \u003c/foo\u003e\".spaceless == \"\u003cfoo\u003e baa \u003c/foo\u003e\"\r\n  check \"\u003cfoo tag='tag tag'\u003e\\n\\nbaa  \u003c/foo\u003e\".spaceless == \"\u003cfoo tag='tag tag'\u003e baa \u003c/foo\u003e\"\r\n  check \"\u003cfoo\u003ebaa  baz\u003c/foo\u003e\".spaceless == \"\u003cfoo\u003ebaa baz\u003c/foo\u003e\"\r\n```\r\n\r\nslugify\r\n-------\r\n\r\nconverts any string to an url friendly one.\r\nRemoves any special chars and replaces non ASCII runes to their ASCII representation.\r\n\r\n```nim\r\nslugify(\"Lession learned german umlauts: öüä\")\r\n```\r\n\r\nwill output:\r\n\r\n```lession-learned-german-umlauts-oua```\r\n\r\n\r\n```nim\r\nlet allowedCharsInSlug = Letters + Digits\r\nproc slugify*(str: string, sperator = \"-\", allowedChars = allowedCharsInSlug): string =\r\n```\r\n\r\nshorthand if `?`\r\n----------------\r\n\r\na shorthand for a condition, this could be used for example\r\nto toggle html classes:\r\n\r\n```nim\r\nproc foo(isDisabled: bool): string =\r\n  compileTemplateStr(\"\"\"{% ?isDisabled: \"disabled\" %}\"\"\")\r\ncheck \"disabled\" == foo(true)\r\ncheck \"\" == foo(false)\r\n```\r\n\r\nfilter `|`\r\n---------\r\n\r\n`a | b` is an alias to `a.b` this is often used in other template engines.\r\n\r\n```nim\r\nproc foo(): string =\r\n  compileTemplateStr(\"\"\"{{\"foo baa baz\" | slugify}}\"\"\")\r\ncheck foo() == \"foo-baa-baz\"\r\n```\r\n\r\n\r\nWant to hack?\r\n-------------\r\n\u003e if you need more utils in nimjautils, please PR!\r\n\u003e they should all be quite easy to implement,\r\n\u003e so they make up a good first issue/pull request!\r\n\u003e\r\n\u003ea good inspiration WHAT to hack is jinja and twig filters.\r\n\r\n\r\nCompile / Use\r\n=============\r\n\r\nThis is a COMPILED template engine.\r\nThis means you must _recompile_ your application\r\nfor every change you do in the templates!\r\n\r\n~~_Automatic recompilation / hot code reloading / dynamic execution is a [planned feature](https://github.com/enthus1ast/nimja/issues/6)._~~ see the\r\n`Automatic Recompilation / Hot Code Reloading (hcr)` section\r\n\r\n```bash\r\nnim c -r yourfile.nim\r\n```\r\n\r\nsometimes, nim does not catch changes to template files.\r\nThen compile with \"-f\" (force)\r\n\r\n```bash\r\nnim c -f -r  yourfile.nim\r\n```\r\n\r\nAutomatic Recompilation / Hot Code Reloading (hcr)\r\n============================================\r\n\r\n(Still an experimental feature, help wanted.)\r\nAutomatic Recompilation enables you to change your templates and without\r\nrecompiling your application, see the changes lives.\r\n\r\nHow it works:\r\n\r\nNimja compiles your templates (and template render functions)\r\nto a shared library (.so/.dll/.dynlib), then your host application loads\r\nthis library, then on source code change, the shared library is unloaded from\r\nyour host, recompiled, and loaded again.\r\n\r\nThis is normally way faster, than recompiling your whole application.\r\n\r\nFor this to work, Nimja now contains a small file watcher, you must utilize this\r\ntool in your own application.\r\n\r\nYou also must restructure you application a little bit,\r\nall you render functions must be in a separate file,\r\nthis file is then compiled to a shared lib and loaded by your host.\r\n\r\nWhen you go live later, you can just disable the recompilation,\r\nand compile the shared library for release, it should be very fast as well.\r\n\r\nBelow is a minimal example, [a more complete example is in the example folder](https://github.com/enthus1ast/nimja/tree/master/examples/hcr)\r\n\r\nMinimal example:\r\n\r\n`host.nim`\r\n\r\n```nim\r\n# this is the file that eg. implements your webserver and loads\r\n# the templates as a shared lib.\r\nimport nimja/hcrutils # Nimja's hot code reloading utilities\r\nimport jester, os\r\n\r\n# We watch the templates folder for change (and also tmpls.nim implicitly)\r\nvar cw = newChangeWatcher(@[getAppDir() / \"templates/\"])\r\nasyncCheck cw.recompile() # if a change is detected we recompile tmpls.nim\r\n\r\ntype\r\n    # You must declare the proc definition from your tmpls.nim here as well.\r\n    ProcNoParam = proc (): string {.gcsafe, stdcall.}\r\n    ProcId = proc (id: string): string {.gcsafe, stdcall.}\r\n\r\nroutes:\r\n  get \"/\":\r\n    resp dyn(ProcNoParam, \"index\")\r\n\r\n  get \"/id/@id\":\r\n    resp dyn(ProcId, \"detail\", @\"id\")\r\n```\r\n\r\n\r\n`tmpls.nim`\r\n\r\n```nim\r\n# this file contains you render functions\r\n# is compiled to a shared lib and loaded by your host application\r\n# to keep compilation fast, use this file only for templates.\r\n# this file is also watched by the filewatcher.\r\n# It can also be changed dynamically!\r\nimport nimja\r\nimport os # for `/`\r\n\r\nproc index*(): string {.exportc, dynlib.} =\r\n  var foos =  1351 # change me i'm dynamic :)\r\n  compileTemplateFile(\"templates/index.nimja\", baseDir = getScriptDir())\r\n\r\nproc detail*(id: string): string {.exportc, dynlib.} =\r\n  compileTemplateFile(\"templates/detail.nimja\", baseDir = getScriptDir())\r\n\r\n```\r\n\r\n`templates/`\r\n\r\n`templates/partials/_master.nimja`\r\n\r\n```html\r\n\u003chead\u003e\r\n  \u003ctitle\u003eHello, world!\u003c/title\u003e\r\n\u003c/head\u003e\r\n\u003cbody\u003e\r\n  \u003ch1\u003e\u003ca href=\"/\"\u003eNimja dynamic test\u003c/a\u003e\u003c/h1\u003e\r\n  \u003cdiv\u003e\r\n    {% block content %}{% endblock %}\r\n  \u003c/div\u003e\r\n  \u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\n\r\n`templates/index.nimja`\r\n```html\r\n{% extends \"templates/partials/_master.nimja\" %}\r\n{% block content %}\r\n\r\n\u003ch1\u003eHello, world! {{foos}}\u003c/h1\u003e\r\n\r\nindex\r\n\r\n{% for idx in 0..100 %}\r\n  \u003ca href=\"/id/{{idx}}\"\u003e{{idx}}\u003c/a\u003e\r\n{%- endfor %}\r\n\r\n{% endblock %}\r\n\r\n```\r\n\r\n\r\n`templates/detail.nimja`\r\n```html\r\n{% extends \"templates/partials/_master.nimja\" %}\r\n{% block content %}\r\ndetail\r\n\u003ca href=\"/id/{{id}}\"\u003e{{id}}\u003c/a\u003e\r\n{% endblock %}\r\n\r\n```\r\n\r\nyou can now change any of the templates or the `tmpls.nim`\r\nfiles.\r\nLater if you wanna go live, comment out the\r\n\r\n```\r\nasyncCheck cw.recompile() # if a change is detected we recompile tmpls.nim\r\n```\r\n\r\nline.\r\n\r\n\r\nNimja Template VSCode Syntax Color Formatting\r\n============================================\r\nIf you are using VSCode to develop your nim app,\r\nyou can still associate nimja template files for color syntax and formating with vscode as an html file.\r\nAdd this segment to your settings.json in vscode:\r\n\r\n```json\r\n  \"files.associations\": {\r\n    \"*.nwt\": \"html\", // Nimja deprecated templates\r\n    \"*.nimja\": \"html\", // Nimja new templates\r\n  },\r\n```\r\n\r\nDebugging\r\n=====================\r\n\r\n\r\n```bash\r\nnim c -d:dumpNwtAst -r yourfile.nim # \u003c-- dump NwtAst\r\nnim c -d:dumpNwtAstPretty -r yourfile.nim # \u003c-- dump NwtAst as pretty json\r\nnim c -d:nwtCacheOff -r yourfile.nim   # \u003c-- disables the NwtNode cache\r\nnim c -d:noPreallocatedString -r yourfile # \u003c-- do not preallocate the output string\r\nnim c -d:noCondenseStrings -r yourfile.nim # \u003c-- disables string condense see #12\r\nnim c -d:dumpNwtMacro -r yourfile.nim # \u003c-- dump generated Nim macros\r\n```\r\n\r\n\r\nChangelog\r\n=========\r\n\r\n## TODO\r\n- 0.?.?\r\n  - Added context to `importnimja`\r\n## DONE\r\n- 0.10.0\r\n  - Possible Breaking Change.\r\n  - [Template fragments](#template-fragments) (good for htmx); Render specific blocks from a template all procs (`tmpls`, `tmplf`, `compileTemplateString` and `compileTemplateFile`) got a \"blockToRender\" parameter, when set, only the given block is rendered.\r\n- 0.9.0\r\n  - BREAKING CHANGE!\r\n  - in order to fix #15 \u0026 #89 and to enable nimja components imported from other modules,\r\n    all proc (`tmpls`, `tmplf`, `compileTemplateString` and `compileTemplateFile`) got a `baseDir` param:\r\n    ```\r\n        compileTemplateStr(\"\"\"{{importnimja \"some/template.nimja\"}}\"\"\", baseDir = getScriptDir())\r\n        tmpls(\"\"\"{{importnimja \"some/template.nimja\"}}\"\"\", baseDir = getScriptDir())\r\n        compileTemplateFile(\"some/template.nimja\", baseDir = getScriptDir())\r\n        tmpls(\"some/template.nimja\", baseDir = getScriptDir())\r\n    ```\r\n    The use of `tmplf(getScriptDir() / \"foo.nimja\")` is discourage, use  `tmplf(\"foo.nimja\", baseDir = getScriptDir())` instead. \r\n    The old way could still work in some cirumstances though. \r\n    But its neccesary if you plan to import your nimja template code into other code.\r\n- 0.8.7\r\n  - Removed unused `NImport`.\r\n  - Error on uneven `when` blocks.\r\n- 0.8.6\r\n- 0.8.5 Nimja exports os, for `/`\r\n- 0.8.4 Fixed string escaping.\r\n- 0.8.3 Added `scope` and `endscope`\r\n- 0.8.2 Readme fixes and tests for `break` and `continue` for the for loop\r\n- 0.8.1 Added `case` `of` and `endcase`\r\n- 0.8.0\r\n  - Breaking change!\r\n  - Changed context to template syntax context = {foo: baa}\r\n  - Added context to `compileTemplateStr` and `compileTemplateFile`\r\n- 0.7.0 Added context to `tmpls` and `tmplf`\r\n- 0.6.9 Added `when` compile time if\r\n- 0.6.8 Added `importnimja` deprecated `importnwt` (importnwt is still valid for now)\r\n- 0.6.7 Removed the \".nwt\" extention everywhere, we go with \".nimja\" now.\r\n- 0.6.6 Preallocate the minimal known output length if `result` is string.\r\n- 0.6.5 Condense strings of extended templates (less assigns -\u003e better runtime performance).\r\n- 0.6.1 No codegen for empty string nodes after whitespaceControl.\r\n- 0.5.6 Added `{{endfunc}}` `{{endproc}}` `{{endmacro}}` for consistency.\r\n- 0.5.5 Added `tmpls` and `tmplf` procs to use inline.\r\n- 0.5.1 Added self variable, to print blocks multiple times\r\n- 0.5.0 Added hot code reloading.\r\n- 0.4.2 Added `includeRawStatic` and `includeStaticAsDataurl`\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenthus1ast%2Fnimja","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fenthus1ast%2Fnimja","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenthus1ast%2Fnimja/lists"}