All files / packages/sds-subsystems/scripts/uif processors.js

0% Statements 0/39
0% Branches 0/8
0% Functions 0/4
0% Lines 0/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 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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162                                                                                                                                                                                                                                                                                                                                   
/**
 * UIF file processors - handles copying foundations and resolving system layers
 *
 * RFC Terminology:
 *   foundation layer - Base component definitions (copied as-is)
 *   system layer - Design system layer that extends foundations (resolved/merged)
 *
 * Note: This package (sds-subsystems) represents SLDS component definitions.
 * The "system" here refers to the UIF layer type, not the package name.
 */
 
import fs from 'fs-extra';
import path from 'path';
import { resolveExtends, validateUif } from '@fds-uif/core';
import { getComponentName, getFoundationOutputPath, getSldsOutputPath } from './paths.js';
 
/**
 * Copy a foundation UIF file to dist (with consistent formatting)
 * @param {string} filePath - Source UIF file path
 * @param {object} options - Options object
 * @param {string} options.rootDir - Project root directory
 * @param {string} options.distDir - Distribution output directory
 * @param {object} options.log - Logger instance
 * @returns {object} Processing result
 */
export function copyFoundation(filePath, { rootDir, distDir, log }) {
  const componentName = getComponentName(filePath);
  const outputPath = getFoundationOutputPath(componentName, distDir);
 
  try {
    fs.ensureDirSync(path.dirname(outputPath));
 
    const content = fs.readJsonSync(filePath);
    fs.writeJsonSync(outputPath, content, { spaces: 2 });
 
    log.detail(`Copied ${componentName}.foundation.uif.json`);
 
    return {
      success: true,
      componentName,
      inputPath: path.relative(rootDir, filePath),
      outputPath: path.relative(rootDir, outputPath),
    };
  } catch (error) {
    log.error(`Failed to copy ${componentName} foundation: ${error.message}`);
    return { success: false, componentName, error: error.message };
  }
}
 
/**
 * Process an SLDS system layer UIF by resolving extends chains and validating
 *
 * System layer files (*.system.slds.uif.json) extend from foundations and
 * apply SLDS-specific tokens, classes, and configurations.
 *
 * @param {string} filePath - Source UIF file path
 * @param {object} options - Options object
 * @param {string} options.rootDir - Project root directory
 * @param {string} options.distDir - Distribution output directory
 * @param {object} options.log - Logger instance
 * @param {boolean} options.verbose - Enable verbose output
 * @returns {Promise<object>} Processing result
 */
export async function processSldsSubsystem(filePath, { rootDir, distDir, log, verbose }) {
  const componentName = getComponentName(filePath);
  const outputPath = getSldsOutputPath(componentName, distDir);
  const startTime = Date.now();
 
  try {
    // Resolve extends chain
    log.detail(`Resolving ${componentName}...`);
    const resolved = await resolveExtends(filePath, {
      cache: true,
      onWarning: (msg) => log.warn(`${componentName}: ${msg}`),
    });
 
    // Validate merged UIF
    const validation = validateUif(resolved);
 
    if (!validation.valid) {
      for (const error of validation.errors) {
        log.error(`${componentName}: ${error.message}`);
        if (error.suggestion) {
          log.detail(`Suggestion: ${error.suggestion}`);
        }
      }
      return { success: false, componentName, errors: validation.errors };
    }
 
    // Show validation warnings in verbose mode
    if (verbose && validation.warnings.length > 0) {
      validation.warnings.forEach((warning) => log.warn(`${componentName}: ${warning.message}`));
    }
 
    // Write resolved UIF
    fs.ensureDirSync(path.dirname(outputPath));
    fs.writeJsonSync(outputPath, resolved, { spaces: 2 });
 
    const duration = Date.now() - startTime;
    log.detail(`Resolved ${componentName}.slds.uif.json (${duration}ms)`);
 
    return {
      success: true,
      componentName,
      inputPath: path.relative(rootDir, filePath),
      outputPath: path.relative(rootDir, outputPath),
      duration,
    };
  } catch (error) {
    log.error(`${componentName}: ${error.message}`);
    if (verbose && error.stack) {
      console.error(`\x1b[90m${error.stack}\x1b[0m`);
    }
    return { success: false, componentName, error: error.message };
  }
}
 
/**
 * Process a single UIF file (optimized for watch mode)
 * @param {string} filePath - Path to the changed UIF file
 * @param {object} options - Options object
 * @returns {Promise<object>} Build result with success status
 */
export async function buildSingleUif(filePath, options) {
  const { log } = options;
  const fileName = path.basename(filePath);
  const componentName = getComponentName(filePath);
  const startTime = Date.now();
 
  try {
    let result;
 
    // Determine file type and process accordingly
    // Per RFC convention:
    //   .foundation.uif.json → Foundation layer (copy as-is)
    //   .system.slds.uif.json → System layer for SLDS (resolve extends)
    if (fileName.endsWith('.foundation.uif.json')) {
      log.detail(`Copying ${componentName} foundation...`);
      result = copyFoundation(filePath, options);
      result.type = 'foundation';
    } else if (fileName.endsWith('.system.slds.uif.json')) {
      log.detail(`Resolving ${componentName} system layer...`);
      result = await processSldsSubsystem(filePath, options);
      result.type = 'system';
    } else {
      log.warn(`Unknown UIF file type: ${fileName}`);
      return { success: false, componentName, error: 'Unknown file type' };
    }
 
    const duration = Date.now() - startTime;
 
    if (result.success) {
      log.success(`Built ${componentName} (${duration}ms)`);
    }
 
    return result;
  } catch (error) {
    log.error(`Failed to build ${componentName}: ${error.message}`);
    return { success: false, componentName, error: error.message };
  }
}