(bin) add tokens repo

This repo is made for the bin script that generates the tokens files,
at the moment it only generates a css file, but it is designed to be
able to generate any other file format ( Typescript, Javascript for example )
This commit is contained in:
Nathan Vasse
2022-12-01 12:03:37 +01:00
committed by NathanVss
parent 16172e7a00
commit 32a48e9e46
18 changed files with 440 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
export default {
configurationFilenames: ["cunningham.js", "cunningham.cjs"],
sass: {
varSeparator: "--",
varPrefix: "c--",
tokenFilenameCss: "cunningham-tokens.css",
},
};

View File

@@ -0,0 +1,32 @@
import path from "path";
import * as fs from "fs";
import deepmerge from "deepmerge";
import Config from "./Config";
import { ConfigShape } from "./TokensGenerator";
import { workPath } from "./Paths";
const getLocalConfig = async () => {
const filename = Config.configurationFilenames
.map((filename_) => path.join(workPath(), filename_))
.find((filename_) => fs.existsSync(filename_));
if (!filename) {
console.log("No local config found, using default config.");
return {};
}
const config = await import(filename);
return config.default;
};
const getDistConfig = async () => {
const config = await import("./cunningham.dist.js");
return config.default;
};
export const getConfig = async () => {
const localConfig = await getLocalConfig();
const distConfig = await getDistConfig();
const config: ConfigShape = deepmerge(distConfig, localConfig);
return config;
};

View File

@@ -0,0 +1,26 @@
import * as path from "path";
import * as fs from "fs";
import chalk from "chalk";
import Config from "./Config";
import { flatify } from "./Utils/Flatify";
export const cssGenerator = async (
tokens: any,
opts: { path: string; selector: string }
) => {
const flatTokens = flatify(tokens, Config.sass.varSeparator);
const cssVars = Object.keys(flatTokens).reduce((acc, token) => {
return (
acc + `\t--${Config.sass.varPrefix}${token}: ${flatTokens[token]};\n`
);
}, "");
const cssContent = `${opts.selector} {\n${cssVars}}`;
const dest = path.join(opts.path, Config.sass.tokenFilenameCss);
console.log("Generating tokens file to " + dest + " ...");
if (!fs.existsSync(opts.path)) {
fs.mkdirSync(opts.path);
}
fs.writeFileSync(dest, cssContent);
console.log(chalk.bgGreen(chalk.white("File generated successfully.")));
};

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env node
import { run } from "./ThemeGenerator";
run(process.argv);

View File

@@ -0,0 +1,3 @@
export const workPath = () => {
return process.cwd();
};

View File

@@ -0,0 +1,39 @@
import { program } from "commander";
import chalk from "chalk";
import figlet from "figlet";
import { getConfig } from "./ConfigLoader";
import { tokensGenerator } from "./TokensGenerator";
import { cssGenerator } from "./CssGenerator";
import { workPath } from "./Paths";
export const buildTheme = async () => {
const options = program.opts();
const config = await getConfig();
const tokens = tokensGenerator(config);
await cssGenerator(tokens, {
path: options.output,
selector: options.selector,
});
};
export const run = async (args: string[]) => {
console.log(
chalk.red(figlet.textSync("Cunningham", { horizontalLayout: "full" }))
);
program
.description("Cunningham's CLI tool.")
.option(
"-o, --output <directory>",
"Specify the output dir of generated files.",
workPath()
)
.option(
"-s, --selector <selector>",
"Specify the css root selector element.",
":root"
)
.parse(args);
await buildTheme();
};

View File

@@ -0,0 +1,15 @@
interface ThemeShape {
colors: {
[key: string]: string;
};
}
export interface ConfigShape {
theme: ThemeShape;
}
export const tokensGenerator = (config: ConfigShape) => {
return {
colors: { ...config.theme.colors },
};
};

View File

