Skip to content

Commit 7ee51ab

Browse files
committed
parseColorOptions
1 parent f0a432e commit 7ee51ab

File tree

7 files changed

+253
-13
lines changed

7 files changed

+253
-13
lines changed

package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,23 @@
7272
"tsafe": "^1.0.1"
7373
},
7474
"devDependencies": {
75+
"@types/css": "^0.0.33",
7576
"@types/node": "^18.7.18",
7677
"@types/react": "18.0.21",
7778
"@types/react-dom": "18.0.6",
78-
"react": "18.2.0",
7979
"@typescript-eslint/eslint-plugin": "^4.24.0",
8080
"@typescript-eslint/parser": "^4.24.0",
81+
"css": "^3.0.0",
8182
"eslint": "^7.26.0",
8283
"eslint-config-prettier": "^8.3.0",
84+
"evt": "^2.4.2",
8385
"husky": "^4.3.8",
8486
"lint-staged": "^11.0.0",
87+
"next": "12.3.1",
8588
"prettier": "^2.3.0",
86-
"ts-node": "^10.2.1",
89+
"react": "18.2.0",
8790
"rimraf": "^3.0.2",
88-
"evt": "^2.4.2",
89-
"next": "12.3.1",
91+
"ts-node": "^10.2.1",
9092
"typescript": "^4.8.3"
9193
}
9294
}

src/bin/generate_theme/parseColorOptions.ts

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { ColorScheme } from "../sharedTypes";
22
import { capitalize } from "tsafe/capitalize";
33
import { id } from "tsafe/id";
4+
import css from "css";
5+
import { assert } from "tsafe/assert";
6+
import { data_fr_theme } from "../sharedTypes";
7+
import { exclude } from "tsafe/exclude";
48

