All files / packages/design-tokens/src/style-dictionary/transforms/common capture-variant-defaults.ts

100% Statements 13/13
100% Branches 2/2
100% Functions 6/6
100% Lines 12/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77                                1x 7x 14x 14x                 1x 4x                                             1x         3x 4x   3x             1x 1x                
import type {
  Config,
  Filter,
  PlatformConfig,
  TransformedToken,
  ValueTransform,
} from 'style-dictionary/types';
 
import type { StyleDictionaryHost } from '../../style-dictionary-host.js';
import { VARIANT_AXES } from '../../utils/variant-extensions.js';
 
/**
 * Return the variant axes a token participates in — those carrying at least one
 * authored variant value under their own `$extensions` namespace (color scheme
 * under `com.salesforce-ux.mode`, density under `com.salesforce-ux.density`).
 */
const participatingAxes = (token: TransformedToken) =>
  VARIANT_AXES.filter((axis) => {
    const block = token.$extensions?.[axis.key];
    return block && axis.variantNames.some((name) => block[name]?.$value !== undefined);
  });
 
/**
 * Filter for tokens that participate in any variant axis (mode, density).
 *
 * @param {Object} token
 * @returns boolean
 */
export const captureVariantDefaultsFilter = ((token: TransformedToken, _options: Config) =>
  participatingAxes(token).length > 0) satisfies Filter['filter'];
 
/**
 * Capture the resolved default value (light, comfy) into each variant axis.
 *
 * Variant axes store their non-default values under their own namespace (e.g.
 * `mode.dark`, `density.compact`); the default is implied by the token's own
 * `$value`. To emit a symmetric block (`{ light, dark }`, `{ comfy, compact }`)
 * in the JSON outputs we need the *resolved* default value (e.g. `#b60554`,
 * `2rem`).
 *
 * On the `flat.json` platform the `value/css-vars` transform runs after value
 * resolution and rewrites dynamic tokens' `$value` into a `var(--…)` reference,
 * discarding the resolved default. This transform runs BEFORE `css-vars` (and
 * after value resolution such as `value/oklch-to-hex`) and copies the resolved
 * `$value` into `$extensions[axis.key][axis.defaultName].$value` for every axis
 * the token participates in. The extension survives the later `css-vars` pass
 * because that transform only rewrites `$value`. `$value` itself is returned
 * unchanged here.
 *
 * @param {Object} token
 * @returns The token's existing `$value`
 */
export const captureVariantDefaultsTransformer = ((
  token: TransformedToken,
  _platform: PlatformConfig,
  _options: Config,
) => {
  for (const axis of participatingAxes(token)) {
    token.$extensions[axis.key][axis.defaultName] = { $value: token.$value };
  }
  return token.$value;
}) satisfies ValueTransform['transform'];
 
/**
 * Style Dictionary registration for the capture-variant-defaults transform
 * @param {StyleDictionary} StyleDictionary
 */
export const captureVariantDefaults = (StyleDictionary: StyleDictionaryHost) => {
  StyleDictionary.registerTransform({
    name: 'value/capture-variant-defaults',
    type: 'value',
    transitive: true,
    filter: captureVariantDefaultsFilter,
    transform: captureVariantDefaultsTransformer,
  } satisfies ValueTransform);
};