All files / packages/design-tokens/src/style-dictionary/preprocessors propagate-css-properties.ts

88.09% Statements 37/42
80.64% Branches 25/31
71.42% Functions 5/7
89.47% Lines 34/38

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       1x 19x   1x 10x   4x 4x 4x 4x   10x     1x 1x 1x   1x     1x 2x   2x 2x   1x   1x 1x     1x 8x 10x   8x 8x 10x   10x 2x 2x     6x       1x             1x             1x      
import type { Preprocessor } from 'style-dictionary/types';
 
import type { StyleDictionaryHost } from '../style-dictionary-host.js';
import { CSS_PROPERTIES_EXTENSION_KEY } from '../utils/constants.js';
 
const LEGACY_CSS_PROPERTIES_KEY = 'cssProperties';
 
type DtcgNode = Record<string, unknown>;
 
const isObject = (value: unknown): value is DtcgNode =>
  typeof value === 'object' && value !== null && !Array.isArray(value);
 
const getCssProperties = (node: DtcgNode): string[] | undefined => {
  if (!isObject(node.$extensions)) return undefined;
 
  const extensions = node.$extensions as DtcgNode;
  const namespaced = extensions[CSS_PROPERTIES_EXTENSION_KEY];
  const legacy = extensions[LEGACY_CSS_PROPERTIES_KEY];
  const cssProperties = Array.isArray(namespaced) ? namespaced : Array.isArray(legacy) ? legacy : undefined;
 
  return cssProperties && cssProperties.length > 0 ? [...cssProperties] : undefined;
};
 
const ensureExtensions = (node: DtcgNode): DtcgNode => {
  Eif (!isObject(node.$extensions)) {
    node.$extensions = {};
  }
  return node.$extensions as DtcgNode;
};
 
const applyToLeaf = (node: DtcgNode, inherited?: string[]) => {
  Iif (!inherited || inherited.length === 0) return;
 
  const current = getCssProperties(node);
  if (current) return;
 
  const extensions = ensureExtensions(node);
  // Write both keys for compatibility during migration.
  extensions[CSS_PROPERTIES_EXTENSION_KEY] = [...inherited];
  extensions[LEGACY_CSS_PROPERTIES_KEY] = [...inherited];
};
 
const propagateCssPropertiesInTree = (node: DtcgNode, inherited?: string[]) => {
  for (const [key, child] of Object.entries(node)) {
    if (key.startsWith('$') || !isObject(child)) continue;
 
    const ownCssProperties = getCssProperties(child);
    const effective = ownCssProperties ?? inherited;
    const isTokenLeaf = '$value' in child;
 
    if (isTokenLeaf) {
      applyToLeaf(child, effective);
      continue;
    }
 
    propagateCssPropertiesInTree(child, effective);
  }
};
 
const cssPropertiesPreprocessor = ((dictionary) => {
  if (isObject(dictionary)) {
    propagateCssPropertiesInTree(dictionary);
  }
  return dictionary;
}) satisfies Preprocessor['preprocessor'];
 
export const propagateCssProperties = (StyleDictionary: StyleDictionaryHost) => {
  StyleDictionary.registerPreprocessor({
    name: 'propagate-css-properties',
    preprocessor: cssPropertiesPreprocessor,
  });
};
 
export const _testExports = {
  propagateCssPropertiesInTree,
};