#!/usr/bin/env python3 """ DSS Figma Sync CLI. This script is a lightweight CLI wrapper around the FigmaTokenSource from the dss.ingest module. It fetches tokens and components from Figma and saves them to the project's .dss directory. The core extraction and processing logic resides in: dss.ingest.sources.figma.FigmaTokenSource """ import argparse import asyncio import json import os import sys from pathlib import Path from dss.ingest.base import TokenCollection from dss.ingest.sources.figma import FigmaTokenSource # Ensure the project root is in the Python path DSS_ROOT = Path(__file__).parent.parent if str(DSS_ROOT) not in sys.path: sys.path.insert(0, str(DSS_ROOT)) # ============================================================================= # CONFIGURATION # ============================================================================= CACHE_DIR = DSS_ROOT / ".dss/cache" TOKENS_DIR = DSS_ROOT / ".dss/data/_system/tokens" COMPONENTS_DIR = DSS_ROOT / ".dss/components" # ============================================================================= # OUTPUT WRITER # ============================================================================= class OutputWriter: """Writes extraction results to the DSS file structure.""" def __init__(self, verbose: bool = False): self.verbose = verbose def write_token_collection(self, collection: TokenCollection, output_dir: Path = TOKENS_DIR): """Write TokenCollection to a structured JSON file.""" output_dir.mkdir(parents=True, exist_ok=True) tokens_file = output_dir / "figma-tokens.json" if self.verbose: print(f" [OUT] Writing {len(collection)} tokens to {tokens_file}") with open(tokens_file, "w") as f: json.dump(json.loads(collection.to_json()), f, indent=2) print(f" [OUT] Tokens: {tokens_file}") def write_components(self, components: Dict, output_dir: Path = COMPONENTS_DIR): """Write component registry.""" output_dir.mkdir(parents=True, exist_ok=True) comp_file = output_dir / "figma-registry.json" if self.verbose: print( f" [OUT] Writing {components.get('component_count', 0)} components to {comp_file}" ) with open(comp_file, "w") as f: json.dump(components, f, indent=2) print(f" [OUT] Components: {comp_file}") # ============================================================================= # MAIN ORCHESTRATOR # ============================================================================= async def main(): """Main CLI orchestration function.""" parser = argparse.ArgumentParser(description="DSS Intelligent Figma Sync") parser.add_argument("--file-key", help="Figma file key to sync") parser.add_argument("--force", action="store_true", help="Force sync, ignoring cache") parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output") args = parser.parse_args() # --- Configuration Loading --- config = load_config() file_key = args.file_key or config.get("uikit_reference", {}).get("file_key") token = os.environ.get("FIGMA_TOKEN") or config.get("token") if not file_key: print("[ERROR] No Figma file key provided.", file=sys.stderr) print(" Provide it with --file-key or in .dss/config/figma.json", file=sys.stderr) sys.exit(1) if not token: print("[ERROR] No Figma token found.", file=sys.stderr) print(" Set FIGMA_TOKEN env var or add 'token' to .dss/config/figma.json", file=sys.stderr) sys.exit(1) print_header(file_key, token, args.force) # --- Extraction --- try: source = FigmaTokenSource(figma_token=token, verbose=args.verbose) token_collection, component_registry = await source.extract(file_key) except Exception as e: print(f"\n[ERROR] An error occurred during extraction: {e}", file=sys.stderr) # In verbose mode, print more details if args.verbose: import traceback traceback.print_exc() sys.exit(1) # --- Writing Output --- print("\n[3/3] Writing output...") writer = OutputWriter(verbose=args.verbose) writer.write_token_collection(token_collection) writer.write_components(component_registry) # --- Summary --- print_summary( file_name=component_registry.get("file_name", "Unknown"), token_count=len(token_collection), component_count=component_registry.get("component_count", 0), ) print("\n[OK] Sync successful!") print(" Next: Run the translation and theming pipeline.") sys.exit(0) def load_config() -> Dict: """Load Figma config from .dss/config/figma.json.""" config_path = DSS_ROOT / ".dss/config/figma.json" if config_path.exists(): try: with open(config_path) as f: return json.load(f) except (json.JSONDecodeError, IOError) as e: print( f"[WARN] Could not read or parse config file: {config_path}\n{e}", file=sys.stderr ) return {} def print_header(file_key: str, token: str, force: bool): """Prints the CLI header.""" print("╔══════════════════════════════════════════════════════════════╗") print("║ DSS FIGMA SYNC (INGESTION CLI) v3.0 ║") print("╚══════════════════════════════════════════════════════════════╝") print(f" File: {file_key}") print(f" Token: {token[:10]}...") print(f" Force: {force}") print("\n[1/3] Initializing Figma Ingestion Source...") def print_summary(file_name: str, token_count: int, component_count: int): """Prints the final summary.""" print("\n" + "=" * 60) print("SYNC COMPLETE") print("=" * 60) print(f" File: {file_name}") print(f" Tokens: {token_count}") print(f" Components: {component_count}") if __name__ == "__main__": asyncio.run(main())