hardening
This commit is contained in:
20
README.md
20
README.md
@@ -1,6 +1,6 @@
|
||||
# tdir
|
||||
|
||||
Treat a directory as a template. File paths and contents support conditionals (`@if`/`@elseif`/`@else`) and variable substitution (`@var`). Provide a Zod schema and tdir validates it matches the template at setup time, then validates context at render time.
|
||||
Treat a directory as a template. File paths and text file contents support conditionals (`@if`/`@elseif`/`@else`) and variable substitution (`@var`). Provide a Zod schema and tdir validates it matches the template at setup time, then validates context at render time. Binary files are copied through unchanged.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -32,7 +32,7 @@ Where `index.html` contains:
|
||||
Render it:
|
||||
|
||||
```ts
|
||||
import { initRenderer } from "tdir"
|
||||
import { initRenderer } from "@gregorlohaus/tdir"
|
||||
import { z } from "zod"
|
||||
|
||||
const createRenderer = initRenderer("./templates")
|
||||
@@ -58,7 +58,7 @@ render("./output", {
|
||||
|
||||
| Directive | Description |
|
||||
|---|---|
|
||||
| `<@if(context.x)>` | Conditional block — truthy check (must end with `<@endif>`) |
|
||||
| `<@if(context.x)>` | Conditional block — boolean check (must end with `<@endif>`) |
|
||||
| `<@if(eq(context.x,"value"))>` | Conditional block — string equality check |
|
||||
| `<@elseif(context.y)>` | Else-if branch (same forms as `@if`) |
|
||||
| `<@else>` | Else branch |
|
||||
@@ -70,7 +70,7 @@ render("./output", {
|
||||
|
||||
| Directive | Description |
|
||||
|---|---|
|
||||
| `<@if(context.x)>dirname` | Conditionally include directory/file (truthy check) |
|
||||
| `<@if(context.x)>dirname` | Conditionally include directory/file (boolean check) |
|
||||
| `<@if(eq(context.x,"value"))>dirname` | Conditionally include by string equality |
|
||||
| `<@var(context.x)>` | Dynamic directory/file name |
|
||||
|
||||
@@ -81,7 +81,7 @@ These can be combined: `<@if(context.web.create)><@var(context.web.dir)>` create
|
||||
`createRenderer` validates that your Zod schema matches the template variables. Mismatches throw `SchemaMismatchError`:
|
||||
|
||||
```ts
|
||||
import { initRenderer, SchemaMismatchError } from "tdir"
|
||||
import { initRenderer, SchemaMismatchError } from "@gregorlohaus/tdir"
|
||||
import { z } from "zod"
|
||||
|
||||
const createRenderer = initRenderer("./templates")
|
||||
@@ -92,14 +92,14 @@ createRenderer(z.object({
|
||||
web: z.string(), // wrong type
|
||||
header: z.object({ show: z.boolean(), title: z.string() })
|
||||
}))
|
||||
// SchemaMismatchError: Shema doesnt match used template variables: web: expected z.boolean() but schema has z.string()
|
||||
// SchemaMismatchError: Schema doesn't match used template variables: web: expected z.boolean() but schema has z.string()
|
||||
|
||||
// Schema is missing fields used in templates -- throws SchemaMismatchError
|
||||
createRenderer(z.object({
|
||||
web: z.boolean()
|
||||
// missing header
|
||||
}))
|
||||
// SchemaMismatchError: Shema doesnt match used template variables: header: missing in schema
|
||||
// SchemaMismatchError: Schema doesn't match used template variables: header: missing in schema
|
||||
```
|
||||
|
||||
## Context validation
|
||||
@@ -123,12 +123,14 @@ render("./output", { web: "not a boolean", header: { show: true, title: "Hi" } }
|
||||
|
||||
`render(target, context)` clears `target` before writing, so rendering the same template into the same directory with different contexts always produces a clean result (files/paths excluded by conditionals won't linger from a previous run).
|
||||
|
||||
For safety, tdir refuses to render into the filesystem root, the current working directory, the home directory, or any directory that overlaps the template source. Dynamic file and directory names are also resolved against the output directory and cannot write outside it.
|
||||
|
||||
## Unmatched directives
|
||||
|
||||
A `<@if>` without a matching `<@endif>` throws at render time:
|
||||
A `<@if>` without a matching `<@endif>` throws when the renderer is initialized:
|
||||
|
||||
```ts
|
||||
// If a template file contains <@if(context.x)> with no <@endif>
|
||||
render("./output", { x: true })
|
||||
initRenderer("./templates")
|
||||
// Error: Unmatched <@if> without <@endif>
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user