(tokens) add scss generator

Add a Sass generator which generate a scss file declaring colors, font
families, weights, sizes and spacings as maps.
This commit is contained in:
jbpenrath
2023-04-17 16:35:12 +02:00
committed by Jean-Baptiste PENRATH
parent 72091eecbc
commit 1667d9b501
9 changed files with 144 additions and 1 deletions

View File

@@ -0,0 +1,5 @@
---
"@openfun/cunningham-tokens": minor
---
Add a sass generator

View File

@@ -28,7 +28,7 @@
"build-bin": "cd src/bin && tsc -p tsconfig.json && tsc-alias && chmod +x ../../dist/bin/Main.js",
"build-lib": "cp dist/cunningham-tokens.ts src/lib && cd src/lib && tsc -p tsconfig.json",
"build": "yarn build-bin && yarn build-default-theme && yarn build-lib && cd ../.. && ln -sf ../../packages/tokens/dist/bin/Main.js node_modules/.bin/cunningham",
"build-default-theme": "./dist/bin/Main.js -o dist -s html -g css,js,ts --utility-classes",
"build-default-theme": "./dist/bin/Main.js -o dist -s html -g scss,css,js,ts --utility-classes",
"test": "FORCE_COLOR=1 jest --runInBand --verbose src"
},
"dependencies": {

View File

@@ -0,0 +1,50 @@
import path from "path";
import fs from "fs";
import Config from "Config";
import { run } from "ThemeGenerator";
import { cleanup } from "tests/Utils";
jest.mock("../Paths", () => ({
workPath: () => __dirname,
}));
describe("SassGenerator", () => {
beforeAll(() => {
jest.spyOn(console, "log").mockImplementation(() => {});
cleanup(__dirname);
});
afterEach(() => {
cleanup(__dirname);
});
it("generates valid Sass file", async () => {
const sassFile = path.join(__dirname, Config.tokenFilename + ".scss");
expect(fs.existsSync(sassFile)).toEqual(false);
await run(["", "", "-g", "scss"]);
expect(fs.existsSync(sassFile)).toEqual(true);
expect(fs.readFileSync(sassFile).toString()).toMatchInlineSnapshot(`
"$colors: (
primary: #055FD2,
secondary: #DA0000
);
$fontFamilies: (
base: Roboto
);
$fontSizes: (
m: 1rem
);
$fontWeights: (
medium: 400
);
$spacings: (
s: 1rem
);
$transitions: (
ease: linear
);
"
`);
});
});

View File

@@ -0,0 +1,80 @@
import path from "path";
import { Generator } from "Generators/index";
import Config from "Config";
import { put } from "Utils/Files";
import { Tokens } from "TokensGenerator";
export const sassGenerator: Generator = async (tokens, opts) => {
const sassContent = generateSassMaps(tokens);
const outputPath = path.join(opts.path, Config.tokenFilename + ".scss");
put(outputPath, sassContent);
};
const generateSassMaps = (tokens: Tokens) => {
return [
generateColorsSassMap(tokens),
generateFontSassMap(tokens),
generateSpacingsSassMap(tokens),
generateTransitionsSassMap(tokens),
].join("\n");
};
// Generate colors sass map with the ability to create sub map
// for a color with shades.
// Example: `primary-500: #000` will return `primary: (500: #000)`
const generateColorsSassMap = (tokens: Tokens) => {
const colors = Object.entries(tokens.theme.colors).reduce(
propertiesReducer,
{}
);
return `$colors: ${JSONToSassMap(colors)};`;
};
const generateFontSassMap = (tokens: Tokens) => {
return [
generateFontFamiliesSassMap(tokens),
generateFontSizesSassMap(tokens),
generateFontWeightsSassMap(tokens),
].join("\n");
};
const generateFontFamiliesSassMap = (tokens: Tokens) => {
return `$fontFamilies: ${JSONToSassMap(tokens.theme.font.families)};`;
};
const generateFontSizesSassMap = (tokens: Tokens) => {
return `$fontSizes: ${JSONToSassMap(tokens.theme.font.sizes)};`;
};
const generateFontWeightsSassMap = (tokens: Tokens) => {
return `$fontWeights: ${JSONToSassMap(tokens.theme.font.weights)};`;
};
const generateSpacingsSassMap = (tokens: Tokens) => {
return `$spacings: ${JSONToSassMap(tokens.theme.spacings)};`;
};
const generateTransitionsSassMap = (tokens: Tokens) => {
return `$transitions: ${JSONToSassMap(tokens.theme.transitions)};`;
};
function JSONToSassMap(json: Object) {
return JSON.stringify(json, null, 2)
.replace(/{/g, "(")
.replace(/}/g, ")")
.replace(/"/g, "");
}
function propertiesReducer(
propertyMap: Record<string, string | Record<string, string>>,
[key, value]: [string, string]
) {
const [property, subProperty] = key.split("-");
if (subProperty) {
if (propertyMap[property] === undefined) {
propertyMap[property] = {};
}
(propertyMap[property] as Record<string, string>)[subProperty] = value;
} else {
propertyMap[property] = value;
}
return propertyMap;
}

View File

@@ -1,4 +1,5 @@
import { cssGenerator } from "Generators/CssGenerator";
import { sassGenerator } from "Generators/SassGenerator";
import { jsGenerator } from "Generators/JsGenerator";
import { Tokens } from "TokensGenerator";
import { tsGenerator } from "Generators/TsGenerator";
@@ -9,6 +10,7 @@ export type Generator = (
) => Promise<void>;
export const Generators: Record<string, Generator> = {
scss: sassGenerator,
css: cssGenerator,
js: jsGenerator,
ts: tsGenerator,

View File

@@ -18,5 +18,8 @@ module.exports = {
spacings: {
s: "1rem",
},
transitions: {
ease: "linear",
},
},
};

View File

@@ -5,4 +5,5 @@
--c--theme--font--weights--medium: 400;
--c--theme--font--families--base: Roboto;
--c--theme--spacings--s: 1rem;
--c--theme--transitions--ease: linear;
}

View File

@@ -5,4 +5,5 @@
--c--theme--font--weights--medium: 400;
--c--theme--font--families--base: Roboto;
--c--theme--spacings--s: 1rem;
--c--theme--transitions--ease: linear;
}

View File

@@ -5,6 +5,7 @@
--c--theme--font--weights--medium: 400;
--c--theme--font--families--base: Roboto;
--c--theme--spacings--s: 1rem;
--c--theme--transitions--ease: linear;
} .clr-primary { color: var(--c--theme--colors--primary); }
.clr-secondary { color: var(--c--theme--colors--secondary); }
.bg-primary { background-color: var(--c--theme--colors--primary); }