defmodule Mix.Tasks.Jetons.Convert do @moduledoc """ Converts Token Studio folder structure to DTCG resolver format. Token Studio exports tokens as a folder with: - `$metadata.json` - contains `tokenSetOrder` for merge precedence - `$themes.json` - array of theme definitions with `selectedTokenSets` - Individual token set files in subdirectories (e.g., `acme/base.json`) This task generates a `.resolver.json` file that can be used with `mix jetons.build` or `Jetons.Resolver.resolve_file!/2`. ## Usage mix jetons.convert path/to/tokens -o tokens.resolver.json ## Options * `-o, --output` - Output file path (default: `tokens.resolver.json`) * `--modifier` - Name for the theme modifier (default: `theme`) ## Example # Convert Token Studio export mix jetons.convert ./tokens -o design.resolver.json # Then use with build mix jetons.build -f design.resolver.json --set theme=light -o tokens.css """ use Mix.Task alias Jetons.TokenStudio @shortdoc "Convert Token Studio format to DTCG resolver" @switches [output: :string, modifier: :string] @aliases [o: :output] @impl true def run(args) do {opts, [dir]} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) token_dir = Path.expand(dir) output_path = Path.expand(opts[:output] || "tokens.resolver.json") modifier_name = opts[:modifier] || "theme" validate_directory!(token_dir) resolver = token_dir |> TokenStudio.load() |> TokenStudio.to_resolver( modifier_name: modifier_name, token_dir: token_dir, output_dir: Path.dirname(output_path) ) write_json!(output_path, resolver) Mix.shell().info("Generated #{output_path}") end defp validate_directory!(dir) do unless File.dir?(dir) do raise Mix.Error, "Not a directory: #{dir}" end end defp write_json!(path, data) do File.write!(path, Jason.encode!(data, pretty: true)) end end