Skip to content

Instantly share code, notes, and snippets.

@jspears
Created February 14, 2022 20:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jspears/11ec281de9db699b91efdcc67503c55b to your computer and use it in GitHub Desktop.
Save jspears/11ec281de9db699b91efdcc67503c55b to your computer and use it in GitHub Desktop.
TypeScript/Types Parsing XML

Have you ever wanted a truly terrible XML Parser. Not only incomplete but slow and difficult to understand. Well here you go.

type Trim<T> = T extends (` ${infer V}`  | `${infer V} ` | `${infer V}\n`) ? Trim<V>: T;

type ParseAttrs<T extends string, R extends {} = {}> = 
    Trim<T> extends '' ? R  : 
    T extends `${infer Before} ${infer Key}="${infer Value}"${infer Rest}`? 
         ParseAttrs<Trim<Before>, ParseAttrs<Trim<Rest>, R & { [k in Key]:Value}>> : 
    T extends  `${infer Before} ${infer Key}='${infer Value}'${infer Rest}` ?
        ParseAttrs<Trim<Rest>, ParseAttrs<Trim<Before>, R & { [k in Key]:Value}>> :
    
    T extends `${infer Key}="${infer Value}"${infer Rest}`?
        ParseAttrs<Trim<Rest>, R & { [k in Key]:Value}> :
    T extends  `${infer Key}='${infer Value}'${infer Rest}` ?
        ParseAttrs<Trim<Rest>, R & { [k in Key]:Value}> :
    T extends `${infer Key} ${infer Rest}` ?  ParseAttrs<Trim<Rest>, R & { [k in Key]:true}> :
    R & { [k in T]:true }

type TextNode = 'Text';

type Parse<T, R extends [] = []> = T extends `<${infer Tag}>${infer Rest}` ? Rest extends `${infer Content}</${Tag}>${infer Right}` ? [[Tag, Parse<Content>], Parse<Right>] :
T extends `<${infer Tag} ${infer Attr}>${infer Rest}` ? Rest extends `${infer Content}</${Tag}>${infer Right}` ? [[Tag, ParseAttrs<Attr>, Parse<Content>], Parse<Right>] :
T extends `<${infer Tag} ${infer Attr}/>${infer Rest}` ? [[Tag, ParseAttrs<Attr>], Parse<Rest>] :
T extends '' ? R :
[TextNode, T]:T extends '' ? R : [TextNode, T] : T extends '' ? R : [TextNode, T] ;


type P1 = Parse<`<div class='stuff'>hello</div>`>;

type P2 = Parse<`<div><span>he</span><why></why></div>`>;


type X0 = Parse<`<div/><span/>`>;        
type X1 = Parse<`<div class='super' value="1"/>`>      ;  
type X2 = Parse<`<div class='super' value="1"></div>`>;        
 
type X3 = Parse<`<div class='super' value="1"><span>hello</span></div>`>;  

 type A1 = ParseAttrs<`hello`>;
 type A2 = ParseAttrs<`hello="world"`>;
 type A3 = ParseAttrs<`hello="world" foo='bar'`>;
 type A4 = ParseAttrs<`hello="world"  foo`>;
 type A5 = ParseAttrs<`hello foo="bar"`>;
 type A6 = ParseAttrs<`hello="world" foo='bar' foo`>;

playground

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment