/** * Figma property mapping DSL * * Provides helpers for mapping Figma component properties to code props. * Inspired by Figma Code Connect's API. * * @example * ```tsx * import { defineFragment, figma } from '@fragments-sdk/cli/core'; * * export default defineFragment({ * component: Button, * meta: { * name: 'Button', * description: 'Primary action trigger', * category: 'actions', * figma: 'https://figma.com/file/abc/Design?node-id=1-2', * figmaProps: { * children: figma.string('Label'), * disabled: figma.boolean('Disabled'), * variant: figma.enum('Type', { * 'Primary': 'primary', * 'Secondary': 'secondary', * }), * }, * }, * // ... * }); * ``` */ import type { FigmaStringMapping, FigmaBooleanMapping, FigmaEnumMapping, FigmaInstanceMapping, FigmaChildrenMapping, FigmaTextContentMapping, } from './types.js'; /** * Map a Figma text property to a string prop. * * @param figmaProperty - The name of the text property in Figma * @returns A string mapping descriptor * * @example * ```tsx * figmaProps: { * label: figma.string('Button Text'), * placeholder: figma.string('Placeholder'), * } * ``` */ function string(figmaProperty: string): FigmaStringMapping { return { __type: 'figma-string', figmaProperty, }; } /** * Map a Figma boolean property to a boolean prop. * Optionally map true/false to different values. * * @param figmaProperty - The name of the boolean property in Figma * @param valueMapping - Optional mapping of true/false to other values * @returns A boolean mapping descriptor * * @example * ```tsx * figmaProps: { * disabled: figma.boolean('Disabled'), * // Map boolean to string values * size: figma.boolean('Large', { true: 'lg', false: 'md' }), * } * ``` */ function boolean( figmaProperty: string, valueMapping?: { true: unknown; false: unknown } ): FigmaBooleanMapping { return { __type: 'figma-boolean', figmaProperty, valueMapping, }; } /** * Map a Figma variant property to an enum prop. * * @param figmaProperty - The name of the variant property in Figma * @param valueMapping - Mapping of Figma values to code values * @returns An enum mapping descriptor * * @example * ```tsx * figmaProps: { * variant: figma.enum('Type', { * 'Primary': 'primary', * 'Secondary': 'secondary', * 'Outline': 'outline', * }), * size: figma.enum('Size', { * 'Small': 'sm', * 'Medium': 'md', * 'Large': 'lg', * }), * } * ``` */ function enumValue>( figmaProperty: string, valueMapping: T ): FigmaEnumMapping { return { __type: 'figma-enum', figmaProperty, valueMapping, }; } /** * Reference a nested Figma component instance. * Use this when a prop accepts a component that's represented * as an instance swap in Figma. * * @param figmaProperty - The name of the instance property in Figma * @returns An instance mapping descriptor * * @example * ```tsx * figmaProps: { * icon: figma.instance('Icon'), * avatar: figma.instance('Avatar'), * } * ``` */ function instance(figmaProperty: string): FigmaInstanceMapping { return { __type: 'figma-instance', figmaProperty, }; } /** * Render children from specific Figma layers. * Use this when children are represented as named layers in Figma. * * @param layers - Array of layer names to include as children * @returns A children mapping descriptor * * @example * ```tsx * figmaProps: { * children: figma.children(['Title', 'Description', 'Actions']), * } * ``` */ function children(layers: string[]): FigmaChildrenMapping { return { __type: 'figma-children', layers, }; } /** * Extract text content from a Figma text layer. * Use this when a prop should be the actual text from a layer. * * @param layer - The name of the text layer in Figma * @returns A text content mapping descriptor * * @example * ```tsx * figmaProps: { * title: figma.textContent('Header Text'), * description: figma.textContent('Body Text'), * } * ``` */ function textContent(layer: string): FigmaTextContentMapping { return { __type: 'figma-text-content', layer, }; } /** * Figma property mapping helpers. * * Use these to define how Figma properties map to your component props. * The mappings are used for: * - Generating accurate code snippets in Figma Dev Mode * - AI agents understanding the design-to-code relationship * - Automated design verification */ export const figma = { string, boolean, enum: enumValue, instance, children, textContent, } as const; /** * Helper type to check if a value is a Figma prop mapping */ export function isFigmaPropMapping( value: unknown ): value is FigmaStringMapping | FigmaBooleanMapping | FigmaEnumMapping | FigmaInstanceMapping | FigmaChildrenMapping | FigmaTextContentMapping { if (typeof value !== 'object' || value === null || !('__type' in value)) { return false; } const typeValue = (value as Record).__type; return typeof typeValue === 'string' && typeValue.startsWith('figma-'); } /** * Resolve a Figma prop mapping to an actual value given Figma property values. * * @param mapping - The Figma prop mapping * @param figmaValues - Object containing Figma property values * @returns The resolved value for the code prop */ export function resolveFigmaMapping( mapping: FigmaStringMapping | FigmaBooleanMapping | FigmaEnumMapping | FigmaInstanceMapping | FigmaChildrenMapping | FigmaTextContentMapping, figmaValues: Record ): unknown { switch (mapping.__type) { case 'figma-string': return figmaValues[mapping.figmaProperty] ?? ''; case 'figma-boolean': { const boolValue = figmaValues[mapping.figmaProperty] as boolean; if (mapping.valueMapping) { return boolValue ? mapping.valueMapping.true : mapping.valueMapping.false; } return boolValue; } case 'figma-enum': { const enumKey = figmaValues[mapping.figmaProperty] as string; return mapping.valueMapping[enumKey] ?? enumKey; } case 'figma-instance': // Instance mappings return the instance reference return figmaValues[mapping.figmaProperty]; case 'figma-children': // Children mappings return array of layer contents return mapping.layers.map((layer) => figmaValues[layer]); case 'figma-text-content': return figmaValues[mapping.layer] ?? ''; default: return undefined; } }