Files
dss/scripts/figma-sync.py
Bruno Sarlo b3c0c0589e
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
fix: Use correct field name 'classification' instead of 'atomic_type'
Component model uses 'classification' field for AtomicType enum

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 06:48:14 -03:00

188 lines
6.6 KiB
Python
Executable File

#!/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 typing import Dict
# Ensure the project root is in the Python path BEFORE importing dss modules
DSS_ROOT = Path(__file__).parent.parent
if str(DSS_ROOT) not in sys.path:
sys.path.insert(0, str(DSS_ROOT))
from dss.ingest.base import TokenCollection
from dss.ingest.sources.figma import FigmaTokenSource
# =============================================================================
# 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, components = 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)
# Build component registry dict from List[Component]
component_registry = {
"file_key": file_key,
"component_count": len(components),
"components": [
{
"name": c.name,
"classification": c.classification.value if hasattr(c.classification, 'value') else str(c.classification),
"variant_count": len(c.variants) if c.variants else 0,
}
for c in components
]
}
writer.write_components(component_registry)
# --- Summary ---
print_summary(
file_name=file_key,
token_count=len(token_collection),
component_count=len(components),
)
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())