A code generation tool that treats real source files as templates. Directives live in comments and are stripped after rendering, so your templates stay valid for any language.
- No handlebars/ejs syntax. Templates are the original language.
- A single token (
@@@) for all directives. - Works with strings, files, and globs.
- Supports includes with path interpolation.
- Comment-only directives keep syntax highlighting, formatters, linters, and IDE tooling intact.
- Runtime: Node.js or Bun.
- Module: native ESM (
.mjs). No bundling or transpilation.
# npm
npm install pluvo
# pnpm
pnpm add pluvo
# yarn
yarn add pluvo
# bun
bun add pluvoGlobal CLI install (optional):
npm install -g pluvoTemplate:
const template = `// @@@ if $.enabled
export const name = /* @@@ echo $.name */"default"/* @@@ endecho */;
// @@@ else
export const name = "fallback";
// @@@ endif
`;Code:
import { pluvo } from "pluvo";
const output = pluvo(template, { enabled: true, name: "variableName" });
console.log(output);Output:
export const name = "variableName";- Templates are valid code. Directives appear in comments and are removed during render.
- The directive token is
@@@. - Expressions use JavaScript syntax and evaluate against a
$scope (e.g.$.foo).
@@@ if <expr>
@@@ elseif <expr>
@@@ else
@@@ endif
@@@ each <item>[, <key>] of <expr>
@@@ endeach
@@@ echo <expr>
@@@ endecho
@@@ set <name> = <expr>
@@@ include <path>
- Expressions are JavaScript.
- Context is accessed via
$, e.g.$.user.name. eachbindsitemandkeyinto scope as$.itemand$.key.setwrites to the current scope.
The engine searches for @@@ inside known comment styles and strips the wrapper.
If a directive is the only content on a line, the whole line is removed.
Default line markers:
//,#,--,;,%,!,REM,::,'
Default block markers:
/* ... */,<!-- ... -->,{- ... -},(* ... *)
You can extend these via commentStyles (see API.md).
include accepts a path, file, or glob. Use {...} to interpolate expressions:
// @@@ include "../path/{$.lang}/index.js"Paths are resolved relative to baseDir or the current file.
See API.md for a full reference. Quick overview:
import pluvo, { renderTemplate, DEFAULT_COMMENT_STYLES } from "pluvo";
const output = pluvo("./templates/*.js", { foo: "bar" });Return value:
- String for a template string or a single file.
- Object map (
{ [filepath]: output }) for globs.
See CLI.md for details. Quick overview:
pluvo ./templates/*.js --context '{"foo":"bar"}'
pluvo ./templates/*.js --context @context.jsonFor globs, the CLI prints headers like:
--- path/to/file.js ---
<rendered output>
Python
Template:
# @@@ set banner = "# generated\n"
# @@@ echo $.banner
# @@@ endecho
def greet(name):
return f"Hello, {name}"
# @@@ each item, key of $.names
# @@@ echo "# name: " + $.item + "\n"
# @@@ endecho
# @@@ endeachCode:
const output = pluvo(template, { names: ["Ada", "Linus"] });Output:
# generated
def greet(name):
return f"Hello, {name}"
# name: Ada
# name: LinusHTML
Template:
<div class="app"></div>
<!-- @@@ if $.ready === true -->
<!-- @@@ echo '<span>ready</span>\n' -->
<!-- @@@ endecho -->
<!-- @@@ else -->
<!-- @@@ echo '<span>pending</span>\n' -->
<!-- @@@ endecho -->
<!-- @@@ endif -->Code:
const output = pluvo(template, { ready: true });Output:
<div class="app"></div>
<span>ready</span>SQL
Template:
SELECT id, email FROM users;
-- @@@ if $.includeWhere
-- @@@ echo "WHERE active = 1\n"
-- @@@ endecho
-- @@@ endif
ORDER BY id;Code:
const output = pluvo(template, { includeWhere: true });Output:
SELECT id, email FROM users;
WHERE active = 1
ORDER BY id;YAML
Template:
service:
name: api
# @@@ echo " replicas: " + $.replicas + "\n"
# @@@ endechoCode:
const output = pluvo(template, { replicas: 3 });Output:
service:
name: api
replicas: 3Expressions are evaluated with new Function. Only process templates and context data
from trusted sources.
- Full directive and parsing spec:
SPEC.md - API details:
API.md - CLI details:
CLI.md
See LICENSE.