Skip to content

feat: add snake template transformation - #490

Merged
danielo515 merged 2 commits into
masterfrom
claude/stoic-allen-uq2lhz
Jul 22, 2026
Merged

danielo515 merged 2 commits into
masterfrom
claude/stoic-allen-uq2lhz

Conversation

@danielo515

Copy link
Copy Markdown
Owner

Summary

  • Adds a snake template transformation that converts a value to snake_case — same shape as the recently added slug, but with underscores instead of dashes so the result is safe as a variable name, YAML key, or database column name.
  • Wired into all three usual entry points: the {{ field | snake }} template pipe, FormResult.asString, and a chainable ResultValue.snake getter (result.getValue('title').snake).
  • Handles nested strings inside arrays/objects individually; FileProxy values are converted from the file name.
  • Unicode letters and numbers are preserved (Café Noël → café_noël) so it stays useful for non-English input.

Behavior

Given input Hello, World! → hello_world
Given input ---My Note (2024) → my_note_2024
Given input __foo___bar__ → foo_bar
Given input Café Noël 2024 → café_noël_2024

Test plan

  • npm run test — all 205 tests pass (including 5 new parser tests and 8 new ResultValue.snake tests)
  • npm run build — lint + svelte-check + esbuild bundle all green (only pre-existing warnings)
  • Manual: apply {{ title | snake }} inside a form template and confirm the output

Generated by Claude Code

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-apps

greptile-apps Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR adds a snake template transformation that mirrors the existing slug transformation but produces snake_case output (underscores instead of dashes). It also fixes the slug template pipe to use applyPerString — which correctly handles arrays by converting each element individually rather than stringifying the whole array first (which would silently drop the comma separator and merge values into a single token).

  • Adds toSnake function and wires it into the template schema, executeTransformation switch, and ResultValue.snake getter.
  • Introduces applyPerString helper that also fixes the pre-existing array and FileProxy handling bug in the slug template pipe.
  • Ships 13 new tests covering strings, unicode, arrays, FileProxy, empty input, and method chaining.

Confidence Score: 5/5

Safe 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.

Important Files Changed

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]
Loading

Reviews (2): Last reviewed commit: "fix: preserve array/FileProxy structure ..." | Re-trigger Greptile

Comment thread src/core/template/templateParser.ts Outdated
`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.
@danielo515
danielo515 merged commit 91451d9 into master Jul 22, 2026
2 checks passed
@danielo515
danielo515 deleted the claude/stoic-allen-uq2lhz branch July 22, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant