All files / packages/fds-uif/generator-base/src/analyzer index.ts

61.76% Statements 21/34
45.45% Branches 5/11
75% Functions 3/4
65.51% Lines 19/29

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 124 125 126 127 128 129 130                                                      8x                                               39x       39x             39x       39x 39x 39x 39x 39x 39x     39x     39x     39x     39x   39x                   5x   28x                 39x                             2x                
/**
 * UIF Analyzer
 *
 * Extracts component metadata from resolved UIFs for code generation.
 * Uses extraction utilities from @fds-uif/core.
 */
 
import type { ResolvedUIF } from '@fds-uif/schema';
 
import {
  validateResolved,
  UifResolvedValidationError,
  getStates,
  getModifiers,
  getVariants,
  getSlots,
  getBoundAttributes,
  getStateClasses,
} from '@fds-uif/core/browser';
 
import type { ComponentMetadata, AnalyzerOptions } from '../types/index.js';
 
import { buildProps } from './props.js';
import { analyzeStructure } from './structure.js';
import { buildClassNameRules } from './classNames.js';
import { detectDependencies } from './dependencies.js';
 
const DEFAULT_ELEMENT = 'div';
 
/**
 * Error thrown during UIF analysis
 */
export class UifAnalyzerError extends Error {
  constructor(
    message: string,
    public readonly componentName?: string,
    public readonly details?: Record<string, unknown>,
  ) {
    super(message);
    this.name = 'UifAnalyzerError';
  }
}
 
/**
 * Analyze a resolved UIF and extract component metadata
 *
 * @param uif - Resolved UIF (with $extends already merged)
 * @param options - Analyzer options
 * @returns Component metadata for code generation
 */
export function analyzeUif(uif: ResolvedUIF | unknown, options: AnalyzerOptions = {}): ComponentMetadata {
  const { defaultElement = DEFAULT_ELEMENT, inferDescriptions = false, validate = true } = options;
 
  // Validate before analysis
  if (validate) {
    const validation = validateResolved(uif);
    if (!validation.valid) {
      const messages = validation.errors?.map((e) => `${e.path}: ${e.message}`).join('; ');
      throw new UifResolvedValidationError(`Invalid UIF: ${messages}`, validation.errors);
    }
  }
 
  const resolvedUif = uif as ResolvedUIF;
 
  try {
    // Use schema extraction utilities
    const states = getStates(resolvedUif);
    const modifiers = getModifiers(resolvedUif);
    const variants = getVariants(resolvedUif);
    const slots = getSlots(resolvedUif);
    const boundAttrs = getBoundAttributes(resolvedUif);
    const stateClasses = getStateClasses(resolvedUif);
 
    // Build props from extracted data
    const props = buildProps(states, modifiers, variants, slots, boundAttrs, inferDescriptions);
 
    // Analyze structure for rendering
    const structure = analyzeStructure(resolvedUif.structure, defaultElement);
 
    // Build class name rules
    const classNames = buildClassNameRules(resolvedUif, modifiers, stateClasses);
 
    // Detect dependencies
    const dependencies = detectDependencies(resolvedUif.structure, defaultElement);
 
    const metadata: ComponentMetadata = {
      name: resolvedUif.name,
      description: resolvedUif.description,
      props,
      structure,
      classNames,
      dependencies,
    };
 
    if (resolvedUif.accessibility) {
      metadata.accessibility = {
        role: resolvedUif.accessibility.role,
        ariaProps: props.filter((p) => p.name.startsWith('aria')).map((p) => p.name),
        landmarks: resolvedUif.accessibility.landmarks,
      };
    }
 
    if (resolvedUif.behavior) {
      metadata.behavior = resolvedUif.behavior;
    }
 
    return metadata;
  } catch (error) {
    if (error instanceof UifAnalyzerError) throw error;
    if (error instanceof UifResolvedValidationError) throw error;
    throw new UifAnalyzerError(
      `Failed to analyze UIF: ${error instanceof Error ? error.message : String(error)}`,
      resolvedUif.name,
    );
  }
}
 
/**
 * Analyze multiple UIFs
 */
export function analyzeMultipleUifs(uifs: ResolvedUIF[], options: AnalyzerOptions = {}): ComponentMetadata[] {
  return uifs.map((uif) => analyzeUif(uif, options));
}
 
// Re-export sub-modules for advanced usage
export { buildProps } from './props.js';
export { analyzeStructure, extractComponentPropsMetadata } from './structure.js';
export { buildClassNameRules } from './classNames.js';
export { detectDependencies } from './dependencies.js';