✨(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:
committed by
Jean-Baptiste PENRATH
parent
72091eecbc
commit
1667d9b501
5
.changeset/pink-fishes-jump.md
Normal file
5
.changeset/pink-fishes-jump.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@openfun/cunningham-tokens": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add a sass generator
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"build-bin": "cd src/bin && tsc -p tsconfig.json && tsc-alias && chmod +x ../../dist/bin/Main.js",
|
"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-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": "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"
|
"test": "FORCE_COLOR=1 jest --runInBand --verbose src"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
50
packages/tokens/src/bin/Generators/SassGenerator.spec.ts
Normal file
50
packages/tokens/src/bin/Generators/SassGenerator.spec.ts
Normal 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
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
80
packages/tokens/src/bin/Generators/SassGenerator.ts
Normal file
80
packages/tokens/src/bin/Generators/SassGenerator.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { cssGenerator } from "Generators/CssGenerator";
|
import { cssGenerator } from "Generators/CssGenerator";
|
||||||
|
import { sassGenerator } from "Generators/SassGenerator";
|
||||||
import { jsGenerator } from "Generators/JsGenerator";
|
import { jsGenerator } from "Generators/JsGenerator";
|
||||||
import { Tokens } from "TokensGenerator";
|
import { Tokens } from "TokensGenerator";
|
||||||
import { tsGenerator } from "Generators/TsGenerator";
|
import { tsGenerator } from "Generators/TsGenerator";
|
||||||
@@ -9,6 +10,7 @@ export type Generator = (
|
|||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
export const Generators: Record<string, Generator> = {
|
export const Generators: Record<string, Generator> = {
|
||||||
|
scss: sassGenerator,
|
||||||
css: cssGenerator,
|
css: cssGenerator,
|
||||||
js: jsGenerator,
|
js: jsGenerator,
|
||||||
ts: tsGenerator,
|
ts: tsGenerator,
|
||||||
|
|||||||
@@ -18,5 +18,8 @@ module.exports = {
|
|||||||
spacings: {
|
spacings: {
|
||||||
s: "1rem",
|
s: "1rem",
|
||||||
},
|
},
|
||||||
|
transitions: {
|
||||||
|
ease: "linear",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,4 +5,5 @@
|
|||||||
--c--theme--font--weights--medium: 400;
|
--c--theme--font--weights--medium: 400;
|
||||||
--c--theme--font--families--base: Roboto;
|
--c--theme--font--families--base: Roboto;
|
||||||
--c--theme--spacings--s: 1rem;
|
--c--theme--spacings--s: 1rem;
|
||||||
|
--c--theme--transitions--ease: linear;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,5 @@
|
|||||||
--c--theme--font--weights--medium: 400;
|
--c--theme--font--weights--medium: 400;
|
||||||
--c--theme--font--families--base: Roboto;
|
--c--theme--font--families--base: Roboto;
|
||||||
--c--theme--spacings--s: 1rem;
|
--c--theme--spacings--s: 1rem;
|
||||||
|
--c--theme--transitions--ease: linear;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
--c--theme--font--weights--medium: 400;
|
--c--theme--font--weights--medium: 400;
|
||||||
--c--theme--font--families--base: Roboto;
|
--c--theme--font--families--base: Roboto;
|
||||||
--c--theme--spacings--s: 1rem;
|
--c--theme--spacings--s: 1rem;
|
||||||
|
--c--theme--transitions--ease: linear;
|
||||||
} .clr-primary { color: var(--c--theme--colors--primary); }
|
} .clr-primary { color: var(--c--theme--colors--primary); }
|
||||||
.clr-secondary { color: var(--c--theme--colors--secondary); }
|
.clr-secondary { color: var(--c--theme--colors--secondary); }
|
||||||
.bg-primary { background-color: var(--c--theme--colors--primary); }
|
.bg-primary { background-color: var(--c--theme--colors--primary); }
|
||||||
|
|||||||
Reference in New Issue
Block a user