✨(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:
8
packages/tokens/src/bin/Config.ts
Normal file
8
packages/tokens/src/bin/Config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
configurationFilenames: ["cunningham.js", "cunningham.cjs"],
|
||||
sass: {
|
||||
varSeparator: "--",
|
||||
varPrefix: "c--",
|
||||
tokenFilenameCss: "cunningham-tokens.css",
|
||||
},
|
||||
};
|
||||
32
packages/tokens/src/bin/ConfigLoader.ts
Normal file
32
packages/tokens/src/bin/ConfigLoader.ts
Normal 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;
|
||||
};
|
||||
26
packages/tokens/src/bin/CssGenerator.ts
Normal file
26
packages/tokens/src/bin/CssGenerator.ts
Normal 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.")));
|
||||
};
|
||||
5
packages/tokens/src/bin/Main.ts
Normal file
5
packages/tokens/src/bin/Main.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { run } from "./ThemeGenerator";
|
||||
|
||||
run(process.argv);
|
||||
3
packages/tokens/src/bin/Paths.ts
Normal file
3
packages/tokens/src/bin/Paths.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const workPath = () => {
|
||||
return process.cwd();
|
||||
};
|
||||
39
packages/tokens/src/bin/ThemeGenerator.ts
Normal file
39
packages/tokens/src/bin/ThemeGenerator.ts
Normal 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();
|
||||
};
|
||||
15
packages/tokens/src/bin/TokensGenerator.ts
Normal file
15
packages/tokens/src/bin/TokensGenerator.ts
Normal 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 },
|
||||
};
|
||||
};
|
||||
15
packages/tokens/src/bin/Utils/Flatify.ts
Normal file
15
packages/tokens/src/bin/Utils/Flatify.ts
Normal 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;
|
||||
};
|
||||
8
packages/tokens/src/bin/cunningham.dist.js
Normal file
8
packages/tokens/src/bin/cunningham.dist.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
theme: {
|
||||
colors: {
|
||||
primary: "#055FD2",
|
||||
secondary: "#DA0000",
|
||||
},
|
||||
},
|
||||
};
|
||||
116
packages/tokens/src/bin/tests/Cunningham.spec.ts
Normal file
116
packages/tokens/src/bin/tests/Cunningham.spec.ts
Normal 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");
|
||||
});
|
||||
});
|
||||
7
packages/tokens/src/bin/tests/assets/cunningham.js
Normal file
7
packages/tokens/src/bin/tests/assets/cunningham.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
theme: {
|
||||
colors: {
|
||||
primary: "AntiqueWhite",
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user