---
title: "Jostraca: A code generator you can run twice"
description: "Jostraca builds file trees from components. You declare Project, Folder, File and Content in a define phase; a build phase writes the result. Because the whole intended output is known before a byte is written, a second run can preserve, present, diff or merge against the code a human edited in between."
source: "https://jostraca.org/"
---

Code generation after the first run

# Generate it now. Generate it _again_.

Regenerate a file tree after manual edits. Jostraca calculates the intended output before writing files, then applies the mode you choose: write, preserve, present, diff, or merge.

Use ordinary code for loops and conditions. Template markers can sit inside the target language’s comments, so the surrounding file remains valid HTML, Go, or SQL.

**2 implementations** of one specification: **TypeScript** and **Go**, held to the same shared test corpus.

[Install Jostraca](https://jostraca.org/how-to#install) [Start the tutorial](https://jostraca.org/docs/tutorial) [Read the docs](https://jostraca.org/docs)

Or straight to [TypeScript](https://jostraca.org/how-to/install-typescript) or [Go](https://jostraca.org/how-to/install-go).

## Declare a tree. Jostraca writes it.

Components nest like directories. `Project` contains `Folder`, which contains `File`, which contains `Content`. They are ordinary function calls, so loops, conditionals, and parameters are ordinary JavaScript too. There is no template language waiting to become your next programming language.

Every name here is a plain string, because a component call is a function call and you already have a language for that. Content that varies with your data comes from a model, which the [tutorial](https://jostraca.org/docs/tutorial) introduces.

gen.mjs

```js
import { Jostraca, Project, Folder, File, Content } from 'jostraca'

await Jostraca().generate({ folder: './out' }, () => {
  Project({ folder: 'acme' }, () => {
    File({ name: 'README.md' }, () => {
      Content('# Acme\n')
    })
    Folder({ name: 'src' }, () => {
      File({ name: 'index.js' }, () => {
        Content('console.log("acme")\n')
      })
    })
  })
})
```

what it wrote

```text
out/acme/README.md
  # Acme
out/acme/src/index.js
  console.log("acme")
```

## No template syntax to learn

Two ways to produce a file. Neither one is a dialect.

### Write code

Components are function calls in the language you are already in. Iteration is `for`. Branching is `if`. Reuse is a function. Your debugger, type checker, formatter, and test runner keep working, because nothing here is new to them.

### Or mark up a real file

When a file reads better as a file, keep it as one. A marker is just a name you chose, sitting where the file already allows text, so the template stays valid HTML: or Go, or SQL, or YAML.

tpl/page.html: an ordinary HTML file

```html
<!doctype html>
<html>
  <head>
    <title>APP_TITLE</title>
  </head>
  <body>
    <h1>APP_NAME</h1>
  </body>
</html>
```

gen.mjs

```js
import { Jostraca, Project, File, Fragment } from 'jostraca'

await Jostraca().generate({ folder: './out' }, () => {
  Project({}, () => {
    File({ name: 'index.html' }, () => {
      Fragment({
        from: '../tpl/page.html',
        replace: {
          APP_TITLE: 'acme',
          APP_NAME: 'Acme',
        },
      })
    })
  })
})
```

what it wrote

```text
out/index.html
  <!doctype html>
  <html>
    <head>
      <title>acme</title>
    </head>
    <body>
      <h1>Acme</h1>
    </body>
  </html>
```

`tpl/page.html` is valid HTML. A browser renders it, a formatter formats it, a linter checks it, and your editor highlights it, because every character in it belongs to HTML. `APP_TITLE` and `APP_NAME` are ordinary text that happens to be replaced. For a file where a bare name would not be valid, the [replace guide](https://jostraca.org/how-to/replace-markers-in-a-template) covers the marker forms that hide inside a comment.

The same page written in a template language needs tooling for that dialect. Rendering the template resolves its loops and substitutions into the final HTML.

the same page in a template language, shown as plain text

```text
<!doctype html>
<html>
  <head>
    {% block head %}{% endblock %}
  </head>
  <body>
    <h1>{{ app.name }}</h1>
  </body>
</html>
```

Use comment markers when you want to edit and validate a template with the target language’s tools.

## The second run

A generator that only ever runs on an empty directory is a project template. Jostraca is built for the other case: the output is checked in, somebody has edited it, and you need to regenerate anyway. 5 modes decide what happens to a file that already exists. Binaries get the first three, because text diffs and merges do not apply to binary files. The file extension selects the set.

`write`

overwrite the file (the default)

`preserve`

overwrite, keeping the old bytes beside it

`present`

leave the file alone and write the new version next to it (needs write: false)

`diff`

write an annotated two-way diff instead

`merge`

three-way merge against the last generate

two runs, with a hand edit in between

```js
import { appendFileSync } from 'node:fs'
import { Jostraca, Project, File, Content } from 'jostraca'

const jostraca = Jostraca({
  existing: { txt: { write: true, merge: true } },
})

const run = (body) => jostraca.generate({ folder: './out' }, () => {
  Project({}, () => File({ name: 'config.sh' }, () => Content(body)))
})

// First run. Then somebody adds a line by hand. Then the generator
// changes a different line and runs again.
await run('PORT=8080\nHOST=localhost\n')
appendFileSync('./out/config.sh', 'DEBUG=1\n')
await run('PORT=9090\nHOST=localhost\n')
```

what it wrote

```text
out/config.sh
  PORT=9090
  HOST=localhost
  DEBUG=1
```

A file carrying the string `JOSTRACA_PROTECT` is never overwritten, whatever the mode says, so a reader can take a generated file away from the generator without asking you first. The [options reference](https://jostraca.org/docs/reference-options) specifies each mode; [the how-to guides](https://jostraca.org/how-to) show them in use.

## 10 components

That is the whole vocabulary. Anything else you need is a function you write yourself and wrap with `cmp()`, which makes it a component like the rest.

`Project`

the root of one generated tree

`Folder`

a directory, nested as deep as you like

`File`

a file, with an optional POSIX mode

`Content`

text, with model values substituted in

`Line`

text, with the newline supplied for you

`Fragment`

a template file read in from disk

`Slot`

a named region of a fragment you fill

`Inject`

content placed between markers in a file that exists

`Copy`

a file or a whole directory, templated on the way through

`List`

one block of content per item in an array

Every prop and every default is in the [component reference](https://jostraca.org/docs/reference-components).

## Two implementations, one output

TypeScript is canonical and Go is a maintained port. They are held to byte-identical output for the same logical input by a shared, language-neutral test corpus that both suites read, so a behaviour documented here means the same thing in either language. Correct defects in TypeScript first where needed, then bring Go into parity.

The Go API, including the places where Go idiom requires a different structure, is in the [Go reference](https://jostraca.org/docs/reference-go).

## Install

npm

```sh
npm install jostraca@0.36.6
```

Go

```sh
go get github.com/jostraca/jostraca/go
```

MIT licensed. Source and issues are on [GitHub](https://github.com/jostraca/jostraca).
