Scaffolding, without the small talk

jen.

The code jen(erator). Flags in, files out. Nothing to answer.

Extremely lightweight. Blazing fast. Zero extra dependencies. Generators are plain JavaScript modules, templates are template literals, and nothing ever asks you a question.

Browse the C++ recipes →
~/projects/my-game
$ jen cpp:class --name=rigid_body --moveOnly --dir=src
cpp:class (pack @codejen/pack-cpp) – create a C++ class (header + source), wired into the CMakeLists.txt with # jen:sources

  + src/RigidBody.h
  + src/RigidBody.cpp
  ~ src/CMakeLists.txt  + RigidBody.cpp

✓ 3 written

Scaffolds anything that's text

How it works

Three steps. Zero surprises.

  1. Params

    Answers come from --flags or a param's own default. Something required is missing, or a flag the generator doesn't know? jen says so and quits. It never prompts.

  2. Plan

    jen works out every change and prints it: + add, ~ modify, = skip. Add --dry-run to only look.

  3. Act

    Then the files are written. Same params, same output, every time. Indentation follows your .editorconfig.

Why

Small on purpose.

Where files go

Right where you stand.

jen never prompts, and it doesn't guess either. Two rules cover it:

  • Files go where you're standing. Run a generator in a folder and its files land there. App starters make a new folder and put everything in it. --dir=<path> writes somewhere else.
  • Generators say what, jen says where. A generator never picks a folder. Files it wires into, like a CMakeLists.txt with a marker, jen finds: the nearest one above where the files go.

The project root, found by walking up to the nearest .jen/, package.json or .git, is the boundary: nothing outside it is touched. That is how a class made in src/net/ still gets wired into the right CMakeLists.txt.

~/projects/my-game
$ cd my-game
$ jen cpp:class --name=Camera --dir=src
cpp:class (pack @codejen/pack-cpp) – create a C++ class (header + source), wired into the CMakeLists.txt with # jen:sources

  + src/Camera.h
  + src/Camera.cpp
  ~ src/CMakeLists.txt  + Camera.cpp

✓ 3 written

$ cd src/net
$ jen cpp:class --name=Socket
cpp:class (pack @codejen/pack-cpp) – create a C++ class (header + source), wired into the CMakeLists.txt with # jen:sources
  root: ../.. (package.json)

  + Socket.h
  + Socket.cpp
  ~ ../CMakeLists.txt  + net/Socket.cpp

✓ 3 written

Packs

Ready-made generators.

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. 1
    Create it. jen generator --name=hello --location=project writes the skeleton to .jen/hello.mjs.
  2. 2
    Run it. Answers are flags. Leave out a required one and jen lists what's missing, then quits. No prompts, ever.
  3. 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

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' }.

Bring your own ecosystem

Not just your own generators.

With @codejen/pack-yeoman, jen also runs real Yeoman generators – JHipster included. generator-code and generator-jhipster are fetched on the fly, without ever touching your project's own dependencies.

~/projects
$ jen yeoman:code --extensionType=command-ts --extensionDisplayName="My Extension"
Fetching @codejen/pack-yeoman@latest …
yeoman:code (pack @codejen/pack-yeoman) – Yeoman generator-code – fetched when run
Fetching generator-code@latest …

  + my-extension/package.json
  + my-extension/src/extension.ts
  …

✓ 15 written

Install it.
Scaffold something.

Star on GitHub
Requires Node.js 24+ · MIT License