@@ -0,0 +1,15 @@
export const flatify = (obj: any, separator: string) => {
const flatObj: any = {};
Object.keys(obj).forEach((key) => {
const value = obj[key];
if (typeof value === "object") {
const flatChild = flatify(value, separator);
Object.keys(flatChild).forEach((subKey) => {
flatObj[key + separator + subKey] = flatChild[subKey];
});
} else {
flatObj[key] = value;
}
});
return flatObj;
};

View File

@@ -0,0 +1,8 @@
module.exports = {
theme: {
colors: {
primary: "#055FD2",
secondary: "#DA0000",
},
},
};

View File

@@ -0,0 +1,116 @@
import * as fs from "fs";
import * as path from "path";
import { run } from "../ThemeGenerator";
import Config from "../Config";
jest.mock("../Paths", () => ({
workPath: () => __dirname,
}));
/**
* Empty the current directory from generated tokens file and local
* config to start with an predictable environment.
*/
const cleanup = () => {
const filePath = path.join(__dirname, Config.sass.tokenFilenameCss);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
const localConfigurationFile = path.join(
__dirname,
Config.configurationFilenames[0]
);
if (fs.existsSync(localConfigurationFile)) {
fs.unlinkSync(localConfigurationFile);
}
const outputPath = path.join(__dirname, "output");
if (fs.existsSync(outputPath)) {
fs.rmSync(outputPath, { recursive: true });
}
};
describe("Cunningham Bin", () => {
beforeAll(() => {
jest.spyOn(console, "log").mockImplementation(() => {});
cleanup();
});
afterEach(() => {
cleanup();
});
it("Runs without existing config file with default values.", async () => {
const cssTokensFile = path.join(__dirname, Config.sass.tokenFilenameCss);
expect(fs.existsSync(cssTokensFile)).toEqual(false);
await run([]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
expect(fs.readFileSync(cssTokensFile).toString()).toEqual(`:root {
\t--c--colors--primary: #055FD2;
\t--c--colors--secondary: #DA0000;
}`);
});
it("Runs with existing config file using local values.", async () => {
const localConfigurationFile = path.join(
__dirname,
Config.configurationFilenames[0]
);
expect(fs.existsSync(localConfigurationFile)).toEqual(false);
const cssTokensFile = path.join(__dirname, Config.sass.tokenFilenameCss);
expect(fs.existsSync(cssTokensFile)).toEqual(false);
fs.copyFileSync(
path.join(__dirname, "assets", Config.configurationFilenames[0]),
localConfigurationFile
);
expect(fs.existsSync(localConfigurationFile)).toEqual(true);
await run([]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
expect(fs.readFileSync(cssTokensFile).toString()).toEqual(`:root {
\t--c--colors--primary: AntiqueWhite;
\t--c--colors--secondary: #DA0000;
}`);
});
const testOutput = async (opt: string) => {
const outputDir = path.join(__dirname, "output");
const cssTokensFile = path.join(outputDir, Config.sass.tokenFilenameCss);
expect(fs.existsSync(cssTokensFile)).toEqual(false);
await run(["", "", opt, outputDir]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
expect(fs.readFileSync(cssTokensFile).toString()).toEqual(`:root {
\t--c--colors--primary: #055FD2;
\t--c--colors--secondary: #DA0000;
}`);
};
it("Runs with -o options.", async () => {
await testOutput("-o");
});
it("Runs with --output options.", async () => {
await testOutput("--output");
});
const testSelector = async (opt: string) => {
const cssTokensFile = path.join(__dirname, Config.sass.tokenFilenameCss);
expect(fs.existsSync(cssTokensFile)).toEqual(false);
await run(["", "", opt, "html"]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
expect(fs.readFileSync(cssTokensFile).toString()).toEqual(`html {
\t--c--colors--primary: #055FD2;
\t--c--colors--secondary: #DA0000;
}`);
};
it("Runs with -s options.", async () => {
await testSelector("-s");
});
it("Runs with --selector options.", async () => {
await testSelector("--selector");
});
});

View File

@@ -0,0 +1,7 @@
module.exports = {
theme: {
colors: {
primary: "AntiqueWhite",
},
},
};