$npx -y skills add vercel-labs/json-render --skill directivesPre-built custom directives for json-render — formatting, math, string manipulation, and i18n. Use when working with @json-render/directives, defining custom directives with defineDirective, or adding $format, $math, $concat, $count, $truncate, $pluralize, $join, or $t to specs.
| 1 | # @json-render/directives |
| 2 | |
| 3 | Pre-built custom directives for `@json-render/core`. Drop them into your catalog and renderer to add formatting, math, string manipulation, and i18n. |
| 4 | |
| 5 | ## Quick Start |
| 6 | |
| 7 | ```typescript |
| 8 | import { standardDirectives } from '@json-render/directives'; |
| 9 | |
| 10 | // Wire into prompt generation |
| 11 | const prompt = catalog.prompt({ directives: standardDirectives }); |
| 12 | |
| 13 | // Wire into the renderer (React example) |
| 14 | import { JSONUIProvider, Renderer } from '@json-render/react'; |
| 15 | |
| 16 | <JSONUIProvider registry={registry} directives={standardDirectives}> |
| 17 | <Renderer spec={spec} registry={registry} /> |
| 18 | </JSONUIProvider> |
| 19 | ``` |
| 20 | |
| 21 | To add factory directives like `createI18nDirective`, spread the array: |
| 22 | |
| 23 | ```typescript |
| 24 | import { standardDirectives, createI18nDirective } from '@json-render/directives'; |
| 25 | |
| 26 | const directives = [...standardDirectives, createI18nDirective(config)]; |
| 27 | ``` |
| 28 | |
| 29 | ## Defining Custom Directives |
| 30 | |
| 31 | Use `defineDirective` from `@json-render/core`: |
| 32 | |
| 33 | ```typescript |
| 34 | import { defineDirective, resolvePropValue } from '@json-render/core'; |
| 35 | import { z } from 'zod'; |
| 36 | |
| 37 | const doubleDirective = defineDirective({ |
| 38 | name: '$double', |
| 39 | description: 'Double a numeric value.', |
| 40 | schema: z.object({ |
| 41 | $double: z.unknown(), |
| 42 | }), |
| 43 | resolve(value, ctx) { |
| 44 | const resolved = resolvePropValue(value.$double, ctx); |
| 45 | return (resolved as number) * 2; |
| 46 | }, |
| 47 | }); |
| 48 | ``` |
| 49 | |
| 50 | Rules: |
| 51 | - Name must start with `$` |
| 52 | - Name must not conflict with built-in keys (`$state`, `$cond`, `$computed`, `$template`, `$item`, `$index`, `$bindState`, `$bindItem`) |
| 53 | - Resolvers should call `resolvePropValue` on sub-values to support composition |
| 54 | |
| 55 | ## Built-in Directives |
| 56 | |
| 57 | ### `$format` — Locale-aware value formatting |
| 58 | |
| 59 | Formats values using `Intl` formatters. Supports `date`, `currency`, `number`, and `percent`. |
| 60 | |
| 61 | ```json |
| 62 | { "$format": "currency", "value": { "$state": "/cart/total" }, "currency": "USD" } |
| 63 | { "$format": "date", "value": { "$state": "/user/createdAt" } } |
| 64 | { "$format": "number", "value": 1234567, "notation": "compact" } |
| 65 | { "$format": "percent", "value": 0.75 } |
| 66 | { "$format": "date", "value": { "$state": "/post/createdAt" }, "style": "relative" } |
| 67 | ``` |
| 68 | |
| 69 | Fields: `$format` (date | currency | number | percent), `value` (any expression), `locale?` (string), `currency?` (string, default "USD"), `notation?` (string), `style?` ("relative" for relative dates), `options?` (extra Intl options). |
| 70 | |
| 71 | ### `$math` — Arithmetic operations |
| 72 | |
| 73 | ```json |
| 74 | { "$math": "add", "a": { "$state": "/subtotal" }, "b": { "$state": "/tax" } } |
| 75 | { "$math": "round", "a": 3.7 } |
| 76 | ``` |
| 77 | |
| 78 | Operations: `add`, `subtract`, `multiply`, `divide`, `mod`, `min`, `max`, `round`, `floor`, `ceil`, `abs`. Unary ops (`round`, `floor`, `ceil`, `abs`) only use `a`. Division by zero returns `0`. |
| 79 | |
| 80 | Fields: `$math` (operation enum), `a?` (first operand, default 0), `b?` (second operand, default 0). |
| 81 | |
| 82 | ### `$concat` — String concatenation |
| 83 | |
| 84 | ```json |
| 85 | { "$concat": [{ "$state": "/user/firstName" }, " ", { "$state": "/user/lastName" }] } |
| 86 | ``` |
| 87 | |
| 88 | Fields: `$concat` (array of values to resolve and join into a string). |
| 89 | |
| 90 | ### `$count` — Array/string length |
| 91 | |
| 92 | ```json |
| 93 | { "$count": { "$state": "/cart/items" } } |
| 94 | ``` |
| 95 | |
| 96 | Returns `.length` of arrays or strings, `0` for other types. |
| 97 | |
| 98 | Fields: `$count` (value to count). |
| 99 | |
| 100 | ### `$truncate` — Text truncation |
| 101 | |
| 102 | ```json |
| 103 | { "$truncate": { "$state": "/post/body" }, "length": 140, "suffix": "..." } |
| 104 | ``` |
| 105 | |
| 106 | Fields: `$truncate` (value to truncate), `length?` (max chars, default 100), `suffix?` (string, default "..."). |
| 107 | |
| 108 | ### `$pluralize` — Singular/plural forms |
| 109 | |
| 110 | ```json |
| 111 | { "$pluralize": { "$state": "/cart/itemCount" }, "one": "item", "other": "items", "zero": "no items" } |
| 112 | ``` |
| 113 | |
| 114 | Output: `"3 items"`, `"1 item"`, or `"no items"`. |
| 115 | |
| 116 | Fields: `$pluralize` (count value), `one` (singular label), `other` (plural label), `zero?` (zero label). |
| 117 | |
| 118 | ### `$join` — Join array elements |
| 119 | |
| 120 | ```json |
| 121 | { "$join": { "$state": "/tags" }, "separator": ", " } |
| 122 | ``` |
| 123 | |
| 124 | Fields: `$join` (array to join), `separator?` (string, default ", "). |
| 125 | |
| 126 | ### `createI18nDirective` — Internationalization factory |
| 127 | |
| 128 | ```typescript |
| 129 | import { createI18nDirective } from '@json-render/directives'; |
| 130 | |
| 131 | const tDirective = createI18nDirective({ |
| 132 | locale: 'en', |
| 133 | messages: { |
| 134 | en: { "greeting": "Hello, {{name}}!", "checkout.submit": "Place Order" }, |
| 135 | es: { "greeting": "Hola, {{name}}!", "checkout.submit": "Realizar Pedido" }, |
| 136 | }, |
| 137 | fallbackLocale: 'en', |
| 138 | }); |
| 139 | ``` |
| 140 | |
| 141 | Usage in specs: |
| 142 | |
| 143 | ```json |
| 144 | { "$t": "checkout.submit" } |
| 145 | { "$t": "greeting", "params": { "name": { "$state": "/user/name" } } } |
| 146 | ``` |
| 147 | |
| 148 | Fields: `$t` (translation key), `params?` (interpolation parameters, values accept expressions). |
| 149 | |
| 150 | Config: `locale` (current locale), `messages` (Record<locale, Record<key, string>>), `fallbackLocale?` (fallback when ke |