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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/constants.ts","../src/utils.ts","../src/build.ts","../src/converters/color.ts","../src/converters/dimension.ts","../src/converters/duration.ts","../src/converters/font-family.ts","../src/converters/font-weight.ts","../src/converters/number.ts","../src/converters/line-height.ts","../src/converters/typography.ts","../src/converters/index.ts","../src/transform.ts","../src/index.ts"],"sourcesContent":["export const PLUGIN_NAME = 'terrazzo-plugin-figma-json';\n\nexport const FORMAT_ID = 'figma-json';\n\n/**\n * Internal metadata property keys used for token processing.\n * These are added during transform and removed during build.\n */\nexport const INTERNAL_KEYS = {\n /** Target token ID for alias references */\n ALIAS_OF: '_aliasOf',\n /** Parent token ID for split sub-tokens (e.g., typography) */\n SPLIT_FROM: '_splitFrom',\n /** Token ID for split sub-tokens */\n TOKEN_ID: '_tokenId',\n} as const;\n\n/**\n * Token types supported by Figma.\n */\nexport const SUPPORTED_TYPES = [\n 'color',\n 'dimension',\n 'duration',\n 'fontFamily',\n 'fontWeight',\n 'number',\n 'typography',\n] as const;\n\nexport type SupportedType = (typeof SUPPORTED_TYPES)[number];\n\n/**\n * Token types that are not supported by Figma and will be dropped with a warning.\n */\nexport const UNSUPPORTED_TYPES = ['shadow', 'border', 'gradient', 'transition', 'strokeStyle', 'cubicBezier'] as const;\n\nexport type UnsupportedType = (typeof UNSUPPORTED_TYPES)[number];\n\n/**\n * Color spaces that Figma natively supports.\n */\nexport const FIGMA_COLOR_SPACES = ['srgb', 'hsl'] as const;\n\nexport type FigmaColorSpace = (typeof FIGMA_COLOR_SPACES)[number];\n","import type { Resolver } from '@terrazzo/parser';\nimport wcmatch from 'wildcard-match';\nimport { INTERNAL_KEYS } from './constants.js';\nimport type {\n DTCGColorValue,\n DTCGDimensionValue,\n DTCGDurationValue,\n DTCGTypographyValue,\n PartialAliasOf,\n TokenWithPartialAlias,\n} from './types.js';\n\n/**\n * Create an exclude matcher function from glob patterns.\n *\n * @param patterns - Array of glob patterns to match against token IDs\n * @returns A function that returns true if the token ID should be excluded\n */\nexport function createExcludeMatcher(patterns: string[] | undefined): (tokenId: string) => boolean {\n return patterns?.length ? wcmatch(patterns) : () => false;\n}\n\n/**\n * Type guard to check if the resolver has a usable configuration.\n * Terrazzo creates a default resolver even without a resolver file,\n * but it has empty contexts that cause errors when used.\n *\n * @param resolver - The resolver from terrazzo parser\n * @returns true if resolver has user-defined sets or modifiers with contexts\n */\nexport function hasValidResolverConfig(resolver: Resolver | undefined): resolver is Resolver {\n if (!resolver?.source || !resolver.listPermutations) {\n return false;\n }\n\n const source = resolver.source;\n const sets = source.sets ?? {};\n const modifiers = source.modifiers ?? {};\n\n const hasUserSets = Object.keys(sets).some((name) => name !== 'allTokens');\n const hasModifierContexts = Object.values(modifiers).some(\n (mod) => mod.contexts && Object.keys(mod.contexts).length > 0,\n );\n\n return hasUserSets || hasModifierContexts;\n}\n\n/**\n * Build default input from resolver's modifiers.\n * Creates an input object with each modifier set to its default value.\n *\n * @param resolverSource - The resolver source configuration\n * @returns Input object for resolver.apply() with default modifier values\n */\nexport function buildDefaultInput(resolverSource: NonNullable<Resolver['source']>): Record<string, string> {\n const input: Record<string, string> = {};\n if (resolverSource.modifiers) {\n for (const [modifierName, modifier] of Object.entries(resolverSource.modifiers)) {\n if (modifier.default) {\n input[modifierName] = modifier.default;\n }\n }\n }\n return input;\n}\n\n/**\n * Remove internal metadata properties from a parsed token value.\n * These properties are used for internal processing and should not appear in output.\n *\n * @param parsedValue - Token value object to clean (mutated in place)\n */\nexport function removeInternalMetadata(parsedValue: Record<string, unknown>): void {\n delete parsedValue[INTERNAL_KEYS.ALIAS_OF];\n delete parsedValue[INTERNAL_KEYS.SPLIT_FROM];\n delete parsedValue[INTERNAL_KEYS.TOKEN_ID];\n}\n\n/**\n * Safely parse a transform value that may be a JSON string or already an object.\n * Returns the parsed value or null if parsing fails.\n *\n * @param value - The transform value (string or object)\n * @returns Parsed value or null on error\n */\nexport function parseTransformValue(value: unknown): any {\n if (typeof value !== 'string') {\n return value;\n }\n try {\n return JSON.parse(value);\n } catch {\n return null;\n }\n}\n\n/**\n * Extract partialAliasOf from a token if present.\n * This property is added by terrazzo parser for composite tokens but not in public types.\n */\nexport function getPartialAliasOf(token: unknown): PartialAliasOf | undefined {\n if (token && typeof token === 'object' && 'partialAliasOf' in token) {\n const value = (token as TokenWithPartialAlias).partialAliasOf;\n if (value && typeof value === 'object') {\n return value;\n }\n }\n return undefined;\n}\n\n/**\n * Type guard to validate DTCGColorValue structure.\n */\nexport function isDTCGColorValue(value: unknown): value is DTCGColorValue {\n if (!value || typeof value !== 'object') {\n return false;\n }\n const v = value as Record<string, unknown>;\n if (typeof v.colorSpace !== 'string') {\n return false;\n }\n if (!Array.isArray(v.components) || v.components.length !== 3) {\n return false;\n }\n for (const c of v.components) {\n if (c !== 'none' && typeof c !== 'number') {\n return false;\n }\n }\n if (v.alpha !== undefined && v.alpha !== 'none' && typeof v.alpha !== 'number') {\n return false;\n }\n return true;\n}\n\n/**\n * Type guard to validate DTCGDimensionValue structure.\n */\nexport function isDTCGDimensionValue(value: unknown): value is DTCGDimensionValue {\n if (!value || typeof value !== 'object') {\n return false;\n }\n const v = value as Record<string, unknown>;\n return typeof v.value === 'number' && typeof v.unit === 'string';\n}\n\n/**\n * Type guard to validate DTCGDurationValue structure.\n */\nexport function isDTCGDurationValue(value: unknown): value is DTCGDurationValue {\n if (!value || typeof value !== 'object') {\n return false;\n }\n const v = value as Record<string, unknown>;\n return typeof v.value === 'number' && typeof v.unit === 'string';\n}\n\n/**\n * Type guard to validate DTCGTypographyValue structure.\n * Only checks that it's an object - individual properties are validated during conversion.\n */\nexport function isDTCGTypographyValue(value: unknown): value is DTCGTypographyValue {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n","import type { BuildHookOptions, Resolver } from '@terrazzo/parser';\nimport { FORMAT_ID, INTERNAL_KEYS } from './constants.js';\nimport type { FigmaJsonPluginOptions } from './types.js';\nimport {\n buildDefaultInput,\n createExcludeMatcher,\n hasValidResolverConfig,\n parseTransformValue,\n removeInternalMetadata,\n} from './utils.js';\n\nexport interface BuildOptions {\n exclude: FigmaJsonPluginOptions['exclude'];\n tokenName?: FigmaJsonPluginOptions['tokenName'];\n getTransforms: BuildHookOptions['getTransforms'];\n preserveReferences?: FigmaJsonPluginOptions['preserveReferences'];\n resolver?: Resolver;\n}\n\n/**\n * Set a nested property on an object using dot-notation path.\n * Creates intermediate objects as needed.\n *\n * @param obj - The object to modify\n * @param path - Dot-notation path (e.g., \"color.primary.base\")\n * @param value - The value to set at the path\n *\n * @example\n * const obj = {};\n * setNestedProperty(obj, \"color.primary\", { $value: \"#ff0000\" });\n * // obj = { color: { primary: { $value: \"#ff0000\" } } }\n */\nfunction setNestedProperty(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.');\n if (parts.length === 0) {\n return;\n }\n\n let current = obj;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (!(part in current)) {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n\n const lastPart = parts[parts.length - 1]!;\n current[lastPart] = value;\n}\n\n/**\n * Convert a dot-notation token ID to Figma's slash notation.\n *\n * @param tokenId - Token ID in dot notation\n * @returns Token ID in slash notation for Figma\n *\n * @example\n * toFigmaVariableName(\"dimension.200\") // \"dimension/200\"\n * toFigmaVariableName(\"color.primary.base\") // \"color/primary/base\"\n */\nfunction toFigmaVariableName(tokenId: string): string {\n return tokenId.replace(/\\./g, '/');\n}\n\n/**\n * Convert $root in a token ID to root for Figma compatibility.\n * DTCG uses $root for default values, but Figma doesn't support $ in names.\n *\n * @param path - Token path that may contain $root\n * @returns Path with $root replaced by root\n *\n * @example\n * normalizeRootInPath(\"color.border.warning.$root\") // \"color.border.warning.root\"\n * normalizeRootInPath(\"color.primary\") // \"color.primary\" (unchanged)\n */\nfunction normalizeRootInPath(path: string): string {\n return path.replace(/\\.\\$root\\b/g, '.root');\n}\n\n/**\n * Token source tracking info.\n */\ntype SourceInfo = {\n source: string;\n isModifier: boolean;\n modifierName?: string;\n contextName?: string;\n};\n\ninterface HandleAliasReferenceOptions {\n parsedValue: Record<string, unknown>;\n aliasOf: string;\n sourceName: string;\n tokenSources: Map<string, SourceInfo[]>;\n tokenOutputPaths: Map<string, string>;\n preserveReferences: boolean;\n}\n\n/**\n * Handle alias references by setting the appropriate $value or $extensions.\n * Mutates parsedValue in place.\n *\n * - Same-file references: Sets $value to curly brace syntax (e.g., \"{color.primary}\")\n * - Cross-file references: Keeps resolved $value and adds com.figma.aliasData extension\n *\n * The function checks for target token in this order:\n * 1. Current context (same-file reference)\n * 2. Set sources only (cross-file reference to primitive/semantic sets)\n * 3. Never references other modifier contexts (e.g., dark won't reference light)\n *\n * @param options - Configuration for alias handling\n * @param options.parsedValue - Token value object to modify (mutated)\n * @param options.aliasOf - Target token ID this token references\n * @param options.sourceName - Name of the current output file/collection\n * @param options.tokenSources - Map of token IDs to their source info\n * @param options.tokenOutputPaths - Map of token IDs to their output paths\n * @param options.preserveReferences - Whether to preserve references (false = no-op)\n */\nfunction handleAliasReference({\n parsedValue,\n aliasOf,\n sourceName,\n tokenSources,\n tokenOutputPaths,\n preserveReferences,\n}: HandleAliasReferenceOptions): void {\n if (!preserveReferences || !aliasOf) {\n return;\n }\n\n // Normalize aliasOf to remove $root for lookups (terrazzo uses normalized IDs)\n const normalizedAliasOf = aliasOf.replace(/\\.\\$root\\b/g, '');\n // Get target's output path, or normalize $root -> root in the original aliasOf\n const targetOutputPath = tokenOutputPaths.get(normalizedAliasOf) ?? normalizeRootInPath(aliasOf);\n\n // Find the target token's sources, handling split sub-tokens by looking up parent\n let targetSources = tokenSources.get(normalizedAliasOf);\n if (!targetSources) {\n // Try parent tokens for split sub-tokens (e.g., \"typography.heading.fontFamily\")\n const parts = normalizedAliasOf.split('.');\n while (parts.length > 1 && !targetSources) {\n parts.pop();\n targetSources = tokenSources.get(parts.join('.'));\n }\n }\n\n if (!targetSources?.length) {\n // Target token not found in any source - leave value as-is\n return;\n }\n\n // Check if target exists in current source (same-file reference)\n const inCurrentSource = targetSources.some((s) => s.source === sourceName);\n if (inCurrentSource) {\n // Same file reference: use curly brace syntax\n parsedValue.$value = `{${targetOutputPath}}`;\n return;\n }\n\n // Check for SET sources only (not modifier contexts)\n // We never want to reference other modifier contexts (e.g., dark shouldn't reference light)\n const setSource = targetSources.find((s) => !s.isModifier);\n if (setSource) {\n // Cross-file reference to a set: use resolved value + aliasData\n const extensions = (parsedValue.$extensions ?? {}) as Record<string, unknown>;\n extensions['com.figma.aliasData'] = {\n targetVariableSetName: setSource.source,\n targetVariableName: toFigmaVariableName(targetOutputPath),\n };\n parsedValue.$extensions = extensions;\n }\n}\n\n/**\n * Token ID info including whether it came from a $root key.\n */\ninterface TokenIdInfo {\n /** The normalized token ID (without $root, as terrazzo uses) */\n id: string;\n /** The output path (with $root preserved for proper JSON structure) */\n outputPath: string;\n}\n\n/**\n * Extract token IDs from a resolver group (token definitions).\n * Recursively walks the group structure to find all token IDs.\n *\n * Handles $root tokens specially per DTCG spec:\n * - Token ID uses parent path (e.g., \"color.primary\" for \"color.primary.$root\")\n * - Output path uses \"root\" without $ for Figma compatibility\n *\n * @param group - Object containing token definitions or nested groups\n * @param prefix - Current path prefix for recursion\n * @returns Array of token ID info with both normalized ID and output path\n *\n * @example\n * extractTokenIds({ color: { primary: { $value: \"#ff0000\" } } })\n * // [{ id: \"color.primary\", outputPath: \"color.primary\" }]\n */\nfunction extractTokenIds(group: Record<string, unknown>, prefix = ''): TokenIdInfo[] {\n const ids: TokenIdInfo[] = [];\n\n for (const [key, value] of Object.entries(group)) {\n // Skip $ properties (like $type, $description, $schema, etc.)\n // But handle $root specially\n if (key.startsWith('$') && key !== '$root') {\n continue;\n }\n\n // Build paths:\n // - normalizedPath: what terrazzo uses as the token ID (no $root)\n // - outputPath: what we output to JSON (uses \"root\" without $ for Figma compatibility)\n const outputKey = key === '$root' ? 'root' : key;\n const outputPath = prefix ? `${prefix}.${outputKey}` : outputKey;\n const normalizedPath = key === '$root' ? prefix : outputPath;\n\n // Check if this is a token (has $value)\n if (value && typeof value === 'object' && '$value' in value) {\n // Only add if we have a valid normalized path\n if (normalizedPath) {\n ids.push({ id: normalizedPath, outputPath });\n }\n // $root is always a leaf token, don't recurse\n // Regular tokens with $value are also leaves\n } else if (value && typeof value === 'object') {\n // Recurse into nested groups (only for non-token objects)\n ids.push(...extractTokenIds(value as Record<string, unknown>, outputPath));\n }\n }\n\n return ids;\n}\n\ninterface TokenSourceMaps {\n tokenSources: Map<string, SourceInfo[]>;\n tokenOutputPaths: Map<string, string>;\n allContexts: Set<string>;\n}\n\n/**\n * Build maps tracking which tokens belong to which sources.\n * Processes both sets and modifier contexts from the resolver source.\n *\n * @param resolverSource - The resolver source configuration\n * @returns Maps for token sources, output paths, and all context keys\n */\nfunction buildTokenSourceMaps(resolverSource: NonNullable<Resolver['source']>): TokenSourceMaps {\n const tokenSources = new Map<string, SourceInfo[]>();\n const tokenOutputPaths = new Map<string, string>();\n const allContexts = new Set<string>();\n\n function addTokenSource(tokenId: string, outputPath: string, info: SourceInfo) {\n const existing = tokenSources.get(tokenId);\n if (existing) {\n existing.push(info);\n } else {\n tokenSources.set(tokenId, [info]);\n }\n if (!tokenOutputPaths.has(tokenId)) {\n tokenOutputPaths.set(tokenId, outputPath);\n }\n }\n\n // Process sets\n if (resolverSource.sets) {\n for (const [setName, set] of Object.entries(resolverSource.sets)) {\n if (set.sources) {\n for (const source of set.sources) {\n const tokenInfos = extractTokenIds(source as Record<string, unknown>);\n for (const { id, outputPath } of tokenInfos) {\n addTokenSource(id, outputPath, {\n source: setName,\n isModifier: false,\n });\n }\n }\n }\n }\n }\n\n // Process modifiers\n if (resolverSource.modifiers) {\n for (const [modifierName, modifier] of Object.entries(resolverSource.modifiers)) {\n if (modifier.contexts) {\n for (const [contextName, contextSources] of Object.entries(modifier.contexts)) {\n const contextKey = `${modifierName}-${contextName}`;\n allContexts.add(contextKey);\n\n if (Array.isArray(contextSources)) {\n for (const source of contextSources) {\n const tokenInfos = extractTokenIds(source as Record<string, unknown>);\n for (const { id, outputPath } of tokenInfos) {\n addTokenSource(id, outputPath, {\n source: contextKey,\n isModifier: true,\n modifierName,\n contextName,\n });\n }\n }\n }\n }\n }\n }\n }\n\n return { tokenSources, tokenOutputPaths, allContexts };\n}\n\n/**\n * Build the Figma-compatible JSON output from transformed tokens.\n * Requires a resolver file - legacy mode is not supported.\n * Always returns output split by resolver structure (sets and modifier contexts).\n *\n * @returns Map of output name to JSON string (e.g., \"primitive\" -> \"{...}\")\n */\nexport default function buildFigmaJson({\n getTransforms,\n exclude,\n tokenName,\n preserveReferences = true,\n resolver,\n}: BuildOptions): Map<string, string> {\n const shouldExclude = createExcludeMatcher(exclude);\n\n // When no valid resolver config, fall back to single output under \"default\" key\n if (!hasValidResolverConfig(resolver)) {\n // Get all transforms without resolver context\n const transforms = getTransforms({ format: FORMAT_ID });\n if (transforms.length === 0) {\n return new Map();\n }\n\n const output: Record<string, unknown> = {};\n for (const transform of transforms) {\n if (!transform.token) {\n continue;\n }\n\n const tokenId = transform.token.id;\n if (shouldExclude(tokenId)) {\n continue;\n }\n\n const outputName = tokenName?.(transform.token) ?? tokenId;\n const parsedValue = parseTransformValue(transform.value);\n if (!parsedValue) {\n continue;\n }\n\n removeInternalMetadata(parsedValue);\n setNestedProperty(output, outputName, parsedValue);\n }\n\n const result = new Map<string, string>();\n result.set('default', JSON.stringify(output, null, 2));\n return result;\n }\n\n // After hasValidResolverConfig, resolver and source are guaranteed to exist\n const resolverSource = resolver.source;\n if (!resolverSource) {\n return new Map();\n }\n\n // Build maps tracking token sources and output paths\n const { tokenSources, tokenOutputPaths, allContexts } = buildTokenSourceMaps(resolverSource);\n\n // Group outputs by source\n const outputBySource = new Map<string, Record<string, unknown>>();\n\n // Initialize empty outputs for all contexts (so empty files are created)\n for (const contextKey of allContexts) {\n outputBySource.set(contextKey, {});\n }\n\n // Get transforms using default input (for set tokens)\n const defaultInput = buildDefaultInput(resolverSource);\n const defaultTransforms = getTransforms({\n format: FORMAT_ID,\n input: defaultInput,\n });\n\n // Process set tokens using default transforms\n for (const transform of defaultTransforms) {\n const parsedValue = parseTransformValue(transform.value);\n if (!parsedValue) {\n continue;\n }\n\n // Handle split sub-tokens (e.g., typography.text.primary.fontFamily)\n // These don't have a token object but have INTERNAL_KEYS.SPLIT_FROM metadata\n let tokenId: string;\n let outputName: string;\n let aliasOf: string | undefined;\n let sourceLookupId: string;\n\n if (transform.token) {\n tokenId = transform.token.id;\n // Use tracked output path (preserves $root) if no custom tokenName\n outputName = tokenName?.(transform.token) ?? tokenOutputPaths.get(tokenId) ?? tokenId;\n aliasOf = parsedValue[INTERNAL_KEYS.ALIAS_OF] ?? transform.token.aliasOf;\n sourceLookupId = tokenId;\n } else if (parsedValue[INTERNAL_KEYS.SPLIT_FROM] && parsedValue[INTERNAL_KEYS.TOKEN_ID]) {\n // Split sub-token: use parent's source\n tokenId = parsedValue[INTERNAL_KEYS.TOKEN_ID];\n // For split tokens, replace parent ID with parent's output path in the token ID\n const parentId = parsedValue[INTERNAL_KEYS.SPLIT_FROM];\n const parentOutputPath = tokenOutputPaths.get(parentId);\n if (parentOutputPath && parentOutputPath !== parentId) {\n // Replace parent ID prefix with parent output path (to preserve $root)\n outputName = parentOutputPath + tokenId.slice(parentId.length);\n } else {\n outputName = tokenId;\n }\n aliasOf = parsedValue[INTERNAL_KEYS.ALIAS_OF];\n sourceLookupId = parentId; // Look up source using parent token ID\n } else {\n // Unknown transform without token - skip\n continue;\n }\n\n if (shouldExclude(tokenId)) {\n continue;\n }\n\n const sources = tokenSources.get(sourceLookupId) ?? [];\n const setSource = sources.find((s) => !s.isModifier);\n if (!setSource) {\n continue; // Skip tokens that aren't in a set\n }\n\n const sourceName = setSource.source;\n let sourceOutput = outputBySource.get(sourceName);\n if (!sourceOutput) {\n sourceOutput = {};\n outputBySource.set(sourceName, sourceOutput);\n }\n\n // Get aliasOf from the transformed value or token (already set above)\n\n // Handle alias references based on preserveReferences setting\n if (aliasOf) {\n handleAliasReference({\n parsedValue,\n aliasOf,\n sourceName,\n tokenSources,\n tokenOutputPaths,\n preserveReferences,\n });\n }\n\n removeInternalMetadata(parsedValue);\n setNestedProperty(sourceOutput, outputName, parsedValue);\n }\n\n // Process modifier context tokens\n // Group modifier tokens by their context\n const modifierTokensByContext = new Map<string, Set<string>>();\n\n for (const [tokenId, sources] of tokenSources) {\n for (const sourceInfo of sources) {\n if (!sourceInfo.isModifier) {\n continue;\n }\n\n const contextKey = sourceInfo.source;\n const existing = modifierTokensByContext.get(contextKey);\n if (existing) {\n existing.add(tokenId);\n } else {\n modifierTokensByContext.set(contextKey, new Set([tokenId]));\n }\n }\n }\n\n // For each context, get transforms with the appropriate input\n for (const [contextKey, tokenIds] of modifierTokensByContext) {\n // Find the source info for this context\n let contextInfo: SourceInfo | undefined;\n for (const tokenId of tokenIds) {\n const sources = tokenSources.get(tokenId);\n contextInfo = sources?.find((s) => s.source === contextKey);\n if (contextInfo) {\n break;\n }\n }\n\n if (!contextInfo?.modifierName || !contextInfo?.contextName) {\n continue;\n }\n\n // Build input for this context (start with defaults, override specific modifier)\n const input: Record<string, string> = { ...defaultInput };\n input[contextInfo.modifierName] = contextInfo.contextName;\n\n // Get transforms for this context\n const contextTransforms = getTransforms({ format: FORMAT_ID, input });\n\n let contextOutput = outputBySource.get(contextKey);\n if (!contextOutput) {\n contextOutput = {};\n outputBySource.set(contextKey, contextOutput);\n }\n\n // Add tokens for this context\n for (const tokenId of tokenIds) {\n if (shouldExclude(tokenId)) {\n continue;\n }\n\n // Find the transform for this token (skip transforms without tokens - synthetic sub-tokens)\n const transform = contextTransforms.find((t) => t.token?.id === tokenId);\n if (!transform) {\n continue;\n }\n\n // Use tracked output path (preserves $root) if no custom tokenName\n const outputName = tokenName?.(transform.token) ?? tokenOutputPaths.get(tokenId) ?? tokenId;\n const parsedValue = parseTransformValue(transform.value);\n if (!parsedValue) {\n continue;\n }\n\n // Get aliasOf from the transformed value (set during transform step) or fall back to token\n const aliasOf = parsedValue[INTERNAL_KEYS.ALIAS_OF] ?? transform.token.aliasOf;\n\n // Handle alias references based on preserveReferences setting\n if (aliasOf) {\n handleAliasReference({\n parsedValue,\n aliasOf,\n sourceName: contextKey,\n tokenSources,\n tokenOutputPaths,\n preserveReferences,\n });\n }\n\n removeInternalMetadata(parsedValue);\n setNestedProperty(contextOutput, outputName, parsedValue);\n }\n }\n\n // Return split output by source\n const result = new Map<string, string>();\n for (const [sourceName, output] of outputBySource) {\n result.set(sourceName, JSON.stringify(output, null, 2));\n }\n return result;\n}\n","import Color from 'colorjs.io';\nimport { FIGMA_COLOR_SPACES, PLUGIN_NAME } from '../constants.js';\nimport type { ConverterContext, ConverterResult, DTCGColorValue } from '../types.js';\nimport { isDTCGColorValue } from '../utils.js';\n\n/**\n * Number of decimal places to round color components to.\n * 6 decimals provides sufficient precision while avoiding floating-point issues.\n */\nconst COLOR_PRECISION = 6;\n\n/**\n * Round a number to COLOR_PRECISION decimal places and clamp to [0, 1] range.\n * Prevents floating-point precision issues (e.g., 1.0000000000000007 -> 1).\n *\n * @param value - Color component value (typically 0-1 for sRGB)\n * @returns Rounded and clamped value in [0, 1] range\n */\nfunction roundAndClamp(value: number): number {\n const rounded = Math.round(value * 10 ** COLOR_PRECISION) / 10 ** COLOR_PRECISION;\n return Math.max(0, Math.min(1, rounded));\n}\n\n/**\n * Normalize color components: round to precision and clamp to valid range.\n * Applies roundAndClamp to each component in the RGB/HSL triplet.\n *\n * @param components - Array of 3 color component values\n * @returns Normalized triplet with values rounded and clamped\n */\nfunction normalizeComponents(components: [number, number, number]): [number, number, number] {\n return components.map(roundAndClamp) as [number, number, number];\n}\n\n/**\n * Map DTCG color space names to colorjs.io color space IDs.\n */\nconst DTCG_TO_COLORJS_SPACE: Record<string, string> = {\n srgb: 'srgb',\n 'srgb-linear': 'srgb-linear',\n hsl: 'hsl',\n hwb: 'hwb',\n lab: 'lab',\n lch: 'lch',\n oklab: 'oklab',\n oklch: 'oklch',\n 'display-p3': 'p3',\n 'a98-rgb': 'a98rgb',\n 'prophoto-rgb': 'prophoto',\n rec2020: 'rec2020',\n 'xyz-d65': 'xyz-d65',\n 'xyz-d50': 'xyz-d50',\n};\n\n/**\n * Convert a DTCG color value to Figma-compatible format.\n * Figma only supports sRGB and HSL color spaces.\n *\n * @example\n * // sRGB colors pass through unchanged\n * convertColor({\n * colorSpace: \"srgb\",\n * components: [0.5, 0.5, 0.5],\n * alpha: 1\n * }, context);\n * // => { value: { colorSpace: \"srgb\", components: [0.5, 0.5, 0.5], alpha: 1 } }\n *\n * @example\n * // OKLCH colors are converted to sRGB\n * convertColor({\n * colorSpace: \"oklch\",\n * components: [0.7, 0.15, 150]\n * }, context);\n * // => { value: { colorSpace: \"srgb\", components: [...], alpha: 1 } }\n */\nexport function convertColor(value: unknown, context: ConverterContext): ConverterResult {\n if (!isDTCGColorValue(value)) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid color value: expected object with colorSpace and components`,\n });\n return { value: undefined, skip: true };\n }\n const color = value;\n\n // If already in a Figma-compatible color space, pass through with alpha normalization\n if (FIGMA_COLOR_SPACES.includes(color.colorSpace as (typeof FIGMA_COLOR_SPACES)[number])) {\n // Handle 'none' values in components\n const components = color.components.map((c) => (c === 'none' ? 0 : c)) as [number, number, number];\n\n // Only normalize sRGB components (which are in 0-1 range), not HSL (which uses different ranges)\n const normalizedComponents = color.colorSpace === 'srgb' ? normalizeComponents(components) : components;\n\n return {\n value: {\n ...color,\n components: normalizedComponents,\n alpha: color.alpha === 'none' ? 1 : (color.alpha ?? 1),\n },\n };\n }\n\n // Handle 'none' values - treat as 0 for conversion purposes\n const components = color.components.map((c) => (c === 'none' ? 0 : c)) as [number, number, number];\n\n // Get the colorjs.io color space ID\n const colorjsSpace = DTCG_TO_COLORJS_SPACE[color.colorSpace];\n if (!colorjsSpace) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has unknown color space: ${color.colorSpace}`,\n });\n return { value: undefined, skip: true };\n }\n\n try {\n // Create color in the source color space\n const sourceColor = new Color(colorjsSpace, components);\n\n // Convert to sRGB\n const srgbColor = sourceColor.to('srgb');\n\n // Check if gamut clipping is needed\n if (!srgbColor.inGamut()) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" color was clipped to sRGB gamut (original color space: ${color.colorSpace})`,\n });\n srgbColor.toGamut({ method: 'css' });\n }\n\n // Get the sRGB coordinates and normalize them\n const srgbChannels = normalizeComponents(srgbColor.coords as [number, number, number]);\n\n // Log info about color space conversion (expected behavior for non-sRGB colors)\n context.logger.info({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" color converted from ${color.colorSpace} to sRGB`,\n });\n\n const result: DTCGColorValue = {\n colorSpace: 'srgb',\n components: srgbChannels,\n alpha: color.alpha === 'none' ? 1 : (color.alpha ?? 1),\n };\n\n return { value: result };\n } catch (err) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" color conversion failed: ${err instanceof Error ? err.message : String(err)}`,\n });\n return { value: undefined, skip: true };\n }\n}\n","import { PLUGIN_NAME } from '../constants.js';\nimport type { ConverterContext, ConverterResult } from '../types.js';\nimport { isDTCGDimensionValue } from '../utils.js';\n\n/**\n * Convert a DTCG dimension value to Figma-compatible format.\n * Figma only supports px units.\n *\n * @example\n * // px values pass through unchanged\n * convertDimension({ value: 16, unit: \"px\" }, context);\n * // => { value: { value: 16, unit: \"px\" } }\n *\n * @example\n * // rem values are converted to px (default base: 16px)\n * convertDimension({ value: 1.5, unit: \"rem\" }, context);\n * // => { value: { value: 24, unit: \"px\" } }\n */\nexport function convertDimension(value: unknown, context: ConverterContext): ConverterResult {\n if (!isDTCGDimensionValue(value)) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid dimension value: expected object with value (number) and unit (string)`,\n });\n return { value: undefined, skip: true };\n }\n const dimension = value;\n\n // Validate numeric value\n if (!Number.isFinite(dimension.value)) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid dimension value: ${dimension.value}`,\n });\n return { value: undefined, skip: true };\n }\n\n // px passthrough\n if (dimension.unit === 'px') {\n return { value: dimension };\n }\n\n // rem to px conversion\n if (dimension.unit === 'rem') {\n const remBasePx = context.options.remBasePx ?? 16;\n const pxValue = dimension.value * remBasePx;\n\n context.logger.info({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" converted from ${dimension.value}rem to ${pxValue}px (base: ${remBasePx}px)`,\n });\n\n return {\n value: {\n value: pxValue,\n unit: 'px',\n },\n };\n }\n\n // Unknown unit - warn and skip\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has unsupported dimension unit: \"${dimension.unit}\". Figma only supports px units. Convert the value to px or use the 'transform' option to handle this token.`,\n });\n return { value: undefined, skip: true };\n}\n","import { PLUGIN_NAME } from '../constants.js';\nimport type { ConverterContext, ConverterResult } from '../types.js';\nimport { isDTCGDurationValue } from '../utils.js';\n\n/**\n * Convert a DTCG duration value to Figma-compatible format.\n * Figma only supports seconds (s) unit.\n *\n * @example\n * // s values pass through unchanged\n * convertDuration({ value: 0.5, unit: \"s\" }, context);\n * // => { value: { value: 0.5, unit: \"s\" } }\n *\n * @example\n * // ms values are converted to s\n * convertDuration({ value: 500, unit: \"ms\" }, context);\n * // => { value: { value: 0.5, unit: \"s\" } }\n */\nexport function convertDuration(value: unknown, context: ConverterContext): ConverterResult {\n if (!isDTCGDurationValue(value)) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid duration value: expected object with value (number) and unit (string)`,\n });\n return { value: undefined, skip: true };\n }\n const duration = value;\n\n // Validate numeric value\n if (!Number.isFinite(duration.value)) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid duration value: ${duration.value}`,\n });\n return { value: undefined, skip: true };\n }\n\n // s passthrough\n if (duration.unit === 's') {\n return { value: duration };\n }\n\n // ms to s conversion (lossless)\n if (duration.unit === 'ms') {\n const sValue = duration.value / 1000;\n\n // This is a lossless conversion, so just info level (not warning)\n context.logger.info({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" converted from ${duration.value}ms to ${sValue}s`,\n });\n\n return {\n value: {\n value: sValue,\n unit: 's',\n },\n };\n }\n\n // Unknown unit - warn and skip\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has unsupported duration unit: \"${duration.unit}\". Figma only supports seconds (s). Convert the value to seconds or use the 'transform' option to handle this token.`,\n });\n return { value: undefined, skip: true };\n}\n","import { PLUGIN_NAME } from '../constants.js';\nimport type { ConverterContext, ConverterResult } from '../types.js';\n\n/**\n * Convert a DTCG fontFamily value to Figma-compatible format.\n * Figma requires a single string, not an array.\n *\n * @example\n * // String values pass through unchanged\n * convertFontFamily(\"Inter\", context);\n * // => { value: \"Inter\" }\n *\n * @example\n * // Arrays are truncated to the first element\n * convertFontFamily([\"Inter\", \"Helvetica\", \"sans-serif\"], context);\n * // => { value: \"Inter\" } (with warning about dropped fallbacks)\n */\nexport function convertFontFamily(value: unknown, context: ConverterContext): ConverterResult {\n // String passthrough\n if (typeof value === 'string') {\n return { value };\n }\n\n // Array - take first element\n if (Array.isArray(value)) {\n if (value.length === 0) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has empty fontFamily array`,\n });\n return { value: undefined, skip: true };\n }\n\n const firstFont = value[0];\n\n if (value.length > 1) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" fontFamily array truncated to first element \"${firstFont}\" (dropped: ${value.slice(1).join(', ')})`,\n });\n }\n\n return { value: firstFont };\n }\n\n // Invalid value\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid fontFamily value: ${typeof value}`,\n });\n return { value: undefined, skip: true };\n}\n","import { PLUGIN_NAME } from '../constants.js';\nimport type { ConverterContext, ConverterResult } from '../types.js';\n\n/**\n * Valid string aliases for font weights as per W3C DTCG spec.\n */\nconst FONT_WEIGHT_ALIASES: Record<string, number> = {\n thin: 100,\n hairline: 100,\n 'extra-light': 200,\n 'ultra-light': 200,\n light: 300,\n normal: 400,\n regular: 400,\n book: 400,\n medium: 500,\n 'semi-bold': 600,\n 'demi-bold': 600,\n bold: 700,\n 'extra-bold': 800,\n 'ultra-bold': 800,\n black: 900,\n heavy: 900,\n 'extra-black': 950,\n 'ultra-black': 950,\n};\n\n/**\n * Convert a DTCG fontWeight value to Figma-compatible format.\n * Output type matches input type (string stays string, number stays number).\n *\n * @example\n * // Number values pass through with validation (1-1000)\n * convertFontWeight(400, context);\n * // => { value: 400 }\n *\n * @example\n * // String aliases pass through if valid\n * convertFontWeight(\"bold\", context);\n * // => { value: \"bold\" }\n */\nexport function convertFontWeight(value: unknown, context: ConverterContext): ConverterResult {\n // Number passthrough - validate range, output as 'number' type\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < 1 || value > 1000) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid fontWeight value: ${value} (must be 1-1000)`,\n });\n return { value: undefined, skip: true };\n }\n return { value, outputType: 'number' };\n }\n\n // String - validate against known aliases, output as 'string' type\n if (typeof value === 'string') {\n const normalized = value.toLowerCase();\n if (!(normalized in FONT_WEIGHT_ALIASES)) {\n const validAliases = Object.keys(FONT_WEIGHT_ALIASES).slice(0, 5).join(', ');\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has unknown fontWeight alias: \"${value}\". Valid aliases include: ${validAliases}, etc. Use a valid alias or a numeric weight (1-1000).`,\n });\n return { value: undefined, skip: true };\n }\n // Pass through the original string value - Figma accepts string font weights\n return { value, outputType: 'string' };\n }\n\n // Invalid type\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid fontWeight type: ${typeof value}`,\n });\n return { value: undefined, skip: true };\n}\n","import { PLUGIN_NAME } from '../constants.js';\nimport type { ConverterContext, ConverterResult } from '../types.js';\n\n/**\n * Convert a DTCG number value to Figma-compatible format.\n * Can output as Boolean if token has com.figma.type extension set to \"boolean\".\n *\n * @example\n * // Regular number token\n * convertNumber(1.5, context) // => { value: 1.5 }\n *\n * @example\n * // Boolean extension: 0 becomes false, non-zero becomes true\n * // Token with $extensions: { \"com.figma\": { \"type\": \"boolean\" } }\n * convertNumber(0, contextWithBooleanExt) // => { value: false }\n * convertNumber(1, contextWithBooleanExt) // => { value: true }\n */\nexport function convertNumber(value: unknown, context: ConverterContext): ConverterResult {\n if (typeof value !== 'number') {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid number value: ${typeof value}`,\n });\n return { value: undefined, skip: true };\n }\n\n if (!Number.isFinite(value)) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has non-finite number value: ${value}`,\n });\n return { value: undefined, skip: true };\n }\n\n // Check for boolean type override via com.figma.type extension\n // Per Figma docs: $extensions: { \"com.figma.type\": \"boolean\" }\n const figmaType = context.extensions?.['com.figma.type'];\n if (figmaType === 'boolean') {\n // Convert number to boolean: 0 = false, non-zero = true\n return { value: value !== 0 };\n }\n\n // Number passthrough\n return { value };\n}\n","import { PLUGIN_NAME } from '../constants.js';\nimport type { ConverterContext, ConverterResult, DTCGDimensionValue } from '../types.js';\n\n/**\n * Context for lineHeight conversion, extending the base converter context\n * with the fontSize value needed for calculating absolute lineHeight.\n */\nexport interface LineHeightConverterContext extends ConverterContext {\n /**\n * The resolved fontSize dimension value from the typography token.\n * Required to calculate absolute lineHeight from the multiplier.\n * Should already be converted to px units.\n */\n fontSize?: DTCGDimensionValue;\n}\n\n/**\n * Convert a W3C DTCG lineHeight value to Figma-compatible format.\n *\n * ## W3C DTCG vs Figma Incompatibility\n *\n * The W3C DTCG specification defines lineHeight as a **number** type -\n * a unitless multiplier relative to fontSize (e.g., `1.5` means 1.5× the\n * font size). This matches CSS behavior where `line-height: 1.5` is unitless.\n *\n * However, Figma Variables require lineHeight to be a **dimension** type\n * with explicit px units. There is no way to represent a unitless multiplier\n * in Figma's variable system.\n *\n * ## Conversion Strategy\n *\n * This converter calculates the absolute lineHeight by multiplying the\n * unitless multiplier with the fontSize:\n *\n * `absoluteLineHeight = lineHeight × fontSize`\n *\n * For example: `lineHeight: 1.5` with `fontSize: 16px` → `24px`\n *\n * ## Trade-off: Loss of Token Reference\n *\n * When converting a multiplier to an absolute dimension, any reference to\n * a primitive number token is lost. This is unavoidable because:\n *\n * 1. Figma does not support unitless multipliers for lineHeight\n * 2. We must compute a concrete px value at build time\n * 3. The computed value cannot maintain an alias to the original number token\n *\n * This approach is the most token-setup-agnostic solution, as it works\n * regardless of how the source tokens are structured.\n *\n * @example\n * // Input: W3C DTCG typography with number lineHeight\n * // lineHeight: 1.5, fontSize: { value: 16, unit: \"px\" }\n * convertLineHeight(1.5, { ...context, fontSize: { value: 16, unit: \"px\" } });\n * // Output: { value: { value: 24, unit: \"px\" } }\n */\nexport function convertLineHeight(value: unknown, context: LineHeightConverterContext): ConverterResult {\n // W3C DTCG specifies lineHeight as a number (unitless multiplier)\n if (typeof value !== 'number') {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid lineHeight value: expected number (per W3C DTCG spec), got ${typeof value}`,\n });\n return { value: undefined, skip: true };\n }\n\n // Validate the multiplier value\n if (!Number.isFinite(value)) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has non-finite lineHeight value: ${value}`,\n });\n return { value: undefined, skip: true };\n }\n\n // fontSize is required to calculate the absolute lineHeight\n if (!context.fontSize) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has lineHeight multiplier (${value}) but no fontSize is defined. Cannot calculate absolute lineHeight for Figma. Provide a fontSize in the typography token.`,\n });\n return { value: undefined, skip: true };\n }\n\n // Calculate absolute lineHeight: multiplier × fontSize\n const rawLineHeight = value * context.fontSize.value;\n\n // Round by default (roundLineHeight defaults to true)\n const shouldRound = context.options.roundLineHeight !== false;\n const absoluteLineHeight = shouldRound ? Math.round(rawLineHeight) : rawLineHeight;\n\n const roundingNote = shouldRound && rawLineHeight !== absoluteLineHeight ? ` (rounded from ${rawLineHeight})` : '';\n\n context.logger.info({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" lineHeight: ${value} × ${context.fontSize.value}px = ${absoluteLineHeight}px${roundingNote} (converted from W3C multiplier to Figma dimension)`,\n });\n\n return {\n value: {\n value: absoluteLineHeight,\n unit: 'px',\n },\n };\n}\n","import { PLUGIN_NAME } from '../constants.js';\nimport type { ConverterContext, ConverterResult, SubToken } from '../types.js';\nimport { isDTCGTypographyValue } from '../utils.js';\nimport { convertDimension } from './dimension.js';\nimport { convertFontFamily } from './font-family.js';\nimport { convertFontWeight } from './font-weight.js';\nimport { convertLineHeight } from './line-height.js';\n\n/**\n * Get the correct alias reference for a typography sub-property.\n * When a typography property references another typography token,\n * the alias needs to point to the corresponding sub-token.\n *\n * @param aliasOf - The referenced token ID, or undefined if not an alias\n * @param propertyName - The sub-property name (fontFamily, fontSize, etc.)\n * @param allTokens - Map of all tokens for type lookup\n * @returns Adjusted alias target, or undefined if not an alias\n *\n * @example\n * // If typography.base is a typography token:\n * getSubTokenAlias(\"typography.base\", \"fontFamily\", tokens)\n * // \"typography.base.fontFamily\"\n *\n * // If dimension.100 is a primitive:\n * getSubTokenAlias(\"dimension.100\", \"fontSize\", tokens)\n * // \"dimension.100\" (unchanged)\n */\nfunction getSubTokenAlias(\n aliasOf: string | undefined,\n propertyName: string,\n allTokens: Record<string, { $type?: string }> | undefined,\n): string | undefined {\n if (!aliasOf) {\n return undefined;\n }\n\n // Check if the referenced token is a typography token\n const referencedToken = allTokens?.[aliasOf];\n if (referencedToken?.$type === 'typography') {\n // The target is also a typography token that will be split\n // Append the property name to reference the correct sub-token\n return `${aliasOf}.${propertyName}`;\n }\n\n // Otherwise, return the alias as-is (it's a primitive token)\n return aliasOf;\n}\n\n/**\n * Convert a DTCG typography value to Figma-compatible format.\n * Typography tokens are split into individual sub-tokens since Figma\n * doesn't support the composite typography type.\n *\n * @example\n * // Input typography token\n * convertTypography({\n * fontFamily: \"Inter\",\n * fontSize: { value: 16, unit: \"px\" },\n * fontWeight: 400,\n * lineHeight: 1.5,\n * letterSpacing: { value: 0, unit: \"px\" }\n * }, context);\n * // => { value: undefined, split: true, subTokens: [...] }\n */\nexport function convertTypography(value: unknown, context: ConverterContext): ConverterResult {\n if (!isDTCGTypographyValue(value)) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has invalid typography value: expected object, got ${typeof value}`,\n });\n return { value: undefined, skip: true };\n }\n const typography = value;\n\n // Get partial alias information from the token (populated by terrazzo parser)\n // Note: partialAliasOf contains the FINAL alias targets after full resolution.\n // Intermediate references (like JSON pointer $refs to other tokens in the same file)\n // are resolved by the parser before we receive them. This is because:\n // 1. processTokens() resolves $ref and stores in refMap\n // 2. replaceNode() replaces $ref with the resolved curly-brace alias\n // 3. resolveAliases() processes the curly-brace alias and OVERWRITES the refMap entry\n // So we only see the final target, not intermediate JSON pointer targets.\n const partialAliasOf = context.partialAliasOf;\n\n const subTokens: SubToken[] = [];\n\n // Convert fontFamily\n if (typography.fontFamily !== undefined) {\n const aliasOf = getSubTokenAlias(partialAliasOf?.fontFamily, 'fontFamily', context.allTokens);\n\n const result = convertFontFamily(typography.fontFamily, {\n ...context,\n tokenId: `${context.tokenId}.fontFamily`,\n });\n if (!result.skip) {\n subTokens.push({\n idSuffix: 'fontFamily',\n $type: 'fontFamily',\n value: result.value,\n aliasOf,\n });\n }\n }\n\n // Convert fontSize (dimension)\n // We also store the resolved fontSize for lineHeight calculation\n let resolvedFontSize: { value: number; unit: string } | undefined;\n if (typography.fontSize !== undefined) {\n const aliasOf = getSubTokenAlias(partialAliasOf?.fontSize, 'fontSize', context.allTokens);\n\n const result = convertDimension(typography.fontSize, {\n ...context,\n tokenId: `${context.tokenId}.fontSize`,\n });\n if (!result.skip) {\n resolvedFontSize = result.value as { value: number; unit: string };\n subTokens.push({\n idSuffix: 'fontSize',\n $type: 'dimension',\n value: result.value,\n aliasOf,\n });\n }\n }\n\n // Convert fontWeight\n if (typography.fontWeight !== undefined) {\n const aliasOf = getSubTokenAlias(partialAliasOf?.fontWeight, 'fontWeight', context.allTokens);\n\n const result = convertFontWeight(typography.fontWeight, {\n ...context,\n tokenId: `${context.tokenId}.fontWeight`,\n });\n if (!result.skip) {\n subTokens.push({\n idSuffix: 'fontWeight',\n $type: result.outputType ?? 'fontWeight',\n value: result.value,\n aliasOf,\n });\n }\n }\n\n // Convert lineHeight (W3C number → Figma dimension)\n // Per W3C DTCG spec, lineHeight is a unitless number (multiplier).\n // Figma requires a dimension, so we compute: lineHeight × fontSize.\n // Note: This loses the reference to any primitive number token - see line-height.ts for details.\n if (typography.lineHeight !== undefined) {\n const result = convertLineHeight(typography.lineHeight, {\n ...context,\n tokenId: `${context.tokenId}.lineHeight`,\n fontSize: resolvedFontSize,\n });\n if (!result.skip) {\n subTokens.push({\n idSuffix: 'lineHeight',\n $type: 'dimension',\n value: result.value,\n // No aliasOf: the computed dimension cannot reference the original number token\n });\n }\n }\n\n // Convert letterSpacing (dimension)\n if (typography.letterSpacing !== undefined) {\n const aliasOf = getSubTokenAlias(partialAliasOf?.letterSpacing, 'letterSpacing', context.allTokens);\n\n const result = convertDimension(typography.letterSpacing, {\n ...context,\n tokenId: `${context.tokenId}.letterSpacing`,\n });\n if (!result.skip) {\n subTokens.push({\n idSuffix: 'letterSpacing',\n $type: 'dimension',\n value: result.value,\n aliasOf,\n });\n }\n }\n\n // If no sub-tokens were created, skip the token\n if (subTokens.length === 0) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" typography value has no valid sub-properties`,\n });\n return { value: undefined, skip: true };\n }\n\n return { value: undefined, split: true, subTokens };\n}\n","import type { TokenNormalized } from '@terrazzo/parser';\nimport { PLUGIN_NAME, SUPPORTED_TYPES, type SupportedType, UNSUPPORTED_TYPES } from '../constants.js';\nimport type { ConverterContext, ConverterResult } from '../types.js';\nimport { convertColor } from './color.js';\nimport { convertDimension } from './dimension.js';\nimport { convertDuration } from './duration.js';\nimport { convertFontFamily } from './font-family.js';\nimport { convertFontWeight } from './font-weight.js';\nimport { convertNumber } from './number.js';\nimport { convertTypography } from './typography.js';\n\n/**\n * Converter function signature.\n */\nexport type Converter = (value: unknown, context: ConverterContext) => ConverterResult;\n\n/**\n * Registry of converters by token type.\n */\nconst converters: Record<SupportedType, Converter> = {\n color: convertColor,\n dimension: convertDimension,\n duration: convertDuration,\n fontFamily: convertFontFamily,\n fontWeight: convertFontWeight,\n number: convertNumber,\n typography: convertTypography,\n};\n\n/**\n * Check if a token type is supported by Figma.\n */\nexport function isSupportedType(type: string): type is SupportedType {\n return SUPPORTED_TYPES.includes(type as SupportedType);\n}\n\n/**\n * Check if a value is an alias reference (curly brace syntax).\n */\nexport function isAlias(value: unknown): value is string {\n return typeof value === 'string' && value.startsWith('{') && value.endsWith('}');\n}\n\n/**\n * Extract the token ID from an alias reference.\n * @param alias - The alias string, e.g., \"{color.primary}\"\n * @returns The token ID, e.g., \"color.primary\"\n */\nfunction extractAliasTarget(alias: string): string {\n return alias.slice(1, -1);\n}\n\n/**\n * Validate an alias reference and return any warnings.\n */\nfunction validateAlias(alias: string, context: ConverterContext): { valid: boolean; warning?: string } {\n const targetId = extractAliasTarget(alias);\n\n // Check if target exists\n if (!context.allTokens) {\n // Can't validate without token map\n return { valid: true };\n }\n\n const targetToken = context.allTokens[targetId];\n if (!targetToken) {\n return {\n valid: false,\n warning: `Token \"${context.tokenId}\" references non-existent token \"${targetId}\". Check the token path for typos or ensure the referenced token is defined.`,\n };\n }\n\n // Check if target is a Figma-compatible type\n if (!isSupportedType(targetToken.$type)) {\n const isKnownUnsupported = UNSUPPORTED_TYPES.includes(targetToken.$type as (typeof UNSUPPORTED_TYPES)[number]);\n return {\n valid: false,\n warning: isKnownUnsupported\n ? `Token \"${context.tokenId}\" aliases unsupported type \"${targetToken.$type}\" (from \"${targetId}\"). This alias will be preserved but may not work in Figma. Consider referencing a supported token type instead.`\n : `Token \"${context.tokenId}\" aliases unknown type \"${targetToken.$type}\" (from \"${targetId}\"). This alias will be preserved but may not work in Figma. Verify the target token has a supported type.`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Convert a token value to Figma-compatible format.\n *\n * @param token - The normalized token\n * @param value - The token value to convert\n * @param context - Converter context with logger and options\n * @returns Converted value or skip indicator\n */\nexport function convertToken(token: TokenNormalized, value: unknown, context: ConverterContext): ConverterResult {\n const { $type } = token;\n\n // Handle missing $type\n if (!$type) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" is missing $type. Ensure all tokens have a valid $type defined either directly or inherited from a parent group.`,\n });\n return { value: undefined, skip: true };\n }\n\n // Handle undefined or null values\n if (value === undefined || value === null) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: `Token \"${context.tokenId}\" has no value (${value}). Ensure $value is defined for this token.`,\n });\n return { value: undefined, skip: true };\n }\n\n // Handle alias references - validate and pass through\n if (isAlias(value)) {\n const validation = validateAlias(value, context);\n\n if (validation.warning) {\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: validation.warning,\n });\n }\n\n // Always pass through the alias - Figma uses the same syntax\n // Even invalid aliases are preserved to avoid breaking references\n return { value };\n }\n\n // Check if type is supported\n if (!isSupportedType($type)) {\n const isKnownUnsupported = UNSUPPORTED_TYPES.includes($type as (typeof UNSUPPORTED_TYPES)[number]);\n\n if (context.options.warnOnUnsupported !== false) {\n const suggestion = isKnownUnsupported\n ? ` Consider excluding this token with the 'exclude' option, or use a supported type (color, dimension, duration, fontFamily, fontWeight, number).`\n : ` If this is a custom type, consider using the 'transform' option to convert it to a supported format.`;\n\n context.logger.warn({\n group: 'plugin',\n label: PLUGIN_NAME,\n message: isKnownUnsupported\n ? `Token \"${context.tokenId}\" has unsupported type \"${$type}\" and will be skipped.${suggestion}`\n : `Token \"${context.tokenId}\" has unknown type \"${$type}\" and will be skipped.${suggestion}`,\n });\n }\n\n return { value: undefined, skip: true };\n }\n\n // Get the converter for this type\n const converter = converters[$type];\n return converter(value, context);\n}\n","import type { TokenNormalized, TransformHookOptions } from '@terrazzo/parser';\nimport { FORMAT_ID, INTERNAL_KEYS } from './constants.js';\nimport { convertToken } from './converters/index.js';\nimport type { FigmaJsonPluginOptions, TokenExtensions } from './types.js';\nimport { createExcludeMatcher, getPartialAliasOf, hasValidResolverConfig } from './utils.js';\n\n/**\n * Register a transform with optional resolver input.\n * Encapsulates the conditional logic for input presence.\n */\nfunction registerTransform(\n setTransform: TransformHookOptions['setTransform'],\n id: string,\n value: string,\n input?: Record<string, string>,\n): void {\n if (input) {\n setTransform(id, { format: FORMAT_ID, value, input });\n } else {\n setTransform(id, { format: FORMAT_ID, value });\n }\n}\n\nexport interface TransformOptions {\n transform: TransformHookOptions;\n options: FigmaJsonPluginOptions;\n}\n\n/**\n * Filter extensions to only include Figma-specific ones (com.figma.*).\n * Removes non-Figma extensions to keep output clean.\n *\n * @param extensions - Token extensions object that may include various namespaces\n * @returns Object with only com.figma.* keys, or undefined if none exist\n *\n * @example\n * filterFigmaExtensions({ \"com.figma.type\": \"boolean\", \"custom.ext\": \"value\" })\n * // { \"com.figma.type\": \"boolean\" }\n */\nfunction filterFigmaExtensions(extensions: TokenExtensions | undefined): TokenExtensions | undefined {\n if (!extensions) {\n return undefined;\n }\n\n const figmaExtensions: TokenExtensions = {};\n let hasFigmaExtensions = false;\n\n for (const [key, value] of Object.entries(extensions)) {\n if (key.startsWith('com.figma')) {\n figmaExtensions[key] = value;\n hasFigmaExtensions = true;\n }\n }\n\n return hasFigmaExtensions ? figmaExtensions : undefined;\n}\n\n/**\n * Transform a single token and register it via setTransform.\n * Handles custom transforms, split tokens (typography), and alias references.\n *\n * @param token - The normalized token from terrazzo parser\n * @param rawValue - The resolved token value\n * @param aliasOf - Target token ID if this is an alias, undefined otherwise\n * @param options - Plugin configuration options\n * @param context - Plugin hook context with logger\n * @param allTokens - Map of all tokens for alias validation\n * @param setTransform - Terrazzo callback to register transformed value\n * @param input - Optional resolver input. When omitted, uses legacy mode without resolver.\n */\nfunction transformToken(\n token: TokenNormalized,\n rawValue: unknown,\n aliasOf: string | undefined,\n options: FigmaJsonPluginOptions,\n context: TransformHookOptions['context'],\n allTokens: Record<string, TokenNormalized>,\n setTransform: TransformHookOptions['setTransform'],\n input?: Record<string, string>,\n): void {\n // Allow custom transform to override\n const customValue = options.transform?.(token);\n if (customValue !== undefined) {\n registerTransform(setTransform, token.id, JSON.stringify(customValue), input);\n return;\n }\n\n const partialAliasOf = getPartialAliasOf(token);\n\n // Convert the token value (always resolve to final value)\n const result = convertToken(token, rawValue, {\n logger: context.logger,\n options,\n tokenId: token.id,\n extensions: token.$extensions,\n allTokens,\n originalValue: token.originalValue?.$value,\n partialAliasOf,\n });\n\n // Skip if converter indicates to skip\n if (result.skip) {\n return;\n }\n\n // Handle split tokens (e.g., typography)\n if (result.split && result.subTokens) {\n for (const subToken of result.subTokens) {\n const subId = `${token.id}.${subToken.idSuffix}`;\n const transformedValue: Record<string, unknown> = {\n $type: subToken.$type,\n $value: subToken.value,\n // Include metadata for build phase to identify split sub-tokens\n [INTERNAL_KEYS.SPLIT_FROM]: token.id, // Parent token ID for source lookup\n [INTERNAL_KEYS.TOKEN_ID]: subId, // This sub-token's ID\n };\n // Preserve alias reference for sub-token if it was a reference\n if (subToken.aliasOf) {\n transformedValue[INTERNAL_KEYS.ALIAS_OF] = subToken.aliasOf;\n }\n if (token.$description) {\n transformedValue.$description = token.$description;\n }\n const subTokenFigmaExtensions = filterFigmaExtensions(token.$extensions);\n if (subTokenFigmaExtensions) {\n transformedValue.$extensions = subTokenFigmaExtensions;\n }\n registerTransform(setTransform, subId, JSON.stringify(transformedValue), input);\n }\n return;\n }\n\n // Build the transformed token structure with resolved value\n const transformedValue: Record<string, unknown> = {\n $type: result.outputType ?? token.$type,\n $value: result.value,\n };\n if (token.$description) {\n transformedValue.$description = token.$description;\n }\n const figmaExtensions = filterFigmaExtensions(token.$extensions);\n if (figmaExtensions) {\n transformedValue.$extensions = figmaExtensions;\n }\n\n // Store aliasOf for build step to create Figma aliasData extension\n // Figma wants resolved $value + aliasData extension for cross-collection references\n if (aliasOf) {\n // Use originalValue.$value to get the direct reference (not fully resolved chain)\n // e.g., \"{dimension.size.height.baseline}\" instead of \"dimension.100\"\n const originalValueStr = token.originalValue?.$value;\n let directAliasOf = aliasOf;\n if (typeof originalValueStr === 'string' && originalValueStr.startsWith('{') && originalValueStr.endsWith('}')) {\n directAliasOf = originalValueStr.slice(1, -1);\n }\n transformedValue[INTERNAL_KEYS.ALIAS_OF] = directAliasOf;\n } else if (token.$type === 'color' && partialAliasOf) {\n // For colors without a direct alias, check if all components reference the same token\n // This handles JSON pointer references like { \"$ref\": \"#/color/palette/white/$value/colorSpace\" }\n const colorPartialAlias = partialAliasOf as {\n colorSpace?: string;\n components?: (string | undefined)[];\n alpha?: string;\n };\n\n // Collect all non-undefined references\n const refs: string[] = [];\n if (colorPartialAlias.colorSpace) {\n refs.push(colorPartialAlias.colorSpace);\n }\n if (colorPartialAlias.components) {\n for (const comp of colorPartialAlias.components) {\n if (comp) {\n refs.push(comp);\n }\n }\n }\n\n // If all color references (colorSpace + components) point to the same token, use it as aliasOf\n if (refs.length > 0) {\n const uniqueRefs = [...new Set(refs)];\n if (uniqueRefs.length === 1) {\n transformedValue[INTERNAL_KEYS.ALIAS_OF] = uniqueRefs[0];\n }\n }\n }\n\n registerTransform(setTransform, token.id, JSON.stringify(transformedValue), input);\n}\n\n/**\n * Transform DTCG tokens into Figma-compatible format.\n * Supports both resolver-based and non-resolver workflows.\n */\nexport default function transformFigmaJson({ transform, options }: TransformOptions): void {\n const { setTransform, context, resolver, tokens } = transform;\n\n const shouldExclude = createExcludeMatcher(options.exclude);\n\n // If no valid resolver config, use flat token map (fallback mode - no input)\n if (!hasValidResolverConfig(resolver)) {\n for (const token of Object.values(tokens)) {\n if (shouldExclude(token.id)) {\n continue;\n }\n\n transformToken(\n token,\n token.$value,\n token.aliasOf,\n options,\n context,\n tokens,\n setTransform,\n // No input - uses legacy mode without resolver\n );\n }\n return;\n }\n\n const permutations = resolver.listPermutations();\n\n // Process each permutation (context combination)\n for (const input of permutations) {\n const contextTokens = resolver.apply(input);\n\n for (const token of Object.values(contextTokens)) {\n if (shouldExclude(token.id)) {\n continue;\n }\n\n transformToken(token, token.$value, token.aliasOf, options, context, contextTokens, setTransform, input);\n }\n }\n}\n","import type { Plugin } from '@terrazzo/parser';\nimport buildFigmaJson from './build.js';\nimport { FORMAT_ID, PLUGIN_NAME } from './constants.js';\nimport transformFigmaJson from './transform.js';\nimport type { FigmaJsonPluginOptions } from './types.js';\n\nexport * from './build.js';\nexport * from './constants.js';\nexport * from './transform.js';\nexport * from './types.js';\nexport * from './utils.js';\n\n/**\n * Terrazzo plugin to convert DTCG design tokens to Figma-compatible JSON format.\n *\n * @example\n * // Basic usage\n * import { defineConfig } from \"@terrazzo/cli\";\n * import figmaJson from \"terrazzo-plugin-figma-json\";\n *\n * export default defineConfig({\n * plugins: [\n * figmaJson({ filename: \"tokens.figma.json\" }),\n * ],\n * });\n *\n * @example\n * // With all options\n * figmaJson({\n * filename: \"design-tokens.figma.json\",\n * exclude: [\"internal.*\", \"deprecated.*\"],\n * remBasePx: 16,\n * warnOnUnsupported: true,\n * preserveReferences: true,\n * tokenName: (token) => token.id.replace(\"color.\", \"brand.\"),\n * transform: (token) => {\n * if (token.id === \"special.token\") return { custom: true };\n * return undefined; // Use default transformation\n * },\n * });\n *\n */\nexport default function figmaJsonPlugin(options?: FigmaJsonPluginOptions): Plugin {\n const { skipBuild } = options ?? {};\n const filename = options?.filename ?? 'tokens.figma.json';\n\n return {\n name: PLUGIN_NAME,\n\n async transform(transformOptions) {\n // Skip if another figma-json plugin has already run\n const existingTransforms = transformOptions.getTransforms({\n format: FORMAT_ID,\n id: '*',\n });\n if (existingTransforms.length) {\n return;\n }\n\n transformFigmaJson({\n transform: transformOptions,\n options: options ?? {},\n });\n },\n\n async build({ getTransforms, outputFile, resolver }) {\n if (skipBuild === true) {\n return;\n }\n\n const result = buildFigmaJson({\n getTransforms,\n exclude: options?.exclude,\n tokenName: options?.tokenName,\n preserveReferences: options?.preserveReferences,\n resolver,\n });\n\n // Output multiple files based on resolver structure\n for (const [sourceName, contents] of result) {\n // sourceName is like \"primitive\" or \"breakpoint-small\" or \"default\" (when no resolver)\n const outputName = sourceName === 'default' ? filename : `${sourceName}.${filename}`;\n outputFile(outputName, contents);\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { TransformHookOptions } from '@terrazzo/parser';
2
+ import type { FigmaJsonPluginOptions } from './types.js';
3
+ export interface TransformOptions {
4
+ transform: TransformHookOptions;
5
+ options: FigmaJsonPluginOptions;
6
+ }
7
+ /**
8
+ * Transform DTCG tokens into Figma-compatible format.
9
+ * Supports both resolver-based and non-resolver workflows.
10
+ */
11
+ export default function transformFigmaJson({ transform, options }: TransformOptions): void;
12
+ //# sourceMappingURL=transform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG9E,OAAO,KAAK,EAAE,sBAAsB,EAAmB,MAAM,YAAY,CAAC;AAoB1E,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,oBAAoB,CAAC;IAChC,OAAO,EAAE,sBAAsB,CAAC;CACjC;AAoKD;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAwCzF"}
@@ -0,0 +1,182 @@
1
+ import type { Logger, TokenNormalized } from '@terrazzo/parser';
2
+ /**
3
+ * Extension keys used by Figma in the $extensions object.
4
+ * Figma uses flat namespaced keys like "com.figma.type", not nested objects.
5
+ */
6
+ export interface FigmaExtensionKeys {
7
+ /**
8
+ * Overrides the inferred Figma variable type.
9
+ * Use "boolean" to treat a number token as a boolean (0 = false, non-zero = true).
10
+ * Key: "com.figma.type"
11
+ */
12
+ 'com.figma.type'?: 'boolean';
13
+ /**
14
+ * Cross-collection alias data for Figma.
15
+ * Key: "com.figma.aliasData"
16
+ */
17
+ 'com.figma.aliasData'?: {
18
+ collection: string;
19
+ mode: string;
20
+ };
21
+ }
22
+ /**
23
+ * Token extensions object that may include Figma-specific keys.
24
+ */
25
+ export type TokenExtensions = FigmaExtensionKeys & {
26
+ [key: string]: unknown;
27
+ };
28
+ /**
29
+ * Output token structure for Figma-compatible JSON.
30
+ */
31
+ export interface FigmaTokenValue {
32
+ $type: string;
33
+ $value: unknown;
34
+ $description?: string;
35
+ $extensions?: TokenExtensions;
36
+ }
37
+ /**
38
+ * Partial alias information for composite types.
39
+ * This is an internal property from terrazzo parser that tracks
40
+ * which sub-properties of a composite token reference other tokens.
41
+ */
42
+ export type PartialAliasOf = Record<string, string | undefined>;
43
+ /**
44
+ * Extended token interface for internal properties not in public types.
45
+ * Terrazzo parser adds these properties but they're not exported in the type definitions.
46
+ */
47
+ export interface TokenWithPartialAlias {
48
+ partialAliasOf?: PartialAliasOf;
49
+ }
50
+ /**
51
+ * Options for the Figma JSON plugin.
52
+ */
53
+ export interface FigmaJsonPluginOptions {
54
+ /**
55
+ * Output filename for the Figma-compatible JSON.
56
+ * @default "tokens.figma.json"
57
+ */
58
+ filename?: string;
59
+ /**
60
+ * Glob patterns to exclude tokens from output.
61
+ * @example ["internal.*", "deprecated.*"]
62
+ */
63
+ exclude?: string[];
64
+ /**
65
+ * Custom transform function to override token values before output.
66
+ * Return undefined to use the default transformation.
67
+ */
68
+ transform?: (token: TokenNormalized) => unknown | undefined;
69
+ /**
70
+ * Custom function to control the token name in the output.
71
+ */
72
+ tokenName?: (token: TokenNormalized) => string;
73
+ /**
74
+ * Skip generating the output file.
75
+ * Useful if consuming transforms in another plugin.
76
+ * @default false
77
+ */
78
+ skipBuild?: boolean;
79
+ /**
80
+ * Base pixel value for rem to px conversion.
81
+ * @default 16
82
+ */
83
+ remBasePx?: number;
84
+ /**
85
+ * Whether to log warnings for unsupported token types.
86
+ * @default true
87
+ */
88
+ warnOnUnsupported?: boolean;
89
+ /**
90
+ * Preserve token references (aliases) in the output.
91
+ * When true:
92
+ * - Same-file references use curly brace syntax in $value (e.g., "{dimension.100}")
93
+ * - Cross-file references use resolved $value + com.figma.aliasData extension
94
+ * When false:
95
+ * - All values are fully resolved, no references preserved
96
+ * @default true
97
+ */
98
+ preserveReferences?: boolean;
99
+ /**
100
+ * Round computed lineHeight values to whole pixels.
101
+ * When true (default): lineHeight values are rounded to integers (e.g., 24px)
102
+ * When false: lineHeight values keep full precision (e.g., 23.999999px)
103
+ *
104
+ * This only affects typography tokens where lineHeight is a unitless multiplier
105
+ * that gets computed to an absolute px value (multiplier × fontSize).
106
+ * @default true
107
+ */
108
+ roundLineHeight?: boolean;
109
+ }
110
+ /**
111
+ * Context passed to converters.
112
+ */
113
+ export interface ConverterContext {
114
+ logger: Logger;
115
+ options: FigmaJsonPluginOptions;
116
+ tokenId: string;
117
+ extensions?: TokenExtensions;
118
+ allTokens?: Record<string, TokenNormalized>;
119
+ originalValue?: unknown;
120
+ partialAliasOf?: Record<string, string | undefined>;
121
+ }
122
+ /**
123
+ * Sub-token information for split composite tokens (e.g., typography).
124
+ */
125
+ export interface SubToken {
126
+ idSuffix: string;
127
+ $type: string;
128
+ value: unknown;
129
+ aliasOf?: string;
130
+ }
131
+ /**
132
+ * Result returned by a converter.
133
+ */
134
+ export interface ConverterResult {
135
+ value: unknown;
136
+ skip?: boolean;
137
+ outputType?: string;
138
+ split?: boolean;
139
+ subTokens?: SubToken[];
140
+ }
141
+ /**
142
+ * Color value structure in DTCG format.
143
+ */
144
+ export interface DTCGColorValue {
145
+ colorSpace: string;
146
+ components: [number | 'none', number | 'none', number | 'none'];
147
+ alpha?: number | 'none';
148
+ }
149
+ /**
150
+ * Dimension value structure in DTCG format.
151
+ */
152
+ export interface DTCGDimensionValue {
153
+ value: number;
154
+ unit: string;
155
+ }
156
+ /**
157
+ * Duration value structure in DTCG format.
158
+ */
159
+ export interface DTCGDurationValue {
160
+ value: number;
161
+ unit: string;
162
+ }
163
+ /**
164
+ * Typography value structure in DTCG format.
165
+ *
166
+ * Note: Per W3C DTCG spec, lineHeight is a number (unitless multiplier).
167
+ * Figma requires dimension tokens for lineHeight, so the plugin converts
168
+ * the multiplier to an absolute px value by multiplying with fontSize.
169
+ */
170
+ export interface DTCGTypographyValue {
171
+ fontFamily?: string | string[];
172
+ fontSize?: DTCGDimensionValue;
173
+ fontWeight?: number | string;
174
+ /** Unitless multiplier (e.g., 1.5 means 1.5× fontSize). Converted to dimension for Figma. */
175
+ lineHeight?: number;
176
+ letterSpacing?: DTCGDimensionValue;
177
+ }
178
+ /**
179
+ * Figma variable types.
180
+ */
181
+ export type FigmaVariableType = 'Color' | 'Number' | 'String' | 'Boolean';
182
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { Resolver } from '@terrazzo/parser';
2
+ import type { DTCGColorValue, DTCGDimensionValue, DTCGDurationValue, DTCGTypographyValue, PartialAliasOf } from './types.js';
3
+ /**
4
+ * Create an exclude matcher function from glob patterns.
5
+ *
6
+ * @param patterns - Array of glob patterns to match against token IDs
7
+ * @returns A function that returns true if the token ID should be excluded
8
+ */
9
+ export declare function createExcludeMatcher(patterns: string[] | undefined): (tokenId: string) => boolean;
10
+ /**
11
+ * Type guard to check if the resolver has a usable configuration.
12
+ * Terrazzo creates a default resolver even without a resolver file,
13
+ * but it has empty contexts that cause errors when used.
14
+ *
15
+ * @param resolver - The resolver from terrazzo parser
16
+ * @returns true if resolver has user-defined sets or modifiers with contexts
17
+ */
18
+ export declare function hasValidResolverConfig(resolver: Resolver | undefined): resolver is Resolver;
19
+ /**
20
+ * Build default input from resolver's modifiers.
21
+ * Creates an input object with each modifier set to its default value.
22
+ *
23
+ * @param resolverSource - The resolver source configuration
24
+ * @returns Input object for resolver.apply() with default modifier values
25
+ */
26
+ export declare function buildDefaultInput(resolverSource: NonNullable<Resolver['source']>): Record<string, string>;
27
+ /**
28
+ * Remove internal metadata properties from a parsed token value.
29
+ * These properties are used for internal processing and should not appear in output.
30
+ *
31
+ * @param parsedValue - Token value object to clean (mutated in place)
32
+ */
33
+ export declare function removeInternalMetadata(parsedValue: Record<string, unknown>): void;
34
+ /**
35
+ * Safely parse a transform value that may be a JSON string or already an object.
36
+ * Returns the parsed value or null if parsing fails.
37
+ *
38
+ * @param value - The transform value (string or object)
39
+ * @returns Parsed value or null on error
40
+ */
41
+ export declare function parseTransformValue(value: unknown): any;
42
+ /**
43
+ * Extract partialAliasOf from a token if present.
44
+ * This property is added by terrazzo parser for composite tokens but not in public types.
45
+ */
46
+ export declare function getPartialAliasOf(token: unknown): PartialAliasOf | undefined;
47
+ /**
48
+ * Type guard to validate DTCGColorValue structure.
49
+ */
50
+ export declare function isDTCGColorValue(value: unknown): value is DTCGColorValue;
51
+ /**
52
+ * Type guard to validate DTCGDimensionValue structure.
53
+ */
54
+ export declare function isDTCGDimensionValue(value: unknown): value is DTCGDimensionValue;
55
+ /**
56
+ * Type guard to validate DTCGDurationValue structure.
57
+ */
58
+ export declare function isDTCGDurationValue(value: unknown): value is DTCGDurationValue;
59
+ /**
60
+ * Type guard to validate DTCGTypographyValue structure.
61
+ * Only checks that it's an object - individual properties are validated during conversion.
62
+ */
63
+ export declare function isDTCGTypographyValue(value: unknown): value is DTCGTypographyValue;
64
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGjD,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EAEf,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAEjG;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,IAAI,QAAQ,CAe3F;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUzG;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAIjF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CASvD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAQ5E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAoBxE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAMhF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAM9E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAElF"}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "terrazzo-plugin-figma-json",
3
+ "version": "0.1.0",
4
+ "description": "Convert DTCG design tokens JSON into Figma-compatible design tokens JSON format.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "author": "dgtlntv",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/dgtlntv/terrazzo-plugin-figma-json.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/dgtlntv/terrazzo-plugin-figma-json/issues"
14
+ },
15
+ "homepage": "https://github.com/dgtlntv/terrazzo-plugin-figma-json#readme",
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "keywords": [
20
+ "design tokens",
21
+ "design system",
22
+ "dtcg",
23
+ "w3c",
24
+ "figma",
25
+ "terrazzo"
26
+ ],
27
+ "main": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "scripts": {
40
+ "build": "rolldown -c && tsc --emitDeclarationOnly",
41
+ "dev": "rolldown -w -c",
42
+ "format": "biome check --fix --unsafe .",
43
+ "lint": "pnpm run \"/^lint:(js|ts)/\"",
44
+ "lint:js": "biome check .",
45
+ "lint:ts": "tsc --noEmit",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "prepublishOnly": "pnpm run lint && pnpm run test && pnpm run build"
49
+ },
50
+ "peerDependencies": {
51
+ "@terrazzo/cli": "^2.0.0-beta.0",
52
+ "@terrazzo/parser": "^2.0.0-beta.0"
53
+ },
54
+ "dependencies": {
55
+ "colorjs.io": "^0.6.1",
56
+ "wildcard-match": "^5.1.4"
57
+ },
58
+ "devDependencies": {
59
+ "@biomejs/biome": "^2.3.13",
60
+ "@terrazzo/cli": "2.0.0-beta.0",
61
+ "@terrazzo/parser": "2.0.0-beta.0",
62
+ "@types/node": "^25.0.10",
63
+ "rolldown": "1.0.0-rc.1",
64
+ "typescript": "^5.9.3",
65
+ "vitest": "^4.0.18"
66
+ },
67
+ "packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402"
68
+ }