terrazzo-plugin-figma-json 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 dgtlntv
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,253 @@
1
+ # terrazzo-plugin-figma-json
2
+
3
+ A [Terrazzo](https://terrazzo.app) plugin that converts W3C DTCG design tokens into Figma-compatible JSON format for import into Figma Variables.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install terrazzo-plugin-figma-json
9
+ # or
10
+ pnpm add terrazzo-plugin-figma-json
11
+ ```
12
+
13
+ ## Basic Usage
14
+
15
+ Add the plugin to your `terrazzo.config.ts`:
16
+
17
+ ```typescript
18
+ import { defineConfig } from "@terrazzo/cli";
19
+ import figmaJson from "terrazzo-plugin-figma-json";
20
+
21
+ export default defineConfig({
22
+ outDir: "./tokens/",
23
+ plugins: [
24
+ figmaJson({
25
+ filename: "tokens.figma.json",
26
+ }),
27
+ ],
28
+ });
29
+ ```
30
+
31
+ Run the Terrazzo build:
32
+
33
+ ```bash
34
+ npx tz build
35
+ ```
36
+
37
+ ## Configuration Options
38
+
39
+ | Option | Type | Default | Description |
40
+ |--------|------|---------|-------------|
41
+ | `filename` | `string` | `"tokens.figma.json"` | Output filename (used as suffix when using resolver) |
42
+ | `exclude` | `string[]` | `[]` | Glob patterns to exclude tokens from output |
43
+ | `transform` | `(token) => unknown` | `undefined` | Custom transform function to override token values |
44
+ | `tokenName` | `(token) => string` | `undefined` | Custom function to control token names in output |
45
+ | `skipBuild` | `boolean` | `false` | Skip generating the output file |
46
+ | `remBasePx` | `number` | `16` | Base pixel value for rem to px conversion |
47
+ | `warnOnUnsupported` | `boolean` | `true` | Log warnings for unsupported token types |
48
+ | `preserveReferences` | `boolean` | `true` | Preserve token aliases in output (see [Alias Handling](#alias-handling)) |
49
+
50
+ ## Output Structure
51
+
52
+ When using a resolver file (recommended), the plugin automatically splits output by resolver sets and modifier contexts:
53
+
54
+ ```
55
+ dist/
56
+ ├── primitive.figma.json # From "primitive" set
57
+ ├── semantic.figma.json # From "semantic" set
58
+ ├── colorScheme-light.figma.json
59
+ ├── colorScheme-dark.figma.json
60
+ └── breakpoint-small.figma.json
61
+ ```
62
+
63
+ Without a resolver, all tokens are output to a single file.
64
+
65
+ ## Supported Token Types
66
+
67
+ | DTCG Type | Figma Variable Type | Transformation |
68
+ |-----------|---------------------|----------------|
69
+ | `color` | Color | sRGB and HSL pass through; other color spaces converted to sRGB |
70
+ | `dimension` | Number | `px` values pass through; `rem` converted to `px` |
71
+ | `duration` | Number | `s` values pass through; `ms` converted to `s` |
72
+ | `fontFamily` | String | Strings pass through; arrays truncated to first element |
73
+ | `fontWeight` | Number or String | Values pass through with validation |
74
+ | `number` | Number or Boolean | Numbers pass through; use `com.figma.type: "boolean"` for booleans |
75
+ | `typography` | Split tokens | Split into fontFamily, fontSize, fontWeight, lineHeight, letterSpacing |
76
+
77
+ ### Typography Token Splitting
78
+
79
+ Figma doesn't support composite typography tokens, so they're automatically split into individual sub-tokens:
80
+
81
+ **Input:**
82
+ ```json
83
+ {
84
+ "typography": {
85
+ "$type": "typography",
86
+ "heading": {
87
+ "$value": {
88
+ "fontFamily": "Inter",
89
+ "fontSize": { "value": 24, "unit": "px" },
90
+ "fontWeight": 700,
91
+ "lineHeight": 1.2,
92
+ "letterSpacing": { "value": 0, "unit": "px" }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ **Output:**
100
+ ```json
101
+ {
102
+ "typography": {
103
+ "heading": {
104
+ "fontFamily": { "$type": "fontFamily", "$value": "Inter" },
105
+ "fontSize": { "$type": "dimension", "$value": { "value": 24, "unit": "px" } },
106
+ "fontWeight": { "$type": "fontWeight", "$value": 700 },
107
+ "lineHeight": { "$type": "dimension", "$value": { "value": 29, "unit": "px" } },
108
+ "letterSpacing": { "$type": "dimension", "$value": { "value": 0, "unit": "px" } }
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ Note: `lineHeight` is converted from a unitless multiplier to absolute px (see [Figma Limitations](#figma-limitations)).
115
+
116
+ ## Unsupported Token Types
117
+
118
+ The following DTCG token types are **not supported** by Figma and will be skipped:
119
+
120
+ - `shadow`, `border`, `gradient`, `transition`, `strokeStyle`, `cubicBezier`
121
+
122
+ ## Alias Handling
123
+
124
+ When `preserveReferences: true` (default):
125
+
126
+ - **Same-file references**: Use curly brace syntax in `$value` (e.g., `"{color.primary}"`)
127
+ - **Cross-file references**: Use resolved `$value` + `com.figma.aliasData` extension
128
+
129
+ ```json
130
+ {
131
+ "color": {
132
+ "brand": {
133
+ "$type": "color",
134
+ "$value": "{color.palette.blue.500}"
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ For cross-collection aliases (referencing tokens in a different output file):
141
+
142
+ ```json
143
+ {
144
+ "color": {
145
+ "text": {
146
+ "$type": "color",
147
+ "$value": { "colorSpace": "srgb", "components": [0.2, 0.4, 0.8], "alpha": 1 },
148
+ "$extensions": {
149
+ "com.figma.aliasData": {
150
+ "targetVariableSetName": "primitive",
151
+ "targetVariableName": "color/palette/blue/500"
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ ```
158
+
159
+ ## Color Space Conversion
160
+
161
+ Figma only supports **sRGB** and **HSL** color spaces. Other color spaces are converted to sRGB:
162
+
163
+ - Display P3, Rec2020, A98 RGB, ProPhoto RGB → sRGB
164
+ - OKLCH, OkLab, Lab, LCH → sRGB
165
+ - XYZ-D65, XYZ-D50, HWB, sRGB-linear → sRGB
166
+
167
+ Colors outside the sRGB gamut will be clipped with a warning.
168
+
169
+ ## Boolean Tokens
170
+
171
+ Use the `number` type with the `com.figma.type` extension:
172
+
173
+ ```json
174
+ {
175
+ "feature-flags": {
176
+ "$type": "number",
177
+ "dark-mode-enabled": {
178
+ "$value": 1,
179
+ "$extensions": { "com.figma.type": "boolean" }
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ - `0` → `false`
186
+ - Non-zero → `true`
187
+
188
+ ## Examples
189
+
190
+ ### Excluding Tokens
191
+
192
+ ```typescript
193
+ figmaJson({
194
+ exclude: [
195
+ "internal.*", // Exclude "internal" group
196
+ "*.deprecated.*", // Exclude "deprecated" tokens
197
+ ],
198
+ })
199
+ ```
200
+
201
+ ### Custom Token Names
202
+
203
+ ```typescript
204
+ figmaJson({
205
+ tokenName: (token) => token.id.replace("color.", "brand.color."),
206
+ })
207
+ ```
208
+
209
+ ### Custom Transform
210
+
211
+ ```typescript
212
+ figmaJson({
213
+ transform: (token) => {
214
+ if (token.id === "color.special") {
215
+ return { colorSpace: "srgb", components: [1, 0, 0], alpha: 1 };
216
+ }
217
+ return undefined; // Use default transformation
218
+ },
219
+ })
220
+ ```
221
+
222
+ ## Figma Limitations
223
+
224
+ Figma Variables have constraints that differ from the W3C DTCG specification. This plugin handles these automatically, but be aware of the following:
225
+
226
+ ### lineHeight Conversion
227
+
228
+ W3C DTCG specifies `lineHeight` as a unitless number (multiplier relative to fontSize, e.g., `1.5`). Figma requires `lineHeight` to be a dimension with px units.
229
+
230
+ The plugin converts by multiplying: `lineHeight × fontSize`. For example, `lineHeight: 1.5` with `fontSize: 16px` becomes `24px`.
231
+
232
+ **Trade-off**: Any reference to a primitive number token for lineHeight is lost, since the computed px value cannot maintain an alias to the original multiplier.
233
+
234
+ ### $root Token Naming
235
+
236
+ The DTCG spec uses `$root` for default mode values. Figma doesn't allow `$` in variable names, so the plugin converts `$root` to `root` in the output.
237
+
238
+ ### Cross-File Alias Resolution
239
+
240
+ When a token uses a JSON pointer (`$ref`) that points to another token using curly-brace alias syntax, the intermediate reference is lost. For example:
241
+
242
+ ```json
243
+ {
244
+ "a": { "$value": { "x": "{b.x}" } },
245
+ "c": { "$ref": "#/a/$value/x" }
246
+ }
247
+ ```
248
+
249
+ Token `c` will resolve to `b.x`, not `a.x`. This is a limitation of how the Terrazzo parser resolves references.
250
+
251
+ ## License
252
+
253
+ MIT
@@ -0,0 +1,18 @@
1
+ import type { BuildHookOptions, Resolver } from '@terrazzo/parser';
2
+ import type { FigmaJsonPluginOptions } from './types.js';
3
+ export interface BuildOptions {
4
+ exclude: FigmaJsonPluginOptions['exclude'];
5
+ tokenName?: FigmaJsonPluginOptions['tokenName'];
6
+ getTransforms: BuildHookOptions['getTransforms'];
7
+ preserveReferences?: FigmaJsonPluginOptions['preserveReferences'];
8
+ resolver?: Resolver;
9
+ }
10
+ /**
11
+ * Build the Figma-compatible JSON output from transformed tokens.
12
+ * Requires a resolver file - legacy mode is not supported.
13
+ * Always returns output split by resolver structure (sets and modifier contexts).
14
+ *
15
+ * @returns Map of output name to JSON string (e.g., "primitive" -> "{...}")
16
+ */
17
+ export default function buildFigmaJson({ getTransforms, exclude, tokenName, preserveReferences, resolver, }: BuildOptions): Map<string, string>;
18
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEnE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AASzD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,sBAAsB,CAAC,WAAW,CAAC,CAAC;IAChD,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC;IACjD,kBAAkB,CAAC,EAAE,sBAAsB,CAAC,oBAAoB,CAAC,CAAC;IAClE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAsSD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EACrC,aAAa,EACb,OAAO,EACP,SAAS,EACT,kBAAyB,EACzB,QAAQ,GACT,EAAE,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqOpC"}
@@ -0,0 +1,30 @@
1
+ export declare const PLUGIN_NAME = "terrazzo-plugin-figma-json";
2
+ export declare const FORMAT_ID = "figma-json";
3
+ /**
4
+ * Internal metadata property keys used for token processing.
5
+ * These are added during transform and removed during build.
6
+ */
7
+ export declare const INTERNAL_KEYS: {
8
+ /** Target token ID for alias references */
9
+ readonly ALIAS_OF: "_aliasOf";
10
+ /** Parent token ID for split sub-tokens (e.g., typography) */
11
+ readonly SPLIT_FROM: "_splitFrom";
12
+ /** Token ID for split sub-tokens */
13
+ readonly TOKEN_ID: "_tokenId";
14
+ };
15
+ /**
16
+ * Token types supported by Figma.
17
+ */
18
+ export declare const SUPPORTED_TYPES: readonly ["color", "dimension", "duration", "fontFamily", "fontWeight", "number", "typography"];
19
+ export type SupportedType = (typeof SUPPORTED_TYPES)[number];
20
+ /**
21
+ * Token types that are not supported by Figma and will be dropped with a warning.
22
+ */
23
+ export declare const UNSUPPORTED_TYPES: readonly ["shadow", "border", "gradient", "transition", "strokeStyle", "cubicBezier"];
24
+ export type UnsupportedType = (typeof UNSUPPORTED_TYPES)[number];
25
+ /**
26
+ * Color spaces that Figma natively supports.
27
+ */
28
+ export declare const FIGMA_COLOR_SPACES: readonly ["srgb", "hsl"];
29
+ export type FigmaColorSpace = (typeof FIGMA_COLOR_SPACES)[number];
30
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,+BAA+B,CAAC;AAExD,eAAO,MAAM,SAAS,eAAe,CAAC;AAEtC;;;GAGG;AACH,eAAO,MAAM,aAAa;IACxB,2CAA2C;;IAE3C,8DAA8D;;IAE9D,oCAAoC;;CAE5B,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,eAAe,iGAQlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,iBAAiB,uFAAwF,CAAC;AAEvH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE;;GAEG;AACH,eAAO,MAAM,kBAAkB,0BAA2B,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { ConverterContext, ConverterResult } from '../types.js';
2
+ /**
3
+ * Convert a DTCG color value to Figma-compatible format.
4
+ * Figma only supports sRGB and HSL color spaces.
5
+ *
6
+ * @example
7
+ * // sRGB colors pass through unchanged
8
+ * convertColor({
9
+ * colorSpace: "srgb",
10
+ * components: [0.5, 0.5, 0.5],
11
+ * alpha: 1
12
+ * }, context);
13
+ * // => { value: { colorSpace: "srgb", components: [0.5, 0.5, 0.5], alpha: 1 } }
14
+ *
15
+ * @example
16
+ * // OKLCH colors are converted to sRGB
17
+ * convertColor({
18
+ * colorSpace: "oklch",
19
+ * components: [0.7, 0.15, 150]
20
+ * }, context);
21
+ * // => { value: { colorSpace: "srgb", components: [...], alpha: 1 } }
22
+ */
23
+ export declare function convertColor(value: unknown, context: ConverterContext): ConverterResult;
24
+ //# sourceMappingURL=color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/converters/color.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAkB,MAAM,aAAa,CAAC;AAoDrF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAoFvF"}
@@ -0,0 +1,17 @@
1
+ import type { ConverterContext, ConverterResult } from '../types.js';
2
+ /**
3
+ * Convert a DTCG dimension value to Figma-compatible format.
4
+ * Figma only supports px units.
5
+ *
6
+ * @example
7
+ * // px values pass through unchanged
8
+ * convertDimension({ value: 16, unit: "px" }, context);
9
+ * // => { value: { value: 16, unit: "px" } }
10
+ *
11
+ * @example
12
+ * // rem values are converted to px (default base: 16px)
13
+ * convertDimension({ value: 1.5, unit: "rem" }, context);
14
+ * // => { value: { value: 24, unit: "px" } }
15
+ */
16
+ export declare function convertDimension(value: unknown, context: ConverterContext): ConverterResult;
17
+ //# sourceMappingURL=dimension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dimension.d.ts","sourceRoot":"","sources":["../../src/converters/dimension.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGrE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAoD3F"}
@@ -0,0 +1,17 @@
1
+ import type { ConverterContext, ConverterResult } from '../types.js';
2
+ /**
3
+ * Convert a DTCG duration value to Figma-compatible format.
4
+ * Figma only supports seconds (s) unit.
5
+ *
6
+ * @example
7
+ * // s values pass through unchanged
8
+ * convertDuration({ value: 0.5, unit: "s" }, context);
9
+ * // => { value: { value: 0.5, unit: "s" } }
10
+ *
11
+ * @example
12
+ * // ms values are converted to s
13
+ * convertDuration({ value: 500, unit: "ms" }, context);
14
+ * // => { value: { value: 0.5, unit: "s" } }
15
+ */
16
+ export declare function convertDuration(value: unknown, context: ConverterContext): ConverterResult;
17
+ //# sourceMappingURL=duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../src/converters/duration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGrE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAoD1F"}
@@ -0,0 +1,17 @@
1
+ import type { ConverterContext, ConverterResult } from '../types.js';
2
+ /**
3
+ * Convert a DTCG fontFamily value to Figma-compatible format.
4
+ * Figma requires a single string, not an array.
5
+ *
6
+ * @example
7
+ * // String values pass through unchanged
8
+ * convertFontFamily("Inter", context);
9
+ * // => { value: "Inter" }
10
+ *
11
+ * @example
12
+ * // Arrays are truncated to the first element
13
+ * convertFontFamily(["Inter", "Helvetica", "sans-serif"], context);
14
+ * // => { value: "Inter" } (with warning about dropped fallbacks)
15
+ */
16
+ export declare function convertFontFamily(value: unknown, context: ConverterContext): ConverterResult;
17
+ //# sourceMappingURL=font-family.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-family.d.ts","sourceRoot":"","sources":["../../src/converters/font-family.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAErE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAqC5F"}
@@ -0,0 +1,17 @@
1
+ import type { ConverterContext, ConverterResult } from '../types.js';
2
+ /**
3
+ * Convert a DTCG fontWeight value to Figma-compatible format.
4
+ * Output type matches input type (string stays string, number stays number).
5
+ *
6
+ * @example
7
+ * // Number values pass through with validation (1-1000)
8
+ * convertFontWeight(400, context);
9
+ * // => { value: 400 }
10
+ *
11
+ * @example
12
+ * // String aliases pass through if valid
13
+ * convertFontWeight("bold", context);
14
+ * // => { value: "bold" }
15
+ */
16
+ export declare function convertFontWeight(value: unknown, context: ConverterContext): ConverterResult;
17
+ //# sourceMappingURL=font-weight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-weight.d.ts","sourceRoot":"","sources":["../../src/converters/font-weight.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AA0BrE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAqC5F"}
@@ -0,0 +1,25 @@
1
+ import type { TokenNormalized } from '@terrazzo/parser';
2
+ import { type SupportedType } from '../constants.js';
3
+ import type { ConverterContext, ConverterResult } from '../types.js';
4
+ /**
5
+ * Converter function signature.
6
+ */
7
+ export type Converter = (value: unknown, context: ConverterContext) => ConverterResult;
8
+ /**
9
+ * Check if a token type is supported by Figma.
10
+ */
11
+ export declare function isSupportedType(type: string): type is SupportedType;
12
+ /**
13
+ * Check if a value is an alias reference (curly brace syntax).
14
+ */
15
+ export declare function isAlias(value: unknown): value is string;
16
+ /**
17
+ * Convert a token value to Figma-compatible format.
18
+ *
19
+ * @param token - The normalized token
20
+ * @param value - The token value to convert
21
+ * @param context - Converter context with logger and options
22
+ * @returns Converted value or skip indicator
23
+ */
24
+ export declare function convertToken(token: TokenNormalized, value: unknown, context: ConverterContext): ConverterResult;
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/converters/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAgC,KAAK,aAAa,EAAqB,MAAM,iBAAiB,CAAC;AACtG,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AASrE;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,KAAK,eAAe,CAAC;AAevF;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,aAAa,CAEnE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEvD;AA6CD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAgE/G"}
@@ -0,0 +1,55 @@
1
+ import type { ConverterContext, ConverterResult, DTCGDimensionValue } from '../types.js';
2
+ /**
3
+ * Context for lineHeight conversion, extending the base converter context
4
+ * with the fontSize value needed for calculating absolute lineHeight.
5
+ */
6
+ export interface LineHeightConverterContext extends ConverterContext {
7
+ /**
8
+ * The resolved fontSize dimension value from the typography token.
9
+ * Required to calculate absolute lineHeight from the multiplier.
10
+ * Should already be converted to px units.
11
+ */
12
+ fontSize?: DTCGDimensionValue;
13
+ }
14
+ /**
15
+ * Convert a W3C DTCG lineHeight value to Figma-compatible format.
16
+ *
17
+ * ## W3C DTCG vs Figma Incompatibility
18
+ *
19
+ * The W3C DTCG specification defines lineHeight as a **number** type -
20
+ * a unitless multiplier relative to fontSize (e.g., `1.5` means 1.5× the
21
+ * font size). This matches CSS behavior where `line-height: 1.5` is unitless.
22
+ *
23
+ * However, Figma Variables require lineHeight to be a **dimension** type
24
+ * with explicit px units. There is no way to represent a unitless multiplier
25
+ * in Figma's variable system.
26
+ *
27
+ * ## Conversion Strategy
28
+ *
29
+ * This converter calculates the absolute lineHeight by multiplying the
30
+ * unitless multiplier with the fontSize:
31
+ *
32
+ * `absoluteLineHeight = lineHeight × fontSize`
33
+ *
34
+ * For example: `lineHeight: 1.5` with `fontSize: 16px` → `24px`
35
+ *
36
+ * ## Trade-off: Loss of Token Reference
37
+ *
38
+ * When converting a multiplier to an absolute dimension, any reference to
39
+ * a primitive number token is lost. This is unavoidable because:
40
+ *
41
+ * 1. Figma does not support unitless multipliers for lineHeight
42
+ * 2. We must compute a concrete px value at build time
43
+ * 3. The computed value cannot maintain an alias to the original number token
44
+ *
45
+ * This approach is the most token-setup-agnostic solution, as it works
46
+ * regardless of how the source tokens are structured.
47
+ *
48
+ * @example
49
+ * // Input: W3C DTCG typography with number lineHeight
50
+ * // lineHeight: 1.5, fontSize: { value: 16, unit: "px" }
51
+ * convertLineHeight(1.5, { ...context, fontSize: { value: 16, unit: "px" } });
52
+ * // Output: { value: { value: 24, unit: "px" } }
53
+ */
54
+ export declare function convertLineHeight(value: unknown, context: LineHeightConverterContext): ConverterResult;
55
+ //# sourceMappingURL=line-height.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-height.d.ts","sourceRoot":"","sources":["../../src/converters/line-height.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEzF;;;GAGG;AACH,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAClE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,0BAA0B,GAAG,eAAe,CAoDtG"}
@@ -0,0 +1,17 @@
1
+ import type { ConverterContext, ConverterResult } from '../types.js';
2
+ /**
3
+ * Convert a DTCG number value to Figma-compatible format.
4
+ * Can output as Boolean if token has com.figma.type extension set to "boolean".
5
+ *
6
+ * @example
7
+ * // Regular number token
8
+ * convertNumber(1.5, context) // => { value: 1.5 }
9
+ *
10
+ * @example
11
+ * // Boolean extension: 0 becomes false, non-zero becomes true
12
+ * // Token with $extensions: { "com.figma": { "type": "boolean" } }
13
+ * convertNumber(0, contextWithBooleanExt) // => { value: false }
14
+ * convertNumber(1, contextWithBooleanExt) // => { value: true }
15
+ */
16
+ export declare function convertNumber(value: unknown, context: ConverterContext): ConverterResult;
17
+ //# sourceMappingURL=number.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/converters/number.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAErE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CA6BxF"}
@@ -0,0 +1,19 @@
1
+ import type { ConverterContext, ConverterResult } from '../types.js';
2
+ /**
3
+ * Convert a DTCG typography value to Figma-compatible format.
4
+ * Typography tokens are split into individual sub-tokens since Figma
5
+ * doesn't support the composite typography type.
6
+ *
7
+ * @example
8
+ * // Input typography token
9
+ * convertTypography({
10
+ * fontFamily: "Inter",
11
+ * fontSize: { value: 16, unit: "px" },
12
+ * fontWeight: 400,
13
+ * lineHeight: 1.5,
14
+ * letterSpacing: { value: 0, unit: "px" }
15
+ * }, context);
16
+ * // => { value: undefined, split: true, subTokens: [...] }
17
+ */
18
+ export declare function convertTypography(value: unknown, context: ConverterContext): ConverterResult;
19
+ //# sourceMappingURL=typography.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typography.d.ts","sourceRoot":"","sources":["../../src/converters/typography.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAY,MAAM,aAAa,CAAC;AA+C/E;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAiI5F"}
@@ -0,0 +1,39 @@
1
+ import type { Plugin } from '@terrazzo/parser';
2
+ import type { FigmaJsonPluginOptions } from './types.js';
3
+ export * from './build.js';
4
+ export * from './constants.js';
5
+ export * from './transform.js';
6
+ export * from './types.js';
7
+ export * from './utils.js';
8
+ /**
9
+ * Terrazzo plugin to convert DTCG design tokens to Figma-compatible JSON format.
10
+ *
11
+ * @example
12
+ * // Basic usage
13
+ * import { defineConfig } from "@terrazzo/cli";
14
+ * import figmaJson from "terrazzo-plugin-figma-json";
15
+ *
16
+ * export default defineConfig({
17
+ * plugins: [
18
+ * figmaJson({ filename: "tokens.figma.json" }),
19
+ * ],
20
+ * });
21
+ *
22
+ * @example
23
+ * // With all options
24
+ * figmaJson({
25
+ * filename: "design-tokens.figma.json",
26
+ * exclude: ["internal.*", "deprecated.*"],
27
+ * remBasePx: 16,
28
+ * warnOnUnsupported: true,
29
+ * preserveReferences: true,
30
+ * tokenName: (token) => token.id.replace("color.", "brand."),
31
+ * transform: (token) => {
32
+ * if (token.id === "special.token") return { custom: true };
33
+ * return undefined; // Use default transformation
34
+ * },
35
+ * });
36
+ *
37
+ */
38
+ export default function figmaJsonPlugin(options?: FigmaJsonPluginOptions): Plugin;
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CA4ChF"}