feat: generate Swift design tokens from tokens.json and add SstUI base

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 15:19:06 +02:00
co-authored by Claude Sonnet 5.5
parent bf731dab37
commit bd6bc3b890
10 changed files with 659 additions and 21 deletions
@@ -0,0 +1,58 @@
import Foundation
import Testing
/// Root of the package: Tests/SstUITests/<this file> → three levels up.
private let root = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
struct TokenIntegrityTests {
@Test func generatedSwiftMatchesTokensJSONAndContrastHolds() throws {
#expect(
FileManager.default.fileExists(atPath: root.appending(path: "Sources/SstUI/DesignTokens.swift").path),
"Sources/SstUI/DesignTokens.swift ontbreekt: run `python3 Design/build_tokens.py`.")
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = ["python3", "Design/build_tokens.py", "--check"]
process.currentDirectoryURL = root
let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe
try process.run()
process.waitUntilExit()
let output = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
#expect(process.terminationStatus == 0, "Run `python3 Design/build_tokens.py`.\n\(output.suffix(600))")
}
/// Views take every colour, size and time from the tokens. A line may opt out with `// token-exempt: reason`.
@Test func viewsContainNoLooseDesignValues() throws {
let forbidden: [(name: String, pattern: String)] = [
("hex colour", #"0x[0-9A-Fa-f]{6}\b"#),
("component colour", #"(NS)?Color\((red|white|hue):"#),
("literal font size", #"\.system\(size:\s*\d"#),
("literal corner radius", #"cornerRadius:\s*\d|\.cornerRadius\(\s*\d"#),
("literal padding", #"\.padding\(\s*(\.\w+\s*,\s*)?\d"#),
("literal frame size", #"(width|height|minWidth|minHeight|maxWidth|maxHeight|idealWidth|idealHeight):\s*[1-9]"#),
("literal line width", #"lineWidth:\s*\d"#),
("literal opacity", #"\.opacity\(\s*[0-9]"#),
]
let skipped: Set<String> = ["DesignTokens.swift", "DynamicColor.swift"]
var directories = [root.appending(path: "Sources/SstUI")]
let app = root.appending(path: "Sources/Sst")
if FileManager.default.fileExists(atPath: app.path) { directories.append(app) }
var problems: [String] = []
for directory in directories {
let files = try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil)
.filter { $0.pathExtension == "swift" && !skipped.contains($0.lastPathComponent) }
for file in files {
let lines = try String(contentsOf: file, encoding: .utf8).components(separatedBy: "\n")
for (index, line) in lines.enumerated() where !line.contains("token-exempt") {
for rule in forbidden where line.range(of: rule.pattern, options: .regularExpression) != nil {
problems.append("\(file.lastPathComponent):\(index + 1) \(rule.name): \(line.trimmingCharacters(in: .whitespaces))")
}
}
}
}
#expect(problems.isEmpty, "Loose design values:\n\(problems.joined(separator: "\n"))")
}
}