I came to Jostraca as an outsider. I write specs and architecture more than I write generators, so I had to learn what a code generator library is before I could judge one. This article covers one claim I tested on version 0.39.0, on Windows, with a git clone: you can regenerate a file and keep the changes you made by hand.
What Jostraca does
Jostraca builds files from code. You describe a tree of folders, files and content, and it writes them to disk. This is the script I used:
import { Jostraca, Project, File, Content } from 'jostraca' const port = process.argv[2] || '8080' const jostraca = Jostraca({ existing: { txt: { write: true, merge: true } } }) await jostraca.generate({ folder: './out' }, () => { Project({}, () => { File({ name: 'config.sh' }, () => Content(`PORT=${port}\nHOST=localhost\n`)) }) }) Running node gen.mjs 8080 gives me out/config.sh with a port and a host. The interesting part is the existing option. It decides what happens when the file is already on disk. The default is to overwrite it. Other modes keep the old file, write a .new next to it, or merge.
Merge is the one a team wants. Generated files get edited by hand sooner or later, and nobody wants a regenerate to wipe that out.
The test that works
I generated the file, added DEBUG=1 by hand, then regenerated with port 9090. With the default mode, DEBUG=1 was gone, which is expected. With merge on, I got all three changes: PORT=9090, HOST=localhost and DEBUG=1. The result reported the file under merged.
This is a three way merge. Jostraca compares your current file against the new output and against the output of the previous run. It stores that previous run in a folder called .jostraca/generated/ inside the output folder. I like this design. It is the same idea git uses, and it needs no markers in your files.
The test that fails
That folder is the catch. On first use Jostraca writes a .jostraca/.gitignore that ignores generated and jostraca.meta.log. So the baseline never reaches git.
I committed the project, cloned it as a teammate would, installed dependencies, added DEBUG=1 by hand, and ran node gen.mjs 9090. The port changed. DEBUG=1 was gone. There was no warning and no error. The clone had no baseline, and merge mode silently acted like the default.
I also ran this on a single machine by deleting .jostraca/ between runs. Same outcome. So the problem needs no unusual setup. Anyone who clones the repo, or cleans the output folder, loses the safety net, and the tool does not say so.
The reference documentation does describe this. It states that merge with no baseline overwrites. The explanation page says it "degrades to a two way comparison", which would imply some attempt to keep your edits. What I observed matches the reference page. The two pages should say the same thing, and the safer wording should not be on the page a newcomer reads first.
The fix exists, but you have to know about it
The docs mention a control option on the generate() call, with a version flag. I set it:
await jostraca.generate({ folder: './out', control: { version: true } }, () => { // same tree as before }) With this, .jostraca/.gitignore is not written, so the generated/ baseline and the meta log are tracked by git. I repeated the clone test. The teammate's clone regenerated with port 9090 and kept DEBUG=1.
That is the result I wanted, so merge mode itself is sound. My complaint is about the default. A tool that offers a merge mode in order to protect hand edits should not drop that protection without a word when its bookkeeping folder is missing. I would call that a bug in the default behaviour, not in the merge logic.
A smaller trap in Content
While reading the docs I tried passing extra template data as a second argument:
Content('N=$$n$$\n', { extra: { n: 1 } }) // writes N=$$n$$ Content({ src: 'N=$$n$$\n', extra: { n: 1 } }) // writes N=1 The first form compiled and ran without complaint, and the placeholder stayed in the output. The second form works. Again the issue is silence. If an unknown positional argument raised an error, or an unresolved $$n$$ printed a warning, I would have found this in a minute instead of by comparing outputs.
What the docs do well
I could test the merge behaviour in an afternoon because the reference pages list each mode and say what each one does. The page on existing told me what to expect when the baseline is missing, and my test confirmed it. Many projects never document their failure cases that precisely. The problem is that the reader has to find that page, and the friendlier page says something softer.
What I would change
- Warn, or refuse to write, when merge mode is on and a file exists but has no baseline. This is the smallest change and it removes the exact failure I hit. A user who really wants overwrite can ask for it.
- Make the explanation page match the reference page on this point. It is a quick edit and it removes the contradiction.
- Mention
control: { version: true }in the getting-started material, with a line on when a team needs it.
Would I use it?
I already generate specs, so yes, I would try Jostraca on one of them. I would turn on version: true from day one and add a check to CI that the baseline folder is present. I have only used it on a small demo, so I cannot speak to large projects. On what I tested, the core idea is good and the default is too quiet about the one situation where it matters most.
I also built an SDK for the Supabase Management API using Jostraca's SDK generator. That's available here: https://github.com/X3PH1RE/supabase-mgmt-sdk

