Seed, Insert and Replace
Three entry types write into a file with care: seed writes a file once and then leaves it alone, insert adds or updates one block inside a file, and replace swaps the whole content of one block. They are set in the type of a mapping entry.
seed: write once
The file is created if it does not exist and never touched again, so you can edit it freely.
"settings.json": [{ target: "settings.json", type: "seed" }],
insert: update one slot inside a file
The target file contains a named slot. The source file is the fragment that goes into that slot. Each generation updates just that fragment and leaves the rest of the file alone.
You write the slot in the target file (usually a template file of your own, so it is created together with
the rest), as two marker lines, each on its own line. The marker is the tag {% slot name %} ... {% endslot %}. You can
write it bare, or wrapped in a comment that starts with CodeBaker:. The comment only wraps the tag and changes nothing in it:
<!-- CodeBaker: {% slot menu %} --> {% slot menu %}
<!-- CodeBaker: {% endslot %} --> {% endslot %}
A template that chose another syntax writes the markers with its own tag delimiters ([% slot menu %] for square), and the same rule holds for the fragment markers CodeBaker writes.
The comment style is up to you: //, /* ... */, #, -- and <!-- ... --> are all accepted (always with the {% %} tag inside). A marker line
is copied into the generated file exactly as you wrote it, in the same style and with the same indentation,
so a later insert can find it. Use a comment where the language has one (the markers then stay invisible), and the
bare tag for plain text such as .txt or .md.
In the mapping:
"menu-item.html": [
{
target: "index.html", // the file that contains the slot
type: "insert",
slot: "menu", // the slot name in the target
id: "___entityKey___", // identifies this fragment inside the slot
mode: "append", // where a NEW id is placed: "append" (default) or "prepend"
indent: "auto", // how far it is indented, see "Indentation" below
},
],
| Key | Meaning |
|---|---|
slot | The slot name used in the target file (letters, digits, _, - and .). |
id | Names this fragment. Give every insert an id (same characters). |
mode | Where a new id goes in the slot: append (default) or prepend. |
indent | Optional: how far a new fragment is indented. Default "auto". See below. |
commentStyle | Optional: html, block, hash, sql, line or tag. See below. |
seed | Optional: a template file used to create the target when it does not exist yet. See below. |
How it behaves:
-
The first time an
idis inserted, CodeBaker wraps the fragment in its own pair of fragment markers inside the slot, for example<!-- CodeBaker: {% fragment laptop %} -->and<!-- CodeBaker: {% endfragment %} -->. They are written in the same style as the slot's own markers (a comment stays a comment, a bare tag stays a bare tag). Leave those markers in the file: they are how the next run finds its fragment. -
Generating the same
idagain replaces its own fragment in place, so repeated runs do not pile up duplicates. Without anidthere is no marker to find, soappendandprependadd the fragment again on every run. -
An insert never removes anything it did not put there itself. The same
idreplaces its own fragment, any otheridis added next to what is already in the slot, and text you wrote by hand is left alone. If you really want the slot emptied and refilled, usetype: "replace"instead (below). -
Text between the markers of a fragment belongs to the template and is replaced on the next run with that
id. Everything else in the file, inside the slot or outside it, is yours and is never touched. -
Indentation. A new fragment is indented with the
indentoption. Numbers are always counted from the line that holds the slot's own marker:indentResult "auto"(default)Follows the neighboring line inside the slot: the last one for append, the first one forprepend. An empty slot has no neighbor, so the fragment gets the indentation of the slot's own line."auto:2","auto:-2"The same, but the first content of an empty slot is indented 2 more (or 2 fewer) than the slot's line. 0or"slot"Always the indentation of the slot's own line. 2or"+2"2 more than the slot's line. -22 fewer than the slot's line. " "Spaces or tabs written out, used exactly as they are. A relative offset adds a tab for each step if the slot's line is indented with tabs, otherwise a space. The indentation inside the fragment is kept, and the fragment markers get the same indentation as the content. A fragment that is updated stays exactly where and how it was, with the indentation its own markers already have.
-
commentStylemakes CodeBaker write the markers of a new fragment in another style than the slot's own (htmlis<!-- -->,blockis/* */,hashis#,sqlis--,lineis//,tagis{% %}). The slot markers you wrote are never changed. -
The file you insert into is not run through the template engine. CodeBaker only looks for the marker lines, and everything outside the slot comes back unchanged, byte for byte. So a Vue, Angular, Blade, Handlebars or Twig file that has its own
{{ ... }}or{% ... %}is safe, and so is a seed file you have edited. -
A
{% block %}tag is not a slot. Pebble runs it when the template file is generated and leaves nothing behind, so a later insert cannot find it. Useslotandendslot.
An insert goes into the generated result, not into the file already on disk. A file that is an ordinary
template file is written again from the template on every run, so a slot starts from what the template contains
(usually nothing) and anything typed into the slot in the project is overwritten. To keep the file, and with it
everything in its slots, from one run to the next, make it a seed: it is written once, and the insert then
updates only its own fragment in it.
"mapping": {
"Hello.txt": [{ target: "Hello.txt", type: "seed" }],
"last-generated.txt": [{ target: "Hello.txt", type: "insert", slot: "lastGenerated", id: "timestamp" }],
}
The price is that a seed is not rendered again, so a variable such as {{ name }} is filled in only the first time.
When something is wrong, nothing is inserted and the file is left as it was, and the step 2 log says why: the slot is not in the file, a slot is not closed, slots are nested or declared twice, or a fragment id appears twice in a slot.
If the target does not exist yet. Inserts run last, but nothing guarantees the target was created by an
earlier entry. If it is missing and the item has no seed, an empty file is written and nothing is inserted.
To start from a prepared page, either add a seed entry for the target file (next section) or give the insert
item itself a starting file:
"menu-item.html": [
{ target: "index.html", type: "insert", slot: "menu", id: "___entityKey___", seed: "index-start.html" },
],
Here seed: "index-start.html" is a template path (relative to the template folder, containing the slot
markers). It is used only when the target is missing or blank. The seed file is itself a template file, so add
"index-start.html": false to the mapping to keep it out of the output. Do not confuse seed: "<path>" with
type: "seed", which is an item type that writes a whole file once.
replace: swap the whole content of a slot
type: "replace" takes the same keys as an insert (target, slot, id, commentStyle, seed), but instead
of adding a fragment it empties the slot and puts the new text in it. Whatever was in the slot before,
including fragments from other ids and text you wrote by hand, is gone. Everything outside the slot is untouched.
"last-generated.txt": [
{ target: "Hello.txt", type: "replace", slot: "lastGenerated", id: "timestamp" },
],
Use it for a slot that only ever shows one thing, such as a "last generated" stamp. Because it is a type of its
own and not a mode, you cannot get this behavior by accident from an insert. mode: "replace" is refused with
an explanation in the log.
If the target file is generated from a template on every run (not seed), it starts from the template's own
slot each time, so previous fragments are not there anyway. To let fragments accumulate across runs, make the
target a seed file.
A complete example: seed and insert together
A page that is written once, then grows by one list item per run.
.CodeBaker/NewsPage/
├── codebaker.config.json5
├── codebaker.advanced.json5
├── page.html
└── entry.html
codebaker.config.json5:
{
id: "news-page",
name: "News Page",
version: "1.0.0",
placeholders: ["___VAR___"],
vars: {
itemKey: "launch",
itemTitle: "We launched",
},
open: ["news.html"],
}
page.html holds the slot:
<h1>News</h1>
<ul>
<!-- CodeBaker: {% slot items %} -->
<!-- CodeBaker: {% endslot %} -->
</ul>
entry.html is the fragment:
<li>___itemTitle___</li>
codebaker.advanced.json5:
{
mapping: {
// Written once. Your later edits to news.html are never overwritten.
"page.html": [{ target: "news.html", type: "seed" }],
// Merged into news.html's "items" slot on every run.
"entry.html": [
{
target: "news.html",
type: "insert",
slot: "items",
id: "___itemKey___",
mode: "append",
},
],
},
}
The first run creates news.html with one item. Running again with another itemKey adds a second item, and
running with the same itemKey updates that item in place. The seed always runs before the insert, so the
insert always has a page to merge into. The CRUD Demo sample template uses the same pairing (see
Getting Started).
Next
- JSON Upsert: add or update an entry in a JSON list.
- Advanced Templates: the mapping file, entry types and run order.
- Questions and Answers: why the text typed into a slot disappears, and how
seedfixes it.