import { RawValueSignature, ResolvableModeLevelAlias, ResolvableTopLevelAlias, UnresolvableModeLevelAlias, } from '@specifyapp/specify-design-token-format'; import { renderVariable as renderScssVariable, DEFAULT_TEMPLATE, } from '../../scss/utils/template.js'; import { throwErrorForUnresolvableAlias } from '../../_utils/throwErrorForUnresolvableAlias.js'; import { modeLevelAliasFontToVariables } from '../../css/tokenTypes/font.js'; import { SpecifyError, specifyErrors } from '../../../errors/index.js'; import { modeLevelAliasTransitonToVariables } from '../../css/tokenTypes/transition.js'; import { modeLevelAliasTextStyleToVariables } from '../../css/tokenTypes/textStyle.js'; import { ScssResolvableAliasStrategy } from './ScssAliasStrategy.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( template?: string, shouldRenderSingleMode = false, ): ScssResolvableAliasStrategy { return alias => { const renderVariable: TemplateRenderer = data => { if (alias.tokenState.modes.length === 1 && !shouldRenderSingleMode) { data.mode = undefined; } return renderScssVariable(template ?? DEFAULT_TEMPLATE, data); }; 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, false); case 'textStyle': const { value: textStyle } = alias.tokenState.resolveDeepStatefulValueForMode(mode); if (textStyle instanceof UnresolvableModeLevelAlias) { throwErrorForUnresolvableAlias(textStyle); } return modeLevelAliasTextStyleToVariables( alias, renderVariable, textStyle as RawValueSignature<'textStyle'>, false, ); case 'font': return modeLevelAliasFontToVariables(alias, renderVariable, false); 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 => 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, false) 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'>, false, ) as any; case 'font': return modeLevelAliasFontToVariables(alias, renderVariable, false) as any; default: return renderVariable(dataOfResolvedStatefulAlias(alias)); } } else { switch (alias.tokenState.type) { case 'font': return modeLevelAliasFontToVariables(alias, renderVariable); default: return renderVariable(dataOfResolvedStatefulAlias(alias, alias.targetMode)); } } }; }