import { RawValueSignature, ResolvableModeLevelAlias, ResolvableTopLevelAlias, UnresolvableModeLevelAlias, } from '@specifyapp/specify-design-token-format'; import { makeCSSAlias } from '../utils/makeCSSAlias.js'; import { renderVariable as renderVariable_, DEFAULT_TOKEN_TEMPLATE, DEFAULT_TOKEN_NOT_IN_COLLECTION_TEMPLATE, } from '../utils/template.js'; import { throwErrorForUnresolvableAlias } from '../../_utils/throwErrorForUnresolvableAlias.js'; import { modeLevelAliasFontToVariables } from '../tokenTypes/font.js'; import { SpecifyError, specifyErrors } from '../../../errors/index.js'; import { modeLevelAliasTransitonToVariables } from '../tokenTypes/transition.js'; import { modeLevelAliasTextStyleToVariables } from '../tokenTypes/textStyle.js'; import { CssResolvableAliasStrategy } from './CssAliasStrategy.js'; import { TemplateRenderer, dataOfResolvedStatefulAlias, dataOfToken, } from '../../../builtInParsers/utils/template.js'; /** * Will create an alias to var strategy. By applying this strategy when converting a token to css, * All the aliases will be converted to a css variable, e.g: `var(--alias-name)`. * Default templates: * - `tokenNotInCollectionNameTemplate` -> `--{{groups}}-{{token}}-{{mode}}` * - `tokenNameTemplate` -> `--{{groups}}-{{token}}` */ export function createAliasToVarStrategy(templates?: { tokenNameTemplate?: string; tokenNotInCollectionNameTemplate?: string; }): CssResolvableAliasStrategy { const renderVariable: TemplateRenderer = rawData => renderVariable_( rawData.collection ? templates?.tokenNameTemplate ?? DEFAULT_TOKEN_TEMPLATE : templates?.tokenNotInCollectionNameTemplate ?? DEFAULT_TOKEN_NOT_IN_COLLECTION_TEMPLATE, rawData, ); return alias => { if (alias instanceof ResolvableTopLevelAlias) { const valuesByMode = (() => { switch (alias.tokenState.type) { case 'transition': case 'font': case 'textStyle': return alias.tokenState .getStatefulValueResult() .resolveDeepValue() .mapUnresolvableTopLevelAlias(alias => { throw new SpecifyError({ publicMessage: `Unresolvable alias with the path: ${alias.targetPath.toString()}`, errorKey: specifyErrors.UNEXPECTED_ERROR.errorKey, }); }) .mapTopLevelValue(topLevel => topLevel .mapModes(mode => { switch (alias.tokenState.type) { case 'transition': return modeLevelAliasTransitonToVariables(alias, renderVariable); case 'textStyle': const { value: textStyle } = alias.tokenState.resolveDeepStatefulValueForMode(mode); if (textStyle instanceof UnresolvableModeLevelAlias) { throwErrorForUnresolvableAlias(textStyle); } return modeLevelAliasTextStyleToVariables( alias, renderVariable, textStyle as RawValueSignature<'textStyle'>, ); case 'font': return modeLevelAliasFontToVariables(alias, renderVariable); default: throw new SpecifyError({ publicMessage: 'This case is not supposed to be triggered', errorKey: specifyErrors.UNEXPECTED_ERROR.errorKey, }); } }) .unwrap(), ) .unwrap() as any; // The any is here to avoid using generics everywhere default: return alias.tokenState .getStatefulValueResult() .resolveDeepValue() .mapUnresolvableTopLevelAlias(throwErrorForUnresolvableAlias) .mapTopLevelValue(value => value .mapModes(mode => makeCSSAlias(renderVariable(dataOfToken(value.tokenState, mode))), ) .unwrap(), ) .unwrap() as any; // The any is here to avoid using generics everywhere } })(); return valuesByMode as { [mode: string]: string | Record }; } else if (alias instanceof ResolvableModeLevelAlias) { switch (alias.tokenState.type) { case 'transition': return modeLevelAliasTransitonToVariables(alias, renderVariable) as any; case 'textStyle': const { value: textStyle } = alias.tokenState.resolveDeepStatefulValueForMode( alias.targetMode, ); if (textStyle instanceof UnresolvableModeLevelAlias) { throwErrorForUnresolvableAlias(textStyle); } return modeLevelAliasTextStyleToVariables( alias, renderVariable, textStyle as RawValueSignature<'textStyle'>, ) as any; case 'font': return modeLevelAliasFontToVariables(alias, renderVariable) as any; default: return makeCSSAlias(renderVariable(dataOfResolvedStatefulAlias(alias))); } } else { switch (alias.tokenState.type) { case 'font': return modeLevelAliasFontToVariables(alias, renderVariable); default: return makeCSSAlias(renderVariable(dataOfResolvedStatefulAlias(alias, alias.targetMode))); } } }; }