mtrgen is a template-driven file generator for Node.js and browser. It ships both as a TypeScript library and as a CLI so you can generate files from .mtr templates in code or directly from the terminal.
This is a port of the original PHP libraries MTRGen and Pars'Em.
Node.js 20 or newer is recommended for development, releases, and CLI usage.
Library:
# NPM:
npm i mtrgen-js
# PNPM:
pnpm i mtrgen-js
# Bun:
bun i mtrgen-jsCLI:
# NPM:
npm i -g mtrgen-js
# PNPM:
pnpm i -g mtrgen-js
# Bun:
bun i -g mtrgen-jsOr run it without a global install:
# NPM:
npx mtrgen generate ./templates/component.ts.mtr --arg name=Button
# PNPM:
pnpx mtrgen generate ./templates/component.ts.mtr --arg name=Button
# Bun:
bunx mtrgen generate ./templates/component.ts.mtr --arg name=ButtonThe package exposes the mtrgen command:
mtrgen <command> [options]Available commands:
generate,gen: generate from a saved template name or from--path <file>.save,s: save a local.mtrtemplate into the local store.saved,ls: list templates in the local store.remove,rm: remove a template from the local store.repair,r: remove broken entries from the local store.add,a: download a template from the online registry into the local store.use,u: generate directly from an online registry template.login,in: log into the online registry and save the access token locally.signup,sign: create a registry account.publish,pub: publish a local or saved template to the online registry.help [command]: show global help or command help.list: list the available commands.-v,--version: print the package version.
The local template store and login profile are saved under ~/.mtrgen.
mtrgen generate [name] [key=value ...]
mtrgen generate --path <template-path> [options]Supported options for generate and use:
-o, --out-dir <dir>: output root directory. Defaults to the current working directory.-d, --data <file>: path to a JSON file with template arguments.-a, --arg <key=value>: inline template argument. Repeat this flag to pass multiple values.--dry-run: print the target file paths without writing anything.-h, --help: show command help.
Examples:
mtrgen generate --path ./templates/component.ts.mtr --arg name=Button --arg folder=components
mtrgen generate ButtonTemplate name=Button folder=components
mtrgen generate ButtonTemplate --data ./component.json --out-dir ./src
mtrgen use vendor/component name=Button --out-dir ./srcInline CLI argument values use the same literal parser as template defaults when possible:
- strings:
name=Button - quoted strings:
title="Hello world" - booleans:
enabled=true - numbers:
count=3 null:value=null- arrays:
items=["one", "two"] - objects:
meta={folder: "ui", nested: {enabled: true}}
Nested keys are supported in CLI arguments:
mtrgen generate ./template.mtr --arg meta.folder=components --arg items[0]=Buttonmtrgen save ./templates/component.ts.mtr --alias ButtonTemplate
mtrgen saved
mtrgen remove ButtonTemplate
mtrgen repairThe registry-backed commands use https://mtrgen.matronator.cz/api.
mtrgen signup my-user Password123
mtrgen login my-user Password123
mtrgen add vendor/component
mtrgen use vendor/component name=Button
mtrgen publish ButtonTemplate
mtrgen publish --path ./templates/component.ts.mtrFor the full language reference, see docs/mtrgen-spec.md.
Every generator template starts with an MTRGEN header:
--- MTRGEN ---
name: component
filename: <% $name|pascalCase %>.tsx
path: src/<% $folder="components" %>
defaults:
title: "Hello"
enabled: true
--- /MTRGEN ---
export const title = "<% $title %>";Required header fields:
name: template name.filename: output filename template.path: output directory template.
Optional header fields:
defaults: global default values available to the filename, path, and template body.syntax: template syntax version. Defaults to2.
Variable tags use <% ... %>:
Hello <% $name %>!Variables support inline defaults:
Hello <% $name="world" %>!Lookup supports nested properties and array indexes:
<% $user.name %>
<% $user.profile.handle %>
<% $items[0] %>
<% $meta["folder"] %>
<% $meta[$field] %>All variable lookups are $-prefixed by default. If you need the legacy bare-lookup behavior, you can opt a template into syntax: 1 in its MTRGEN header.
Plain objects are interpolated as JSON.
When the same value exists in multiple places, precedence is:
- explicit runtime arguments
- inline variable defaults like
<% $name="Button" %> - header defaults in the
defaults:block
Template defaults and CLI values can use these literal forms:
- strings:
"hello"or'hello' - numbers:
1,3.14,-2 - booleans:
true,false - null:
null - arrays:
[1, "two", true, null] - objects:
{folder: "ui", nested: {enabled: true}}
Multiline arrays and objects are supported inside the header defaults: block.
Filters are chained with |:
<% $name|pascalCase %>
<% $title|truncate:30,"..." %>
<% $foo|truncate:3,""|upper %>Available filters:
- text casing:
upper,lower,upperFirst,lowerFirst,camelCase,snakeCase,kebabCase,pascalCase,titleCase - collection helpers:
first,last,length,reverse,random,shuffle - string shaping:
truncate,trim,substring,stripTags,nl2br - encoding and escaping:
url,escape,unescape,hash,rot13,encode,decode - math helpers:
pow,ceil,floor,round
Examples:
<% $name|upper %>
<% $componentName|pascalCase %>
<% $body|truncate:120,"..." %>
<% $url|encode:"base64" %>
<% $count|pow:2 %>Conditional blocks use if, elseif, else, and endif:
<% if $enabled %>
enabled
<% else %>
disabled
<% endif %>Supported comparison operators:
===!====!=<<=>>=
Conditions support:
- variable lookups with
$name - nested lookups like
$user.nameor$items[0] - grouped expressions with parentheses
- boolean operators
&&and|| - literal comparisons
- negation with
! - nested condition blocks
Example:
<% if ($kind === "service" && $enabled) || !$fallback %>
export class <% $name|pascalCase %>Service {}
<% elseif !$enabled %>
// disabled
<% else %>
export const <% $name %> = true;
<% endif %>Loop blocks use for and endfor:
<% for item of $items %>
<% $item %>
<% endfor %>Supported forms:
<% for item of $array %><% for [item, index] of $array %><% for [item, key] of $object %><% for item of $object %><% for [_, index] of $array %><% for [_, key] of $object %>
Loop position blocks are also available inside for bodies:
<% for item of $items %>
<% first %>[<% endfirst %>
<% $item %><% sep %>, <% endsep %>
<% last %>]<% endlast %>
<% endfor %>empty renders only when the iterable has no entries:
<% for item of $items %>
<% $item %>
<% empty %>No items<% endempty %>
<% endfor %>Comments are removed from the final output:
<# this will not be rendered #>Multiline comments are supported too.
import { Generator, Parser } from "mtrgen-js";
const template = `--- MTRGEN ---
name: component
filename: <% $name|pascalCase %>.ts
path: src/components
--- /MTRGEN ---
export const name = "<% $name %>";
`;
const file = Generator.parseTemplate(template, { name: "button" });
console.log(file.filePath);
console.log(file.contents);
console.log(Parser.parseString("Hello <% $name %>!", { name: "world" }));Generate from a template file and write it to disk:
import { Generator } from "mtrgen-js";
const files = Generator.parseAnyFile("./templates/component.ts.mtr", {
name: "button",
folder: "ui",
});
Generator.writeFiles(files, {
rootDir: process.cwd(),
});npm run build
npm run test