UNPKG

@tokens-studio/sd-transforms

Version:

Custom transforms for Style-Dictionary, to work with Design Tokens that are exported from Tokens Studio

99 lines (98 loc) 4.9 kB
import { usesReferences, resolveReferences, convertTokenData } from 'style-dictionary/utils'; import { fontWeightReg, fontStyles } from '../transformFontWeight.js'; function resolveFontWeight(fontWeight, copy, usesDtcg) { const tokenMap = convertTokenData(copy, { output: 'map', usesDtcg }); let resolved = fontWeight; if (usesReferences(fontWeight)) { try { resolved = `${resolveReferences(fontWeight, tokenMap, { usesDtcg })}`; } catch (e) { if (e instanceof Error) { // create an extended error const err = new Error(`tokens-studio preprocessor -> addFontStyles: Failing to resolve references within fontWeight -> ${fontWeight}.\n\n${e.message}`); err.stack = e.stack; // dont throw fatal, see: https://github.com/tokens-studio/sd-transforms/issues/217 // we throw once we only support SD v4, for v3 we need to be more permissive // Update: maybe sd-transforms should also be (re-)using the new logger approach that's coming to SD v5 in the future // that way it's up to users to decide whether they want these to be thrown, error'd or something else. console.error(err); } } } return resolved; } function splitWeightStyle(fontWeight, alwaysAddFontStyle) { let weight = fontWeight; let style = alwaysAddFontStyle ? 'normal' : undefined; if (fontWeight) { const fontStyleMatch = fontWeight.match(fontWeightReg); if (fontStyleMatch?.groups?.weight && fontStyleMatch.groups.style) { style = fontStyleMatch.groups.style.toLowerCase(); weight = fontStyleMatch?.groups?.weight; } // Roboto Regular Italic might have only: `fontWeight: 'Italic'` // which means that the weight is Regular and the style is Italic if (fontStyles.includes(fontWeight.toLowerCase())) { style = fontWeight.toLowerCase(); weight = 'Regular'; } } return { weight, style }; } function recurse(slice, refCopy, alwaysAddFontStyle = false) { Object.keys(slice).forEach(key => { const potentiallyToken = slice[key]; const isToken = typeof potentiallyToken === 'object' && ((Object.hasOwn(potentiallyToken, '$type') && Object.hasOwn(potentiallyToken, '$value')) || (Object.hasOwn(potentiallyToken, 'type') && Object.hasOwn(potentiallyToken, 'value'))); if (isToken) { const token = potentiallyToken; const usesDtcg = Object.hasOwn(token, '$value'); const { value, $value, type, $type } = token; const tokenType = (usesDtcg ? $type : type); const tokenValue = (usesDtcg ? $value : value); if (tokenType === 'typography') { const tokenTypographyValue = tokenValue; if (tokenTypographyValue.fontWeight === undefined) return; const fontWeight = resolveFontWeight(`${tokenTypographyValue.fontWeight}`, refCopy, usesDtcg); const { weight, style } = splitWeightStyle(fontWeight, alwaysAddFontStyle); if (style) { tokenTypographyValue.fontWeight = weight; tokenTypographyValue.fontStyle = style; } } else if (tokenType === 'fontWeight') { const tokenFontWeightsValue = tokenValue; const fontWeight = resolveFontWeight(`${tokenFontWeightsValue}`, refCopy, usesDtcg); // alwaysAddFontStyle should only apply to typography tokens, so we pass `false` here const { weight, style } = splitWeightStyle(fontWeight, false); if (style) { // since tokenFontWeightsValue is a primitive (string), we have to permutate the change directly slice[key] = { weight: { ...token, [`${usesDtcg ? '$' : ''}type`]: 'fontWeight', [`${usesDtcg ? '$' : ''}value`]: weight, }, style: { ...token, [`${usesDtcg ? '$' : ''}type`]: 'fontStyle', [`${usesDtcg ? '$' : ''}value`]: style, }, }; } } } else if (typeof potentiallyToken === 'object') { recurse(potentiallyToken, refCopy, alwaysAddFontStyle); } }); } export function addFontStyles(dictionary, transformOpts) { const refCopy = structuredClone(dictionary); const newCopy = structuredClone(dictionary); recurse(newCopy, refCopy, transformOpts?.alwaysAddFontStyle); return newCopy; }