UNPKG

@sil/themer

Version:
250 lines (206 loc) 6.98 kB
import { readFileSync } from "fs"; import { join } from "path"; import { hello } from "@sil/tools"; import { RGB, COLOR, hexToRgb, toHex, toRGB, toHSL, mix } from "@sil/color"; import { jsOutput, tsOutput, sassOutput,jsonOutput } from "./output.js"; import { toDarkMode, toLightMode, getColorMode } from "./color.js"; import { state } from "./state.js"; import { getKey, getTextColor, getEnv, writeFile } from "./helpers.js"; import { ColorSet, ColorMode } from "./types.js"; import { blockFooter, blockHeader, blockLine, blockMid, blockRowLine, bold, yellow, dim, blockSettings, blockLineSuccess, } from "cli-block"; const buildColors = ( ogColors: ColorSet, mixColor: COLOR, mixColorAlt: COLOR ) => { const newColors: { [key: string]: COLOR | number } = {}; const mixRgb = hexToRgb(toHex(mixColor)); const altMixRgb = hexToRgb(toHex(mixColorAlt)); Object.keys(ogColors).forEach((key) => { const isBase = key == "background" || key == "foreground"; state.settings.colorSteps.forEach((step) => { const color: COLOR = getKey(ogColors, key); const mixer = color == mixColor ? altMixRgb : mixRgb; const mixStep = isBase ? Math.abs(100 - step) : step; const mixed = mix(mixer, toRGB(color), mixStep) as RGB; const hsl = toHSL(color); newColors[`${key}${step}`] = toHex(mixed); newColors[`${key}${step}-r`] = mixed.r; newColors[`${key}${step}-g`] = mixed.g; newColors[`${key}${step}-b`] = mixed.b; newColors[`${key}${step}-h`] = hsl.h; newColors[`${key}${step}-s`] = hsl.s; newColors[`${key}${step}-l`] = hsl.l; newColors[`${key}${step}-text`] = getTextColor(state, mixed); }); }); // Create RGB for input colors Object.keys(ogColors).forEach((key) => { const color: COLOR = getKey(ogColors, key); const rgb = toRGB(color); const hsl = toHSL(color); const textColor = getTextColor(state, color); newColors[`${key}-r`] = rgb.r; newColors[`${key}-g`] = rgb.g; newColors[`${key}-b`] = rgb.b; newColors[`${key}-h`] = hsl.h; newColors[`${key}-s`] = hsl.s; newColors[`${key}-l`] = hsl.l; newColors[`${key}-text`] = textColor; }); // Rgb's return newColors; }; // Get files const getFiles = () => { const env = getEnv(); try { const data = readFileSync(join(env.local, "themer.json"), { encoding: "utf8", }); state.local = JSON.parse(data); } catch (err) { console.error("woops"); } }; /* * Steps */ // Generate files const setOutputs = async() => { state.output = { ...state.themer.outputs, ...state.local.outputs }; await blockMid('Outputs') await blockSettings(state.output) }; const mergeData = async () => { blockMid("Colors"); const mergedColors = { ...(state?.themer?.colors ? state.themer.colors : {}), ...state.local.colors, }; Object.keys(mergedColors).forEach((key: string) => { let localColor = ""; if (state.local.colors && getKey(state.local.colors, key)) { localColor = getKey(state.local.colors, key); } const themerColor = getKey(state.themer.colors, key); if (themerColor == localColor) { blockRowLine([bold(key), getKey(mergedColors, key)]); } else { blockRowLine([ bold(key), `${dim(themerColor)} ${yellow("→")} ${localColor}`, ]); } }); state.colors.og = mergedColors as ColorSet; blockMid("Settings"); const mergedSettings = { ...state.themer.settings, ...state.local.settings }; Object.keys(mergedSettings).forEach((key) => { blockRowLine([key, getKey(mergedSettings, key)]); }); state.settings = mergedSettings; blockMid("Base"); const mergedBase = { ...state.themer.base, ...state.local.base }; Object.keys(mergedBase).forEach((key) => { blockRowLine([key, getKey(mergedBase, key)]); }); state.base = mergedBase; blockMid("Typography"); const mergedTypography = { ...state.themer.typography, ...state.local.typography, }; Object.keys(mergedTypography).forEach((key) => { blockRowLine([key, getKey(mergedTypography, key)]); }); state.typography = mergedTypography; }; const fixColors = () => { if (state.colors.og.background && state.colors.og.foreground) { state.colors.mode = getColorMode(state.colors.og); } state.colors.og.dark = state.colors.og.dark || state.colors.mode == ColorMode.DARK ? state.colors.og.background : state.colors.og.foreground; state.colors.og.light = state.colors.og.light || state.colors.mode == ColorMode.LIGHT ? state.colors.og.background : state.colors.og.foreground; }; const createColors = () => { const ogColors = Object.assign({}, state.colors.og); const darkColor = state.colors.og.dark || "#000000"; const lightColor = state.colors.og.light || "#ffffff"; const darkmodeColorSet = toDarkMode(state.colors.og); const lightmodeColorSet = toLightMode(state.colors.og); const lightModeColors = buildColors(lightmodeColorSet, lightColor, darkColor); const darkModeColors = buildColors(darkmodeColorSet, darkColor, lightColor); state.colors.light = { ...ogColors, ...lightModeColors }; state.colors.dark = { ...ogColors, ...darkModeColors }; // Fix background / Foreground if (state.colors.mode == "light") { state.colors.dark.background = state.colors.og.foreground; state.colors.dark.foreground = state.colors.og.background; } else { state.colors.light.background = state.colors.og.foreground; state.colors.light.foreground = state.colors.og.background; } // Set default colors according to current colormode state.colors.default = state.colors.mode == "light" ? state.colors.light : state.colors.dark; }; const writeConfig = async () => { const env = getEnv(); if (state.output.scss) { const sassData = sassOutput(state); const sassFile = join(env.local, state.output.scss); await writeFile(sassFile, sassData); } if (state.output.js) { const jsData = jsOutput(state); const jsFile = join(env.local, state.output.js || "src/data/theme.js"); await writeFile(jsFile, jsData); } if (state.output.ts) { const tsData = tsOutput(state); const tsFile = join(env.local, state.output.ts || "src/data/theme.ts"); await writeFile(tsFile, tsData); } if (state.output.json) { const jsonData = jsonOutput(state); const jsonFile = join(env.local, state.output.json || "src/data/theme.json"); await writeFile(jsonFile, jsonData); } }; // Runner hello() .then(() => { blockHeader("Themer Config"); blockLine(); blockLine([ "Themer is creating your config files. If you didnt define any", "custom options, Themer will just use the defaults", ]); }) .then(() => getFiles()) .then(() => mergeData()) .then(() => setOutputs()) .then(() => fixColors()) .then(() => createColors()) .then(() => writeConfig()) .then(() => { blockFooter(); });