Thought I would write a CSS Parser in TypeScript Types. Like all these exercises there is no practical application, but I thought they are fun.
Maybe there is something to learn, cause I did.
type WS = ' ' | '\n' | '\t';
type Trim<T> = T extends (`${WS}${infer V}` | `${infer V}${WS}`) ? Trim<V> : T;
type AddKey<O extends {}, Key , Value, Z = Trim<Key>> = Trim<Z> extends '' ? Value : Z extends PropertyKey ? {[k in Z]:Value} & (Z extends keyof O ? Exclude<O, Z> : O) : Value;
type _ParseComment<T> = Trim<T> extends `/*${infer Comment}*/${infer Rest}` ? [Comment, Rest] : ['', T]
type _ParseSelector<T> = T extends `${infer Selector}{${infer Rest}` ? Trim<Selector> extends '' ? ['' , T] : [Trim<Selector>, Trim<Rest>] : ['', T];
type _ParseMediaQuery<T, Ret extends {} = {}> = T extends `${infer Query}{${infer Content}` ?
_ParseCSS<Content> extends [infer CSSI, infer Rest] ?
Trim<Rest> extends `}${infer CRest}` ? [ AddKey<Ret, Query, CSSI>, CRest ] :
[Ret, ''] : [Ret, ''] : [Ret, ''];
type _ParseProperties<T, Ret extends {} = {}> =
_ParseComment<T> extends [string, infer PR] ?
Trim<PR> extends '' ? [Ret, ''] :
Trim<PR> extends `}${infer Rest}` ? [Ret, Rest] :
Trim<PR> extends `${infer Key}:${infer Val};${infer Rest}` ? _ParseProperties<Rest, AddKey<Ret, Key, Trim<Val>>> :
Trim<PR> extends `${infer Key}:${infer Val}}${infer Rest}` ? [ AddKey<Ret, Key, Trim<Val>>, Rest] :
[Ret, PR]: [Ret, T];
type _ParseCSS<S, Ret extends {} = {}> =
Trim<S> extends '' ? [Ret, ''] :
Trim<S> extends `}${string}` ? [ Ret, S ] :
_ParseComment<S> extends [string, infer CRest] ?
_ParseSelector<CRest> extends [infer Selector, infer Rest] ?
_ParseProperties<Rest> extends [infer Props, infer PRest] ? _ParseCSS<PRest, AddKey<Ret, Selector, Props>> : never : never : never ;
type ParseCSS<S, Ret extends {} = {}> =
Trim<S> extends '' ? Ret :
S extends `${infer Before}@media${infer Media}` ?
_ParseCSS<Before, Ret> extends [infer O, string] ?
_ParseMediaQuery<Media, O> extends [infer R, infer Con] ? ParseCSS<Con, R> : never : ParseCSS<S, Ret> : _ParseCSS<S> extends [infer R, string] ? R : never;
type MQ = _ParseMediaQuery<`only screen and (max-width: 600px) { body { background-color: lightblue; } } `>;
type PC1 = ParseCSS<'.stuff { color:white }'>;
type PC1S = PC1['.stuff']['color'];
type PC2 = ParseCSS<'/* what */ .stuff { color:white; }'>;
type PC3 = ParseCSS<'/* what */ .stuff { color:white; background:red }'>;
type PC4 = ParseCSS<' .stuff { color:white; /* what */background:red }'>;
type PC5 = ParseCSS<`.stuff { color:white;background:red }
.selector2 {
font:sans-serif
}`>;
type PC51 = PC5['.selector2']['font'];
type _PC = _ParseCSS<`.stuff { color:white;background:red }`>;
type TQ1 = ParseCSS<`.stuff {
background-color: lightblue;
}`>;
type MQ1 = ParseCSS<`@media only screen and (max-width: 600px) { body { background-color: lightblue; } }`>;
type MQ2 = ParseCSS<`@media only screen and (max-width: 600px) { body { background-color: lightblue; } }`>;
type MQ3 = ParseCSS<`body { color: pink } @media only screen and (max-width: 600px) { body { background-color: lightblue; } } @media only screen { body { background-color: lightblue; } }`>;
Thanks for sharing @jspears. I do think there's a practical application for this. Would you be able to offer a license?