import { TokenState } from '@specifyapp/specify-design-token-format'; import { specifyErrors, SpecifyError } from '../../../errors/index.js'; import { groupBy, merge } from 'lodash-es'; import { makeFileName } from './utils/makeFileName.js'; import { throwOnUnexpectedUnresolvable } from '../../utils/throwOnUnexpectedUnresolvable.js'; import { matchRawValue } from '../../utils/rawValueMatcher.js'; import { aliasOfResolvableAlias } from './utils/aliasOfResolvedStatefulAlias.js'; import { bitmapToStyleDictionary } from './converters/bitmap.js'; import { borderToStyleDictionary } from './converters/border.js'; import { colorToStyleDictionary } from './converters/color.js'; import { fontToStyleDictionary } from './converters/font.js'; import { gradientToStyleDictionary } from './converters/gradient.js'; import { gradientsToStyleDictionary } from './converters/gradients.js'; import { borderStyleToStyleDictionary } from './converters/borderStyle.js'; import { shadowToStyleDictionary } from './converters/shadow.js'; import { shadowsToStyleDictionary } from './converters/shadows.js'; import { sizeToStyleDictionary } from './converters/size.js'; import { spacingsToStyleDictionary } from './converters/spacings.js'; import { textStyleToStyleDictionary } from './converters/textStyle.js'; import { timeToStyleDictionary } from './converters/time.js'; import { transitionToStyleDictionary } from './converters/transition.js'; import { vectorToStyleDictionary } from './converters/vector.js'; export type styleDictionaryStructure = { [k: string]: { [tokenName: string]: { value: any; }; }; }; export function sortTokens(type: TokenState['type'], tokens: Array) { return tokens.reduce((acc, tokenState) => { if (tokenState.type !== type) throw new SpecifyError({ errorKey: specifyErrors.UNEXPECTED_ERROR.errorKey, publicMessage: `${type} is not matching ${tokenState.type}. Both types are supposed to match`, }); if (!tokenState.isFullyResolvable) return acc; const output = tokenState .getStatefulValueResult() .resolveDeepValue() .mapUnresolvableTopLevelAlias(throwOnUnexpectedUnresolvable) .mapTopLevelValue(modeLevel => modeLevel .mapRawValue(rawValue => matchRawValue( { bitmap: bitmapToStyleDictionary, border: borderToStyleDictionary, borderStyle: borderStyleToStyleDictionary, breakpoint: sizeToStyleDictionary, color: colorToStyleDictionary, font: fontToStyleDictionary, gradient: gradientToStyleDictionary, gradients: gradientsToStyleDictionary, shadow: shadowToStyleDictionary, shadows: shadowsToStyleDictionary, dimension: sizeToStyleDictionary, radius: sizeToStyleDictionary, spacing: sizeToStyleDictionary, spacings: spacingsToStyleDictionary, textStyle: textStyleToStyleDictionary, duration: timeToStyleDictionary, transition: transitionToStyleDictionary, vector: vectorToStyleDictionary, opacity: v => v, zIndex: v => v, fontFamily: v => v, fontWeight: v => v, cubicBezier: v => v.map(v => v.resolveDeepValue().unwrapValue()), }, () => undefined, tokenState.type, rawValue, ), ) .mapUnresolvableModeLevelAlias(throwOnUnexpectedUnresolvable) .mapResolvableModeLevelAlias(aliasOfResolvableAlias) .unwrap(), ) .unwrap(); return merge( acc, Object.entries(output).reduce((acc, [mode, value]) => { if (!value) return acc; const [fileName, tokenNesting] = makeFileName(tokenState.type, tokenState, mode); return merge( acc, Array.isArray(value) && tokenState.type !== 'cubicBezier' ? { [fileName]: value.reduce((acc, value, i) => { acc[(!!tokenNesting ? `${tokenNesting}.` : '') + `${tokenState.name}${i}`] = { value, type: tokenState.type, ...(!!tokenState.description ? { description: tokenState.description } : {}), }; return acc; }, {}), } : { [fileName]: { [(!!tokenNesting ? `${tokenNesting}.` : '') + `${tokenState.name}`]: { value, type: tokenState.type, ...(!!tokenState.description ? { description: tokenState.description } : {}), }, }, }, ); }, {}), ); }, {}); } export function sortTokensToStyleDictionary( type: TokenState['type'], tokens: Array, ): styleDictionaryStructure | undefined { switch (type) { case 'border': case 'breakpoint': case 'color': case 'cubicBezier': case 'dimension': case 'duration': case 'fontFamily': case 'fontWeight': case 'gradient': case 'opacity': case 'radius': case 'shadow': case 'spacing': case 'textStyle': case 'transition': case 'zIndex': case 'gradients': case 'shadows': case 'spacings': case 'bitmap': case 'vector': case 'font': return sortTokens(type, tokens); default: return undefined; } } export function sortTokenStatesToStyleDictionary(tokenStates: Array) { const tokenStatesByType = groupBy(tokenStates, tokenState => tokenState.type); return Object.entries(tokenStatesByType).reduce((acc, [t, tokenStates]) => { const type = t as TokenState['type']; const sortedTokens = sortTokensToStyleDictionary(type, tokenStates); return merge(acc, sortedTokens); }, {}); }