Make it yours
Write your own jen.
A generator is one file: the params it accepts and an actions() function that returns what should happen. No template language, no plugin API. Drop it in .jen/ and it's a command.
.jen/hello.mjs
// params come from --flags, or their default
export const params = {
name: {}, // no default = required
};
export const actions = ({ name }, { pascal }) => [
{
add: `${pascal(name)}.txt`,
template: `Hello, ${name}!\n`,
},
];
- 1
Create it. jen generator --name=hello --location=project writes the skeleton to .jen/hello.mjs.
- 2
Run it. Answers are flags. Leave out a required one and jen lists what's missing, then quits. No prompts, ever.
- 3
Keep it. .mjs, .js, .ts, even .mts. TypeScript runs through Node's type stripping, no build step.
~/projects/my-game
$ jen hello --name=world
hello (project) – say hello
+ World.txt
✓ 1 written
- +
add
Create a file from a template literal. Existing files are skipped unless --force.
- ~
insert
Put a line before a marker comment like # jen:sources, matching its indentation. Idempotent.
- ~
modify
Regex search and replace. Also the way to remove lines.
- -
delete
Remove a file. Stays inside the project root, never touches directories.
Case helpers, built in
pascal camel snake kebab constant: rigid body becomes RigidBody, rigidBody, rigid_body, rigid-body, RIGID_BODY.
Logic is just JavaScript
Conditionals and loops are ? : and .map(). Want a test file only on request? ...(withTest ? [{ add: … }] : []).
Respects your .editorconfig
New files and inserted blocks come out in the project's own indent style, tabs or spaces.
Share it as a pack
Publish to npm as jen-pack-<name> or @scope/pack-<name>. Anyone with it in package.json gets your generators. Like @codejen/pack-cpp.
jen looks in .jen/ (searched upwards like .git, but not past the repository or your home folder, and only if it's yours), then your user config, $JEN_PATH, installed packs (local or global), and built-ins. First match wins; jen --list shows what shadows what, jen <generator> --help a generator's params, and jen --list --json all of it for scripts – the command line has every option, and writing generators the whole reference.
Paths in your actions are relative to where the files go; start one with / for the project root. To wire into an existing file, let jen find it: { insert: { find: 'CMakeLists.txt' }, before: '# jen:sources', path: 'Foo.cpp' }.