Add foundation for exporting tokens

Nate Butler created

Change summary

styles/package-lock.json               | 11 +++++++
styles/package.json                    |  4 ++
styles/src/buildTokens.ts              | 39 ++++++++++++++++++++++++++++
styles/src/theme/tokens/colorScheme.ts | 12 ++++++++
styles/src/theme/tokens/players.ts     | 28 ++++++++++++++++++++
styles/src/theme/tokens/token.ts       | 14 ++++++++++
styles/src/utils/slugify.ts            |  1 
7 files changed, 108 insertions(+), 1 deletion(-)

Detailed changes

styles/package-lock.json 🔗

@@ -9,6 +9,7 @@
             "version": "1.0.0",
             "license": "ISC",
             "dependencies": {
+                "@tokens-studio/types": "^0.2.3",
                 "@types/chroma-js": "^2.4.0",
                 "@types/node": "^18.14.1",
                 "ayu": "^8.0.1",
@@ -53,6 +54,11 @@
                 "@jridgewell/sourcemap-codec": "^1.4.10"
             }
         },
+        "node_modules/@tokens-studio/types": {
+            "version": "0.2.3",
+            "resolved": "https://registry.npmjs.org/@tokens-studio/types/-/types-0.2.3.tgz",
+            "integrity": "sha512-2KN3V0JPf+Zh8aoVMwykJq29Lsi7vYgKGYBQ/zQ+FbDEmrH6T/Vwn8kG7cvbTmW1JAAvgxVxMIivgC9PmFelNA=="
+        },
         "node_modules/@tsconfig/node10": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@@ -271,6 +277,11 @@
                 "@jridgewell/sourcemap-codec": "^1.4.10"
             }
         },
+        "@tokens-studio/types": {
+            "version": "0.2.3",
+            "resolved": "https://registry.npmjs.org/@tokens-studio/types/-/types-0.2.3.tgz",
+            "integrity": "sha512-2KN3V0JPf+Zh8aoVMwykJq29Lsi7vYgKGYBQ/zQ+FbDEmrH6T/Vwn8kG7cvbTmW1JAAvgxVxMIivgC9PmFelNA=="
+        },
         "@tsconfig/node10": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",

styles/package.json 🔗

@@ -5,11 +5,13 @@
     "main": "index.js",
     "scripts": {
         "build": "ts-node ./src/buildThemes.ts",
-        "build-licenses": "ts-node ./src/buildLicenses.ts"
+        "build-licenses": "ts-node ./src/buildLicenses.ts",
+        "build-tokens": "ts-node ./src/buildTokens.ts"
     },
     "author": "",
     "license": "ISC",
     "dependencies": {
+        "@tokens-studio/types": "^0.2.3",
         "@types/chroma-js": "^2.4.0",
         "@types/node": "^18.14.1",
         "ayu": "^8.0.1",

styles/src/buildTokens.ts 🔗

@@ -0,0 +1,39 @@
+import * as fs from "fs"
+import * as path from "path"
+import { ColorScheme, createColorScheme } from "./common"
+import { themes } from "./themes"
+import { slugify } from "./utils/slugify"
+import { colorSchemeTokens } from "./theme/tokens/colorScheme"
+
+const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens")
+
+function clearTokens(tokensDirectory: string) {
+    if (!fs.existsSync(tokensDirectory)) {
+        fs.mkdirSync(tokensDirectory, { recursive: true })
+    } else {
+        for (const file of fs.readdirSync(tokensDirectory)) {
+            if (file.endsWith(".json")) {
+                fs.unlinkSync(path.join(tokensDirectory, file))
+            }
+        }
+    }
+}
+
+function writeTokens(colorSchemes: ColorScheme[], tokensDirectory: string) {
+    clearTokens(tokensDirectory)
+
+    for (const colorScheme of colorSchemes) {
+        const fileName = slugify(colorScheme.name)
+        const tokens = colorSchemeTokens(colorScheme)
+        const tokensJSON = JSON.stringify(tokens, null, 2)
+        const outPath = path.join(tokensDirectory, `${fileName}.json`)
+        fs.writeFileSync(outPath, tokensJSON)
+        console.log(`- ${outPath} created`)
+    }
+}
+
+const colorSchemes: ColorScheme[] = themes.map((theme) =>
+    createColorScheme(theme)
+)
+
+writeTokens(colorSchemes, TOKENS_DIRECTORY)

styles/src/theme/tokens/colorScheme.ts 🔗

@@ -0,0 +1,12 @@
+import { ColorScheme } from "../colorScheme"
+import { PlayerTokens, players } from "./players"
+
+interface ColorSchemeTokens {
+    players: PlayerTokens
+}
+
+export function colorSchemeTokens(colorScheme: ColorScheme): ColorSchemeTokens {
+    return {
+        players: players(colorScheme),
+    }
+}

styles/src/theme/tokens/players.ts 🔗

@@ -0,0 +1,28 @@
+import { SingleColorToken } from "@tokens-studio/types"
+import { ColorScheme, Players } from "../../common"
+import { colorToken } from "./token"
+
+export type PlayerToken = Record<"selection" | "cursor", SingleColorToken>
+
+export type PlayerTokens = Record<keyof Players, PlayerToken>
+
+function buildPlayerToken(colorScheme: ColorScheme, index: number): PlayerToken {
+
+    const playerNumber = index.toString() as keyof Players
+
+    return {
+        selection: colorToken(`player${index}Selection`, colorScheme.players[playerNumber].selection),
+        cursor: colorToken(`player${index}Cursor`, colorScheme.players[playerNumber].cursor),
+    }
+}
+
+export const players = (colorScheme: ColorScheme): PlayerTokens => ({
+    "0": buildPlayerToken(colorScheme, 0),
+    "1": buildPlayerToken(colorScheme, 1),
+    "2": buildPlayerToken(colorScheme, 2),
+    "3": buildPlayerToken(colorScheme, 3),
+    "4": buildPlayerToken(colorScheme, 4),
+    "5": buildPlayerToken(colorScheme, 5),
+    "6": buildPlayerToken(colorScheme, 6),
+    "7": buildPlayerToken(colorScheme, 7)
+})

styles/src/theme/tokens/token.ts 🔗

@@ -0,0 +1,14 @@
+import { SingleColorToken, TokenTypes } from "@tokens-studio/types"
+
+export function colorToken(name: string, value: string, description?: string): SingleColorToken {
+    const token: SingleColorToken = {
+        name,
+        type: TokenTypes.COLOR,
+        value,
+        description,
+    }
+
+    if (!token.value || token.value === '') throw new Error("Color token must have a value")
+
+    return token
+}

styles/src/utils/slugify.ts 🔗

@@ -0,0 +1 @@
+export function slugify(t: string): string { return t.toString().toLowerCase().replace(/\s+/g, '-').replace(/[^\w\-]+/g, '').replace(/\-\-+/g, '-').replace(/^-+/, '').replace(/-+$/, '') }