Skip to content

Commit db250e0

Browse files
committed
Implement typography and refactor (wip)
1 parent 50d191b commit db250e0

32 files changed

+1544
-518
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"@emotion/styled": "^11.10.4",
8181
"@mui/material": "^5.10.7",
8282
"@types/css": "^0.0.33",
83+
"@types/memoizee": "^0.4.8",
8384
"@types/node": "^18.7.18",
8485
"@types/react": "18.0.21",
8586
"@types/react-dom": "18.0.6",
@@ -91,6 +92,7 @@
9192
"evt": "^2.4.2",
9293
"husky": "^4.3.8",
9394
"lint-staged": "^11.0.0",
95+
"memoizee": "^0.4.15",
9496
"next": "12.3.1",
9597
"prettier": "^2.3.0",
9698
"react": "18.2.0",

src/bin/css_to_ts/breakpoints.ts

Lines changed: 114 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import css from "css";
1+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
22
import { assert } from "tsafe/assert";
3+
import type { Equals } from "tsafe";
34
import { objectKeys } from "tsafe/objectKeys";
45
import { exclude } from "tsafe/exclude";
6+
import { parseCss } from "./parseCss";
7+
import memoize from "memoizee";
58

69
export type BreakpointsValues = {
710
unit: string /* em, px ... */;
@@ -11,50 +14,127 @@ export type BreakpointsValues = {
1114
xl: number;
1215
};
1316

14-
export function parseBreakpointsValues(rawCssCode: string): BreakpointsValues {
15-
const parsedCss = css.parse(rawCssCode);
17+
export type MediaQueryByBreakpoint = Record<"sm" | "md" | "lg" | "xl", string>;
1618

17-
let prevUnit: string | undefined = undefined;
19+
assert<Equals<keyof MediaQueryByBreakpoint, Exclude<keyof BreakpointsValues, "unit">>>();
1820

19-
const values: number[] = [];
21+
export const parseBreakpointsValues = memoize(
22+
(
23+
rawCssCode: string
24+
): {
25+
breakpointsValues: BreakpointsValues;
26+
mediaQueryByBreakpoint: MediaQueryByBreakpoint;
27+
} => {
28+
const parsedCss = parseCss(rawCssCode);
2029

21-
parsedCss.stylesheet?.rules
22-
.filter(rule => rule.type === "media")
23-
.forEach(rule => {
24-
const match = (rule as { media: string }).media.match(
25-
/^\(min-width:\s*([0-9]+)([^)]+)\)$/
26-
);
30+
let prevUnit: string | undefined = undefined;
2731

28-
if (match === null) {
29-
return;
30-
}
32+
const values: number[] = [];
3133

32-
const [, valueStr, unit] = match;
34+
const mediaQueryByValue = new Map<number, string>();
3335

34-
if (prevUnit === undefined) {
35-
prevUnit = unit;
36-
} else {
37-
assert(prevUnit === unit);
38-
}
36+
parsedCss.stylesheet?.rules
37+
.filter(rule => rule.type === "media")
38+
.forEach(rule => {
39+
const mediaQuery: string = (rule as any).media;
3940

40-
values.push(parseFloat(valueStr));
41-
});
41+
const match = mediaQuery.match(/^\(min-width:\s*([0-9]+)([^)]+)\)$/);
4242

43-
assert(values.length === 4);
44-
assert(prevUnit !== undefined);
43+
if (match === null) {
44+
return;
45+
}
4546

46-
const [sm, md, lg, xl] = values.sort();
47+
const [, valueStr, unit] = match;
4748

48-
return {
49-
"unit": prevUnit,
50-
sm,
51-
md,
52-
lg,
53-
xl
54-
};
55-
}
49+
if (prevUnit === undefined) {
50+
prevUnit = unit;
51+
} else {
52+
assert(prevUnit === unit);
53+
}
54+
55+
const value = parseFloat(valueStr);
56+
57+
values.push(value);
58+
59+
mediaQueryByValue.set(value, mediaQuery);
60+
});
61+
62+
assert(values.length === 4);
63+
assert(prevUnit !== undefined);
64+
65+
const [sm, md, lg, xl] = values.sort();
66+
67+
const breakpointsValues: BreakpointsValues = {
68+
"unit": prevUnit,
69+
sm,
70+
md,
71+
lg,
72+
xl
73+
};
74+
75+
const mediaQueryByBreakpoint = Object.fromEntries(
76+
objectKeys(breakpointsValues)
77+
.map(breakPoint =>
78+
breakPoint === "unit"
79+
? undefined
80+
: ([breakPoint, breakpointsValues[breakPoint]] as const)
81+
)
82+
.filter(exclude(undefined))
83+
.map(([breakPoint, value]) => [
84+
breakPoint,
85+
(() => {
86+
const mediaQuery = mediaQueryByValue.get(value);
87+
assert(mediaQuery !== undefined);
88+
return mediaQuery;
89+
})()
90+
])
91+
) as MediaQueryByBreakpoint;
92+
93+
return { breakpointsValues, mediaQueryByBreakpoint };
94+
}
95+
);
96+
97+
export type RulesByBreakpoint = Record<
98+
"root" | keyof MediaQueryByBreakpoint,
99+
{
100+
type: "rule";
101+
selectors: string[];
102+
declarations: Record<string, unknown>[];
103+
}[]
104+
>;
105+
106+
export const getRulesByBreakpoint = memoize((rawCssCode: string): RulesByBreakpoint => {
107+
const parsedCss = parseCss(rawCssCode);
108+
109+
const { mediaQueryByBreakpoint } = parseBreakpointsValues(rawCssCode);
110+
111+
const rulesByBreakpoint: RulesByBreakpoint = {} as any;
112+
113+
rulesByBreakpoint.root = (parsedCss.stylesheet!.rules as any[]).filter(
114+
({ type }) => type === "rule"
115+
);
116+
117+
(parsedCss.stylesheet!.rules as any[])
118+
.filter(({ type }) => type === "media")
119+
.filter(({ media }) => Object.values(mediaQueryByBreakpoint).includes(media))
120+
.forEach(
121+
({ media, rules }: any) =>
122+
(rulesByBreakpoint[
123+
objectKeys(mediaQueryByBreakpoint)
124+
.map(breakpoint => ({
125+
breakpoint,
126+
"mediaQuery": mediaQueryByBreakpoint[breakpoint]
127+
}))
128+
.find(({ mediaQuery }) => mediaQuery === media)!.breakpoint
129+
] = rules.filter(({ type }: any) => type === "rule"))
130+
);
131+
132+
return rulesByBreakpoint;
133+
});
134+
135+
export function generateBreakpointsTsCode(rawCssCode: string): string {
136+
const { breakpointsValues } = parseBreakpointsValues(rawCssCode);
56137

57-
export function generateBreakpointsTsCode(breakpointsValues: BreakpointsValues): string {
58138
const sortedKeys = objectKeys(breakpointsValues)
59139
.filter(exclude("unit"))
60140
.sort((a, b) => breakpointsValues[a] - breakpointsValues[b]);

src/bin/css_to_ts/colorDecisions.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { State } from "./colorOptions";
2-
import { states } from "./colorOptions";
2+
import { states, parseColorOptions } from "./colorOptions";
33
import { id } from "tsafe/id";
44
import { assert } from "tsafe/assert";
55
import { capitalize } from "tsafe/capitalize";
6-
import css from "css";
6+
import { parseCss } from "./parseCss";
77
import { exclude } from "tsafe/exclude";
88
import { is } from "tsafe/is";
99
import { parseColorOptionName, getThemePath as getColorOptionThemePath } from "./colorOptions";
@@ -26,14 +26,15 @@ export type ParsedColorDecisionName = {
2626
state: State | undefined;
2727
};
2828

29-
export function createParseColorDecisionName(params: {
29+
export function createParseColorDecisionName(rawCssCode: string) {
3030
/** Like [ "grey", "blueFrance", ... ]
3131
* All the the color name in camel case that we deduce from Options
3232
* it help parsing without making assumption on what is a valid Usage
3333
*/
34-
colorNames: string[];
35-
}) {
36-
const { colorNames } = params;
34+
const colorNames = parseColorOptions(rawCssCode)
35+
.map(({ colorOptionName }) => colorOptionName)
36+
.map(parseColorOptionName)
37+
.map(o => o.colorName);
3738

3839
function parseColorDecisionName(colorDecisionName: `--${string}`): ParsedColorDecisionName {
3940
/*
@@ -159,18 +160,14 @@ export type ColorDecision = {
159160
optionThemePath: string[];
160161
};
161162

162-
export function parseColorDecision(params: {
163-
/** ["--grey-1000-50-hover", "--grey-1000-50", ... ] */
164-
colorOptionNames: `--${string}`[];
165-
rawCssCode: string;
166-
}): ColorDecision[] {
167-
const { colorOptionNames, rawCssCode } = params;
163+
export function parseColorDecision(rawCssCode: string): ColorDecision[] {
164+
const { parseColorDecisionName } = createParseColorDecisionName(rawCssCode);
168165

169-
const colorNames = colorOptionNames.map(parseColorOptionName).map(o => o.colorName);
166+
const parsedCss = parseCss(rawCssCode);
170167

171-
const { parseColorDecisionName } = createParseColorDecisionName({ colorNames });
172-
173-
const parsedCss = css.parse(rawCssCode);
168+
const colorOptionNames = parseColorOptions(rawCssCode).map(
169+
({ colorOptionName }) => colorOptionName
170+
);
174171

175172
const { declarations } = (() => {
176173
const node = parsedCss.stylesheet?.rules.find(
@@ -210,12 +207,12 @@ export function parseColorDecision(params: {
210207
.filter(exclude(undefined));
211208
}
212209

213-
export function generateGetColorDecisionsTsCode(colorDecisions: ColorDecision[]): string {
210+
export function generateGetColorDecisionsTsCode(rawCssCode: string): string {
214211
const obj: any = {};
215212

216213
const keyValues: Record<string, string> = {};
217214

218-
colorDecisions.forEach(colorDecision => {
215+
parseColorDecision(rawCssCode).forEach(colorDecision => {
219216
const value = (() => {
220217
const hash = crypto
221218
.createHash("sha256")

0 commit comments

Comments
 (0)