Files
minstrel/flutter_client/tool/gen_tokens.dart
T
bvandeusen 7d82be864d fix(flutter): handle nested colors split in tokens.json codegen
Web tokens.json was reshaped for the SPA theme toggle (#362) with
colors split into dark/light/flat blocks. gen_tokens.dart still
expected a flat string map and threw _Map<String,dynamic> cast
errors in the flutter CI workflow. Read colors.dark + colors.flat
and merge into the existing flat namespace; light variants will be
emitted when the Flutter client gains a theme toggle as part of
#356 parity work. Adds hyphen→camelCase normalization so the new
on-action token surfaces as onAction.
2026-05-03 14:46:27 -04:00

59 lines
2.2 KiB
Dart

// Reads shared/fabledsword.tokens.json and emits lib/theme/tokens.dart.
// Run via `dart run tool/gen_tokens.dart`. The generated file is
// committed (CI validates it matches the JSON to catch drift).
//
// Web tokens.json splits colors into `dark`, `light`, and `flat` blocks
// for the SPA's theme toggle. Flutter v1 ships dark-only, so we merge
// dark + flat into a single flat namespace. Light variants will be
// emitted alongside as part of the Flutter theme-toggle parity work.
import 'dart:convert';
import 'dart:io';
void main() {
final jsonFile = File('shared/fabledsword.tokens.json');
final tokens = jsonDecode(jsonFile.readAsStringSync()) as Map<String, dynamic>;
final colorsRoot = (tokens['colors'] as Map).cast<String, dynamic>();
final dark = (colorsRoot['dark'] as Map).cast<String, String>();
final flat = (colorsRoot['flat'] as Map).cast<String, String>();
final colors = <String, String>{...dark, ...flat};
final radii = (tokens['radii'] as Map).cast<String, String>();
final fonts = (tokens['fonts'] as Map).cast<String, String>();
final out = StringBuffer()
..writeln('// GENERATED — do not edit. Source: shared/fabledsword.tokens.json')
..writeln('// Run `dart run tool/gen_tokens.dart` to regenerate.')
..writeln("import 'package:flutter/material.dart';")
..writeln()
..writeln('class FabledSwordTokens {');
colors.forEach((name, hex) {
final value = hex.replaceFirst('#', '0xFF');
out.writeln(' static const Color ${_camel(name)} = Color($value);');
});
radii.forEach((name, px) {
final v = px.replaceAll('px', '');
out.writeln(' static const double radius${_pascal(name)} = $v;');
});
fonts.forEach((name, family) {
out.writeln(' static const String font${_pascal(name)} = ${jsonEncode(family)};');
});
out.writeln('}');
File('lib/theme/tokens.dart').writeAsStringSync(out.toString());
stdout.writeln('wrote lib/theme/tokens.dart');
}
// Hyphens become camelCase boundaries: on-action → onAction.
String _camel(String s) {
final parts = s.split('-');
return parts.first +
parts.skip(1).map((p) => p[0].toUpperCase() + p.substring(1)).join();
}
String _pascal(String s) => s[0].toUpperCase() + s.substring(1);