Repository navigation
feat: add snake template transformation - #490
Conversation
Converts values to `snake_case` — same shape as `slug` but with
underscores instead of dashes so the result is safe as a variable name,
YAML key, or database column. Whitespace and dashes become underscores,
punctuation is stripped, runs of underscores collapse, and edge
underscores are trimmed. Unicode letters and numbers are preserved so
`Café Noël` becomes `café_noël`.
Wired into the same three entry-points as the existing transformations:
- Form templates: `{{ title | snake }}`
- `FormResult.asString`: `result.asString("{{ title | snake }}.md")`
- `ResultValue.snake` getter: `result.getValue('title').snake`,
chainable with `trimmed`, `lower`, etc. Handles arrays/objects by
converting nested strings individually and `FileProxy` values by
converting the file name.
Common use case is turning a form field into an identifier for
generated code or a snake_case YAML key.
Greptile SummaryThis PR adds a
Confidence Score: 5/5Safe to merge — the change is additive, all three entry points are correctly wired, and the applyPerString fix to slug resolves an existing array-merging bug without breaking existing single-string or object usage. The toSnake implementation is correct and symmetrical with toSlug. The applyPerString helper that fixes both slug and snake for arrays and FileProxy values is well-reasoned and its behaviour is fully covered by the new regression tests. No logic errors, type unsafety, or unhandled edge cases were found. No files require special attention.
|
| Filename | Overview |
|---|---|
| src/core/template/templateParser.ts | Adds toSnake, applyPerString helper, and fixes slug case to use applyPerString; logic is correct and well-commented. |
| src/core/template/templateSchema.ts | Adds literal('snake') to the transformations union — minimal, correct change. |
| src/core/ResultValue.ts | Adds snake getter following the exact same structure as the existing slug getter; FileProxy and deepMap paths are both handled correctly. |
| src/core/ResultValue.test.ts | Eight new tests for ResultValue.snake covering basic conversion, unicode, arrays, mixed arrays, empty string, and chaining — good coverage. |
| src/core/template/templateParser.test.ts | Five new snake template-pipe tests plus two regression tests for slug (array and FileProxy handling); all assertions are correct given the implementation. |
| docs/templates.md | Adds snake entry to the transformation list with accurate examples. |
| docs/ResultValue.md | Updates heading and adds snake shortcut description consistent with the code behaviour. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Form field value] --> B{Entry point}
B -->|"{{ field | snake }}"| C[templateParser.ts\nexecuteTransformation]
B -->|"result.getValue('x').snake"| D[ResultValue.ts\nget snake]
B -->|"formResult.asString(template)"| C
C --> E{applyPerString toSnake}
E -->|Array| F["map each item\nfn(String(item))\njoin with ','"]
E -->|FileProxy| G["fn(v.name)"]
E -->|Other| H["fn(String(v))"]
D --> I{value type?}
I -->|FileProxy| J["toSnake(v.name)"]
I -->|Other| K["deepMap:\ntoSnake per string element"]
F --> L[toSnake function]
G --> L
H --> L
J --> L
K --> L
L --> M["1. toLocaleLowerCase()"]
M --> N["2. replace whitespace+dashes → '_'"]
N --> O["3. strip non-letter/number/underscore"]
O --> P["4. collapse runs of '_'"]
P --> Q["5. trim leading/trailing '_'"]
Q --> R[snake_case result]
Reviews (2): Last reviewed commit: "fix: preserve array/FileProxy structure ..." | Re-trigger Greptile
`slug` and `snake` strip punctuation, so calling `String(value)` on an array consumed the comma separator and merged distinct values into one token (`["Foo Bar", "Hello World!"]` → `foo-bar-hello-world`), and on a FileProxy consumed path slashes and merged folder + filename. Route both cases through a shared `applyPerString` helper: map each array element and join with commas so values stay visually distinct after transformation, and read `FileProxy.name` (matching the existing ResultValue.slug / ResultValue.snake getters) instead of the full path. Other transformations (upper/lower/trim/capitalize/stringify) don't strip punctuation, so their existing String(value) behavior already keeps commas intact and stays unchanged.
Summary
snaketemplate transformation that converts a value tosnake_case— same shape as the recently addedslug, but with underscores instead of dashes so the result is safe as a variable name, YAML key, or database column name.{{ field | snake }}template pipe,FormResult.asString, and a chainableResultValue.snakegetter (result.getValue('title').snake).FileProxyvalues are converted from the file name.Café Noël→café_noël) so it stays useful for non-English input.Behavior
Given input
Hello, World!→hello_worldGiven input
---My Note (2024)→my_note_2024Given input
__foo___bar__→foo_barGiven input
Café Noël 2024→café_noël_2024Test plan
npm run test— all 205 tests pass (including 5 new parser tests and 8 newResultValue.snaketests)npm run build— lint + svelte-check + esbuild bundle all green (only pre-existing warnings){{ title | snake }}inside a form template and confirm the outputGenerated by Claude Code