All files / packages/fds-uif/core/src/css metadata-queries.ts

100% Statements 34/34
90.9% Branches 20/22
100% Functions 5/5
100% Lines 26/26

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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123                              13x                             5x 5x             13x 13x             13x   13x   8x     5x   2x     3x                 4x         4x         8x         4x         6x         1x         2x     5x 5x     2x               5x 4x 3x 2x         3x    
/**
 * @fds-uif/core - CSS Metadata Query Utilities (Browser-safe)
 *
 * Query and naming utilities for working with CssMetadata.
 * These functions do NOT use PostCSS and are fully browser-compatible.
 *
 * @see RFC: uif-styling-layer.md
 */
 
import type { CssMetadata, CssRule, ComponentHook } from './types.js';
 
// ============================================================================
// Constants
// ============================================================================
 
const DEFAULT_SYSTEM = 'slds';
 
// ============================================================================
// Naming Utilities
// ============================================================================
 
/**
 * Generate the output filename for CSS metadata.
 *
 * Format: `{component}.css.{system}.uif.json`
 */
export function getCssMetadataFilename(
  componentName: string,
  system: string = DEFAULT_SYSTEM,
): string {
  const normalizedName = componentName.toLowerCase();
  return `${normalizedName}.css.${system}.uif.json`;
}
 
/**
 * Get the basename of a path (browser-safe implementation).
 */
function getBasename(filepath: string): string {
  const lastSlash = Math.max(filepath.lastIndexOf('/'), filepath.lastIndexOf('\\'));
  return lastSlash >= 0 ? filepath.slice(lastSlash + 1) : filepath;
}
 
/**
 * Infer the design system from a UIF filename.
 */
export function inferSystemFromFilename(filename: string): string {
  const base = getBasename(filename);
 
  const match = base.match(/\.system\.([^.]+)\.uif\.json$/);
  if (match) {
    return match[1];
  }
 
  const legacyMatch = base.match(/\.([^.]+)\.uif\.json$/);
  if (legacyMatch && legacyMatch[1] !== 'system' && legacyMatch[1] !== 'foundation') {
    return legacyMatch[1];
  }
 
  return DEFAULT_SYSTEM;
}
 
// ============================================================================
// Query Utilities
// ============================================================================
 
/** Get all unique hook names. */
export function getAllHookNames(metadata: CssMetadata): string[] {
  return metadata.hooks.map(hook => hook.name);
}
 
/** Get hooks by category. */
export function getHooksByCategory(metadata: CssMetadata, category: string): ComponentHook[] {
  return metadata.hooks.filter(hook => hook.category === category);
}
 
/** Get hooks by element. */
export function getHooksByElement(metadata: CssMetadata, element: string): ComponentHook[] {
  return metadata.hooks.filter(hook => hook.element === element);
}
 
/** Get hooks with global token references. */
export function getHooksWithGlobalValues(metadata: CssMetadata): ComponentHook[] {
  return metadata.hooks.filter(hook => hook.globalValue);
}
 
/** Get hooks with shared hook references. */
export function getHooksWithSharedValues(metadata: CssMetadata): ComponentHook[] {
  return metadata.hooks.filter(hook => hook.sharedValue);
}
 
/** Check if metadata has deprecated hooks. */
export function hasDeprecatedHooks(metadata: CssMetadata): boolean {
  return metadata.deprecated.length > 0;
}
 
/** Get summary of hooks by category. */
export function getHookCategorySummary(metadata: CssMetadata): Record<string, number> {
  const summary: Record<string, number> = {};
 
  for (const hook of metadata.hooks) {
    const category = hook.category || 'uncategorized';
    summary[category] = (summary[category] || 0) + 1;
  }
 
  return summary;
}
 
/** Get rules from a specific layer. */
export function getRulesFromLayer(
  metadata: CssMetadata,
  layerType: 'theme' | 'defaults' | 'unlayered' | string,
): CssRule[] {
  if (layerType === 'theme') return metadata.themeLayer?.rules || [];
  if (layerType === 'defaults') return metadata.defaultsLayer?.rules || [];
  if (layerType === 'unlayered') return metadata.unlayered.rules;
  return metadata.themeOverrides[layerType]?.rules || [];
}
 
/** Get all theme override names. */
export function getThemeOverrideNames(metadata: CssMetadata): string[] {
  return Object.keys(metadata.themeOverrides);
}