Skip to main content
Code generation provides an easy mechanism for automating common development workflows and file structures. Whether it’s scaffolding a new library or application, updating configuration, or standardizing patterns. To accomplish this, we provide a generator, which is divided into two parts. The first being the templates and their files to be scaffolded. The second is our rendering engine that writes template files to a destination.

Creating a new template

To create a new template, run moon generate while passing the --template option. This will create a template directory and template.* file in the 1st file-based template location defined in generator.templates.
moon generate <name> --template
1

Configure template metadata

Every template requires a template.* file in the template’s directory root. This file acts as a schema and declares metadata and variables required by the generator.
template.yml
title: 'npm package'
description: |
  Scaffolds the initial structure for an npm package,
  including source and test folders, a package.json, and more.
variables:
  name:
    type: 'string'
    default: ''
    required: true
    prompt: 'Package name?'
2

Add template files

Feel free to add any files and folders to the template that you’d like to be generated by consumers! These files will then be scaffolded 1:1 in structure at the target destination.An example of the templates folder structure may look something like the following:
templates/
├── npm-package/
│   ├── src/
│   ├── tests/
│   ├── package.json
│   └── template.yml
└── react-app/
3

Use template syntax (optional)

Template files use Tera template engine with syntax similar to Twig, Liquid, and Django.
package.json
{
  "name": "{{ name | kebab_case }}",
  "version": "0.0.1"
}

Template features

Interpolation

Variables can be interpolated into file paths using the form [varName]. For example, if you had a template file src/[type].ts, and a variable type with a value of “bin”, then the destination file path would be src/bin.ts. This syntax also supports filters, such as [varName | camel_case].

File extensions

To enable syntax highlighting for template engine syntax, you may use the .tera (preferred) or .twig file extensions. These extensions are optional, but will be removed when the files are generated. Depending on your preferred editor, these extensions may be supported through a plugin:

Partials

Partials are special template files that are used for composition and inheritance. Because of this, these files should not be generated into the target destination, and do not support frontmatter. To ensure they are not generated, include the word “partial” anywhere in the file path. For example, partials/header.tpl or header.partial.tpl.

Raws

Raw template files are another special type of file that bypass all Tera rendering, and are used as-is instead. This is useful for files that contain syntax that conflicts with Tera. To mark a file as raw, add a .raw extension, for example: file.raw.js or file.js.raw. When the file is generated, the .raw extension will be removed.

Frontmatter

Frontmatter is a well-known concept for “per-file configuration”, and is achieved by inserting YAML at the top of the file, delimited by wrapping ---. This is a very powerful feature that provides more control than the alternatives.
package.json
---
force: true
---

{
  "name": "{{ name | kebab_case }}",
  "version": "0.0.1"
}

Template engine & syntax

Rendering templates is powered by Tera, a Rust based template engine with syntax similar to Twig, Liquid, Django, and more.

Variable interpolation

{{ varName }} -> foo
{{ varName | upper }} -> FOO

Conditional blocks

{% if price < 10 or always_show %}
  Price is {{ price }}.
{% elif price > 1000 and not rich %}
  That's expensive!
{% else %}
  N/A
{% endif %}

Loops

{% for item in items %}
  {{ loop.index }} - {{ item.name }}
{% endfor %}

Filters

Filters are a mechanism for transforming values during interpolation and are written using pipes (|). Tera provides many built-in filters, but we also provide the following custom filters:
  • Strings - camel_case, pascal_case, snake_case, upper_snake_case, kebab_case, upper_kebab_case, lower_case, upper_case
{{ some_value | upper_case }}
  • Paths - path_join, path_relative
{{ some_path | path_join(part = "another/folder") }}
{{ some_path | path_relative(from = other_path) }}
{{ some_path | path_relative(to = other_path) }}

Built-in variables

The following variables are always available within a template:
  • dest_dir - Absolute path to the destination folder.
  • dest_rel_dir - Relative path to the destination folder from the working directory.
  • working_dir - Current working directory.
  • workspace_root - The moon workspace root.

Generating code from a template

Once a template has been created and configured, you can generate files based on it using the moon generate command! This is also know as scaffolding or code generation. This command requires the name of a template as the 1st argument. The template name is the folder name on the file system that houses all the template files, or the id setting configured in template.*.
moon generate npm-package
An optional destination path, relative from the current working directory, can be provided as the 2nd argument.
moon generate npm-package --to ./packages/example
This command is extremely interactive, as we’ll prompt you for the destination path, variable values, whether to overwrite files, and more. If you’d prefer to avoid interactions, pass --defaults, or --force, or both.

Configuring template locations

Templates can be located anywhere, especially when being shared. Because of this, our generator will loop through all template paths configured in generator.templates, in order, until a match is found.
.moon/workspace.yml
generator:
  templates:
    - './templates'
    # Or
    - 'file://other/templates'
When using literal file paths, all paths are relative from the workspace root.

Archive URLs

Template locations can reference archives (zip, tar, etc) through https URLs. These archives should contain templates and will be downloaded and unpacked.
.moon/workspace.yml
generator:
  templates:
    - 'https://domain.com/some/path/to/archive.zip'
Archives will be unpacked to ~/.moon/templates/archive/<host>, and will be cached for future use.

Globs

If you’d prefer more control over literal file paths, you can instead use glob paths or the glob:// protocol. Globs are relative from the workspace root, and will only match directories, or patterns that end in template.*.
.moon/workspace.yml
generator:
  templates:
    - './templates/*'
    # Or
    - 'glob://projects/*/templates/*'

Git repositories

Templates locations can also reference templates in an external Git repository using the git:// locator protocol. This locator requires the Git host, repository path, and revision (branch, tag, commit, etc).
.moon/workspace.yml
generator:
  templates:
    - 'git://github.com/moonrepo/templates#master'
    - 'git://gitlab.com/org/repo#v1.2.3'
Git repositories will be cloned to ~/.moon/templates/git/<host> using an HTTPS URL (not a Git URL), and will be cached for future use.

npm packages

Additionally, template locations can also reference npm packages using the npm:// locator protocol. This locator requires a package name and published version.
.moon/workspace.yml
generator:
  templates:
    - 'npm://@moonrepo/templates#1.2.3'
    - 'npm://other-templates#4.5.6'
npm packages will be downloaded and unpacked to ~/.moon/templates/npm and cached for future use.

Declaring variables with CLI arguments

During generation, you’ll be prompted in the terminal to provide a value for any configured variables. However, you can pre-fill these variable values by passing arbitrary command line arguments after -- to moon generate. Argument names must exactly match the variable names. Using the package template example above, we could pre-fill the name variable like so:
moon generate npm-package --to ./packages/example -- --name '@company/example' --private
  • Array variables support multiple options of the same name.
  • Boolean variables can be negated by prefixing the argument with --no-<arg>.
  • Object variables can not declare values through arguments.

Sharing templates

Although moon is designed for a monorepo, you may be using multiple repositories and would like to use the same templates across all of them. You can share templates using:
  • Git submodules
  • Git repositories (using git:// protocol)
  • File archives
  • Node.js modules
  • npm packages (using npm:// protocol)
  • Another packaging system
Regardless of the choice, simply configure generator.templates to point to these locations:
.moon/workspace.yml
generator:
  templates:
    - './templates'
    - 'file://./templates'
    # Git
    - './path/to/submodule'
    - 'git://github.com/org/repo#branch'
    # npm
    - './node_modules/@company/shared-templates'
    - 'npm://@company/shared-templates#1.2.3'

Build docs developers (and LLMs) love