59
/*
610
This type doesn't exist
@@ -15,9 +19,7 @@ type ColorOptions = {
1519
1620
};
1721
18-
*/
1922
20-
/** Generated */
2123
export function getColorOptions(colorScheme: ColorScheme) {
2224
const isDark: boolean = (() => {
2325
switch (colorScheme) {
@@ -38,6 +40,7 @@ export function getColorOptions(colorScheme: ColorScheme) {
3840
}
3941
} as const;
4042
}
43+
*/
4144

4245
/*
4346
https://www.systeme-de-design.gouv.fr/elements-d-interface/fondamentaux-identite-de-l-etat/couleurs-palette
@@ -350,10 +353,75 @@ export function getThemePath(parsedColorOptionName: ParsedColorOptionName): stri
350353
];
351354
}
352355

353-
export type ParsedColorOption = {
354-
parsedColorOptionName: ParsedColorOptionName;
355-
lightValue: `#${string}`;
356-
darkValue: `#${string}` | undefined;
356+
export type ColorOption = {
357+
themePath: string[];
358+
color:
359+
| string
360+
| {
361+
light: `#${string}`;
362+
dark: `#${string}`;
363+
};
357364
};
358365

359-
export declare function parseColorOptions(rawCssCode: string): ParsedColorOption[];
366+
export function parseColorOptions(rawCssCode: string): ColorOption[] {
367+
const parsedCss = css.parse(rawCssCode);
368+
369+
const { declarations } = (() => {
370+
const node = parsedCss.stylesheet?.rules.find(
371+
rule => rule.type === "rule" && (rule as any)?.selectors?.[0] === ":root"
372+
);
373+
374+
assert(node !== undefined);
375+
376+
const { declarations } = node as any;
377+
378+
return { declarations };
379+
})();
380+
381+
const { declarationsDark } = (() => {
382+
const node = parsedCss.stylesheet?.rules.find(
383+
rule =>
384+
rule.type === "rule" &&
385+
(rule as any)?.selectors?.[0] === `:root:where([${data_fr_theme}="dark"])`
386+
);
387+
388+
assert(node !== undefined);
389+
390+
const { declarations: declarationsDark } = node as any;
391+
392+
return { declarationsDark };
393+
})();
394+
395+
return declarations
396+
.map(({ property: colorName, value: color }: any) => {
397+
const htmlColorRegexp = /^#[0-9a-f]{6}$/;
398+
399+
if (!htmlColorRegexp.test(color)) {
400+
return undefined;
401+
}
402+
403+
const parsedName = parseColorOptionName(colorName);
404+
405+
const colorDark = declarationsDark.find(
406+
({ property }: any) => property === colorName
407+
)?.value;
408+
409+
assert(typeof colorDark === "string");
410+
assert(htmlColorRegexp.test(colorDark));
411+
412+
if (parsedName.brightness.isInvariant) {
413+
assert(color === colorDark);
414+
}
415+
416+
return {
417+
"themePath": getThemePath(parsedName),
418+
"color": parsedName.brightness.isInvariant
419+
? color
420+
: {
421+
"light": color,
422+
"dark": colorDark
423+
}
424+
};
425+
})
426+
.filter(exclude(undefined));
427+
}

src/bin/sharedTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
//NOTE: lib/ can import from bin but not the other way around.
22

33
export type ColorScheme = "light" | "dark";
4+
export const data_fr_theme = "data-fr-theme";

src/lib/useColorScheme.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { useConstCallback } from "./tools/powerhooks/useConstCallback";
33
import { assert } from "tsafe/assert";
44
import { isBrowser } from "./tools/isBrowser";
55
import type { ColorScheme } from "../bin/sharedTypes";
6+
import { data_fr_theme } from "../bin/sharedTypes";
67

78
export type { ColorScheme };
9+
export { data_fr_theme };
810

911
export const data_fr_scheme = "data-fr-scheme";
10-
export const data_fr_theme = "data-fr-theme";
1112

1213
export const $colorScheme = createStatefulObservable<ColorScheme>(() => "light");
1314

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
import "./parseColorOptionName.test";
22
import "./getThemePath.test";
3+
import "./parseColorOptions.test";
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { parseColorOptions } from "../../../bin/generate_theme/parseColorOptions";
2+
import type { ColorOption } from "../../../bin/generate_theme/parseColorOptions";
3+
import { same } from "evt/tools/inDepth/same";
4+
import { assert } from "tsafe/assert";
5+
6+
console.log(`Running test ${__filename}`);
7+
8+
const rawCssCode = `
9+
:root {
10+
--name1-name2-111: #000000;
11+
--name1-name2-111-hover: #000001;
12+
--name1-name2-sun-111: #000002;
13+
--name1-name2-sun-111-hover: #000003;
14+
--name1-name2-111-222: #000004;
15+
--name1-name2-111-222-hover: #000005;
16+
--name1-name2-sun-111-222: #000006;
17+
--name1-name2-sun-111-222-hover: #000007;
18+
--name1-name2-111-moon-222: #000008;
19+
--name1-name2-111-moon-222-hover: #000009;
20+
--name1-name2-sun-111-moon-222: #00000a;
21+
--name1-name2-sun-111-moon-222-hover: #00000b;
22+
--grey-1000-50-hover: #00000c;
23+
--blue-france-sun-113-625: #00000d;
24+
--blue-france-sun-113-625-active: #00000e;
25+
--blue-france-main-525: #00000f;
26+
--purple-glycine-sun-319-moon-630-hover: #000010;
27+
28+
--title-spacing: 0 0 1.5rem;
29+
--display-spacing: 0 0 2rem;
30+
--background-default-grey: var(--grey-1000-50);
31+
--background-default-grey-hover: var(--grey-1000-50-hover);
32+
}
33+
:root:where([data-fr-theme="dark"]) {
34+
--name1-name2-111-222: #100000;
35+
--name1-name2-111-222-hover: #200000;
36+
--name1-name2-sun-111-222: #300000;
37+
--name1-name2-sun-111-222-hover: #400000;
38+
--name1-name2-111-moon-222: #500000;
39+
--name1-name2-111-moon-222-hover: #600000;
40+
--name1-name2-sun-111-moon-222: #700000;
41+
--name1-name2-sun-111-moon-222-hover: #800000;
42+
--grey-1000-50-hover: #900000;
43+
--blue-france-sun-113-625: #a00000;
44+
--blue-france-sun-113-625-active: #b00000;
45+
--purple-glycine-sun-319-moon-630-hover: #c00000;
46+
47+
--name1-name2-111: #000000;
48+
--name1-name2-111-hover: #000001;
49+
--name1-name2-sun-111: #000002;
50+
--name1-name2-sun-111-hover: #000003;
51+
--blue-france-main-525: #00000f;
52+
}
53+
`;
54+
55+
const got = parseColorOptions(rawCssCode);
56+
57+
const expected: ColorOption[] = [
58+
{
59+
"themePath": ["name1Name2", "_111", "default"],
60+
"color": "#000000"
61+
},
62+
{
63+
"themePath": ["name1Name2", "_111", "hover"],
64+
"color": "#000001"
65+
},
66+
{
67+
"themePath": ["name1Name2", "sun111", "default"],
68+
"color": "#000002"
69+
},
70+
{
71+
"themePath": ["name1Name2", "sun111", "hover"],
72+
"color": "#000003"
73+
},
74+
{
75+
"themePath": ["name1Name2", "_111_222", "default"],
76+
"color": { "light": "#000004", "dark": "#100000" }
77+
},
78+
{
79+
"themePath": ["name1Name2", "_111_222", "hover"],
80+
"color": { "light": "#000005", "dark": "#200000" }
81+
},
82+
{
83+
"themePath": ["name1Name2", "sun111_222", "default"],
84+
"color": { "light": "#000006", "dark": "#300000" }
85+
},
86+
{
87+
"themePath": ["name1Name2", "sun111_222", "hover"],
88+
"color": { "light": "#000007", "dark": "#400000" }
89+
},
90+
{
91+
"themePath": ["name1Name2", "_111moon222", "default"],
92+
"color": { "light": "#000008", "dark": "#500000" }
93+
},
94+
{
95+
"themePath": ["name1Name2", "_111moon222", "hover"],
96+
"color": { "light": "#000009", "dark": "#600000" }
97+
},
98+
{
99+
"themePath": ["name1Name2", "sun111moon222", "default"],
100+
"color": { "light": "#00000a", "dark": "#700000" }
101+
},
102+
{
103+
"themePath": ["name1Name2", "sun111moon222", "hover"],
104+
"color": { "light": "#00000b", "dark": "#800000" }
105+
},
106+
{
107+
"themePath": ["grey", "_1000_50", "hover"],
108+
"color": { "light": "#00000c", "dark": "#900000" }
109+
},
110+
{
111+
"themePath": ["blueFrance", "sun113_625", "default"],
112+
"color": { "light": "#00000d", "dark": "#a00000" }
113+
},
114+
{
115+
"themePath": ["blueFrance", "sun113_625", "active"],
116+
"color": { "light": "#00000e", "dark": "#b00000" }
117+
},
118+
{
119+
"themePath": ["blueFrance", "main525", "default"],
120+
"color": "#00000f"
121+
},
122+
{
123+
"themePath": ["purpleGlycine", "sun319moon630", "hover"],
124+
"color": { "light": "#000010", "dark": "#c00000" }
125+
}
126+
];
127+
128+
assert(same(expected, got));
129+
130+
console.log("PASS");

yarn.lock

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@
207207
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
208208
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
209209

210+
"@types/css@^0.0.33":
211+
version "0.0.33"
212+
resolved "https://registry.yarnpkg.com/@types/css/-/css-0.0.33.tgz#d0b49c4090c09c8e5dc01364560627e5ebb770f2"
213+
integrity sha512-qjeDgh86R0LIeEM588q65yatc8Yyo/VvSIYFqq8JOIHDolhGNX0rz7k/OuxqDpnpqlefoHj8X4Ai/6hT9IWtKQ==
214+
210215
"@types/json-schema@^7.0.7":
211216
version "7.0.11"
212217
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
@@ -419,6 +424,11 @@ astral-regex@^2.0.0:
419424
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
420425
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
421426

427+
atob@^2.1.2:
428+
version "2.1.2"
429+
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
430+
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
431+
422432
balanced-match@^1.0.0:
423433
version "1.0.2"
424434
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -565,6 +575,15 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
565575
shebang-command "^2.0.0"
566576
which "^2.0.1"
567577

578+
css@^3.0.0:
579+
version "3.0.0"
580+
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
581+
integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
582+
dependencies:
583+
inherits "^2.0.4"
584+
source-map "^0.6.1"
585+
source-map-resolve "^0.6.0"
586+
568587
csstype@^3.0.2:
569588
version "3.1.1"
570589
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
@@ -577,6 +596,11 @@ debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2:
577596
dependencies:
578597
ms "2.1.2"
579598

599+
decode-uri-component@^0.2.0:
600+
version "0.2.0"
601+
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
602+
integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==
603+
580604
deep-is@^0.1.3:
581605
version "0.1.4"
582606
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
@@ -980,7 +1004,7 @@ inflight@^1.0.4:
9801004
once "^1.3.0"
9811005
wrappy "1"
9821006

983-
inherits@2:
1007+
inherits@2, inherits@^2.0.4:
9841008
version "2.0.4"
9851009
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
9861010
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -1523,6 +1547,19 @@ source-map-js@^1.0.2:
15231547
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
15241548
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
15251549

1550+
source-map-resolve@^0.6.0:
1551+
version "0.6.0"
1552+
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
1553+
integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
1554+
dependencies:
1555+
atob "^2.1.2"
1556+
decode-uri-component "^0.2.0"
1557+
1558+
source-map@^0.6.1:
1559+
version "0.6.1"
1560+
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
1561+
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
1562+
15261563
sprintf-js@~1.0.2:
15271564
version "1.0.3"
15281565
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"

0 commit comments

Comments
 (0)