{"id":15138702,"url":"https://github.com/shawnbot/meta-template","last_synced_at":"2025-10-23T15:30:55.085Z","repository":{"id":138035529,"uuid":"72076624","full_name":"shawnbot/meta-template","owner":"shawnbot","description":":sparkles: Automagically convert Nunjucks templates into a variety of other formats!","archived":false,"fork":false,"pushed_at":"2017-12-03T16:56:56.000Z","size":104,"stargazers_count":52,"open_issues_count":12,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-30T19:11:19.344Z","etag":null,"topics":["javascript","nunjucks","template-metaprogramming"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shawnbot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2016-10-27T06:09:46.000Z","updated_at":"2024-06-15T21:02:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"25d86daf-91e2-4178-8b24-cd594ccd6454","html_url":"https://github.com/shawnbot/meta-template","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shawnbot%2Fmeta-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shawnbot%2Fmeta-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shawnbot%2Fmeta-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shawnbot%2Fmeta-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shawnbot","download_url":"https://codeload.github.com/shawnbot/meta-template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237843932,"owners_count":19375230,"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":["javascript","nunjucks","template-metaprogramming"],"created_at":"2024-09-26T07:43:42.985Z","updated_at":"2025-10-23T15:30:49.768Z","avatar_url":"https://github.com/shawnbot.png","language":"JavaScript","readme":"# meta-template\nMaintaining templates can be a pain in the butt, especially if you need to\nmaintain templates for multiple engines or host languages. Meta-template aims\nto solve the problem of multi-engine template maintenance by making it possible\nto treat [Nunjucks] templates (which are _theoretically_ compatible with\n[Jinja] out of the box, and _almost_ compatible with [Django], [Liquid], and\n[Twig]) as the **source of truth** and programmatically transform them into other\nformats (such as [ERB], [Handlebars], [Mustache]) and even other _languages_,\nsuch as [JSX] or [PHP].\n\n## How it works\nAt a high level, there are three steps in the template conversion process:\n\n1. Use [Nunjucks] to [parse] a template into an _abstract syntax tree_ (AST)\n\n  ```js\n  const mt = require('meta-template');\n  const ast = mt.parse.string('{% if foo %}{{ foo }}{% else %}no foo!{% endif %}');\n  ```\n  \n1. Make any necessary transformations to the AST to match the output format\n\n  ```js\n  mt.ast.walk(ast, node =\u003e {\n    if (node.type === 'TemplateData') {\n      // do something with node.value here to modify the output, e.g.\n      node.value = '(' + node.value + ')';\n    }\n  });\n  ```\n  \n1. Format the AST into a string with a function that declaratively handles\n   different types of AST \"node\" (`If`, `Output`, etc.), and automatically\n   throws errors for unsupported node types\n   \n  ```js\n  const out = mt.format.php(ast);\n  console.log(out);\n  // produces:\n  // '\u003c?php if ($foo): ?\u003e\u003c?= $foo ?\u003e\u003c?php else: ?\u003e(no foo!)\u003c?php endif; ?\u003e'\n  ```\n  \nYou can try it yourself by combining the above snippets into a [standalone script](https://gist.github.com/shawnbot/b92f28c5b84aaee2922f1d9d3e20a18c)\nand run it through the `php` command with:\n\n```sh\nnode njk2php.js | php\n# (no foo!)\n```\n\n## The abstract syntax tree\nThe abstract syntax tree, or AST, is a tree structure of JavaScript objects that\ndescribes the parsed template. Some common nodes in the tree are:\n\n* `TemplateData` represents a raw string of template output\n* `Output` represents template _data_ output, such as a variable\n* `If` represents a conditional control structure with `cond` (condition),\n  `body` (the output when `cond` succeeds), and optional `else_` child nodes\n* `Symbol` represents a \"simple\" variable expression, e.g. `foo`\n* `LookupVal` represents a nested variable expression, e.g. `foo.bar[0]`\n* `Literal` represents literals like `true`, `false`, and `null` (which must\n  be converted to their language-specific equivalents in Ruby and Python)\n* `Include` the Nunjucks/Jinja/Liquid implementation of template partials\n\n**TODO**: explain the [parse] and [AST](ast/index.js) bits.\n\n## The format API\n**TODO**: explain the [abstract](format/abstract.js) and concrete format APIs.\n\n## Play with it!\nCurrently I'm experimenting with different output formats, starting with\n[Liquid][] (most useful for us Jekyll users at 18F) and [PHP][] (which seemed\nto me the most potentially difficult). You can test these out by cloning the\nrepo, running `npm install` to get the dependencies, then running the\n[bin/parse.js](bin/parse.js) script:\n\n```sh\n# output the Nunjucks AST in JSON format\n./bin/parse.js path/to/template.html\n\n# do the same without line and col info (--clean), trim input (--trim)\n./bin/parse.js --clean --trim path/to/template.html\n\n# or use stdin\necho 'foo {{ bar }} baz {% if x %}hi{% endif %}' | ./bin/parse.js\n\n# reformat the AST as Nunjucks (this _should_ produce the same output)\necho 'foo {{ bar }} baz...' | ./bin/parse.js --format\n\n# reformat as Liquid\necho 'foo {{ bar }} baz...' | ./bin/parse.js --format liquid\n\n# reformat as PHP!\necho 'foo {{ bar }} baz...' | ./bin/parse.js --format php\n```\n\n## Roadmap\nThis project is in its infancy, but here is a very rough roadmap:\n\n- [x] Adapt the Nunjucks parser to [parse] templates into an abstract syntax\n  tree (AST)\n- [x] Write a [format] API that can transform AST nodes back into\n  Nunjucks template strings\n- [x] Flesh out the conversion API in JavaScript\n- [ ] Do some research on template engine popularity in order to prioritize\n  target formats\n- [ ] Determine the \"lowest common denominator\" set of template features to\n  support in parsing so that we can warn when source templates use features\n  that aren't available in the desired output format(s)\n- [x] Make a command line tool\n- [ ] Write some API docs\n- [ ] Profit?\n\n[Nunjucks]: https://mozilla.github.io/nunjucks/\n[Django]: https://docs.djangoproject.com/en/1.10/topics/templates/\n[Jinja]: http://jinja.pocoo.org/\n[Handlebars]: http://handlebarsjs.com/\n[ERB]: https://docs.puppet.com/puppet/latest/reference/lang_template_erb.html\n[Liquid]: https://shopify.github.io/liquid/\n[Mustache]: https://mustache.github.io/\n[PHP]: http://php.net/\n[JSX]: https://facebook.github.io/jsx/\n[Twig]: http://twig.sensiolabs.org/\n[parse]: parse/index.js\n[format]: format/index.js\n[AST]: https://en.wikipedia.org/wiki/Abstract_syntax_tree\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshawnbot%2Fmeta-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshawnbot%2Fmeta-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshawnbot%2Fmeta-template/lists"}