Passed
Push — main ( 1fc2e6...329f2f )
by Andrii
02:38
created

__sandbox__/bem.test.ts   A

Complexity

Total Complexity 9
Complexity/F 4.5

Size

Lines of Code 144
Function Count 2

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 9
eloc 53
mnd 7
bc 7
fnc 2
dl 0
loc 144
rs 10
bpm 3.5
cpm 4.5
noi 0
c 0
b 0
f 0
1
import { CssModule } from "../src/types"
2
3
export {}
4
5
6
it("tree2classes", () => {
7
8
  type BemTree = {
9
    [block: string]: {
10
        // ""?: {[blockMod: string]: true}
11
        [element: string]: boolean | {[elMod: string]: boolean} 
12
    }
13
  }
14
  
15
  type El2Classes<DMod extends string, S extends Record<string, boolean|Record<string, boolean>>> = {[K in string & keyof S]:
16
    K | (S[K] extends Record<string, boolean> ? `${K}${DMod}${string & keyof S[K]}` : never)
17
  }[string & keyof S]
18
  
19
  type BemTree2Classes<DEl extends string, DMod extends string, S extends BemTree> = {[K in string & keyof S]:
20
    K | `${K}${DEl}${El2Classes<DMod,S[K]>}`
21
  }[string & keyof S]
22
 
23
 
24
  const bem2 = {
25
    "Button": {
26
       "Container": true,
27
       "Icon": {
28
           "small": true,
29
           "big": true
30
       }
31
   },
32
   "Form": {
33
       "Container": true,
34
       "Button": true
35
   }   
36
  }
37
  
38
  const suite: Record<BemTree2Classes<"__", "--", typeof bem2>, boolean> = {
39
    Button: true,
40
    Button__Icon: true,
41
    "Button__Icon--big": true,
42
    "Button__Icon--small": true,
43
    Button__Container: true,
44
    Form: true,
45
    Form__Button: true,
46
    Form__Container: true
47
   }
48
49
  expect(suite).toBeInstanceOf(Object)     
50
})
51
52
type ClassNames = {
53
  "App": string
54
  "App--dark": string
55
  "App__Container": string
56
  "App__Container--loading": string
57
  "Btn": string
58
  "Btn--disabled": string
59
  "Btn__Icon": string
60
  "Btn__Icon--big": string
61
}
62
63
it("take blocks", () => {
64
  // type digits = '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
65
  type smallLetters = 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z'
66
  type bigLetters = 'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z'
67
  type letters = smallLetters | bigLetters
68
69
  type FirstWord<chars extends string, word extends string, stacked extends string = ""> = word extends `${chars}${infer K}`
70
  ? word extends `${infer F}${K}`
71
  ? FirstWord<chars, K, `${stacked}${F}`>
72
  : never
73
  : stacked
74
75
  type Blocks<T> = {[K in keyof T]: FirstWord<letters, K & string>}[keyof T]
76
77
  const suite: Record<Blocks<ClassNames>, Blocks<ClassNames>> = {
78
    App: "App",
79
    Btn: "Btn"
80
  }
81
82
  expect(suite).toBeInstanceOf(Object)
83
})
84
85
it("upon delimiter", () => {
86
  type Strip<Str extends string, Delimiter extends string> = Str extends `${infer Lead}${Delimiter}${string}` ? Lead : Str
87
  type StripFromObj<T, Delimiter extends string> = {[K in string & keyof T]: Strip<K,Delimiter>}[keyof T]
88
  type GetMods<T, B extends string, E extends string|undefined> = {
89
    [K in string & keyof T]: E extends string 
90
    ? K extends `${B}__${E}--${infer M}` ? M : never
91
    : K extends `${B}--${infer M}` ? M : never
92
  }[keyof T]
93
94
  type Bemer<ClassNames extends CssModule> = (
95
    <
96
      BE extends StripFromObj<ClassNames, "--">,
97
      Block extends Strip<BE, "__">,
98
      Element extends undefined|(BE extends `${Block}__${infer Element}` ? Element : undefined) = undefined,
99
      Modifier extends undefined|GetMods<ClassNames, Block, Element> = undefined
100
    >(
101
      block: Block,
102
      element?: Element,
103
      modifier?: Modifier
104
    ) => `${
105
      Block
106
    }${
107
      Element extends string ? ` ${Block}__${Element}` : ""
108
    }${
109
      Modifier extends string 
110
      ? ` ${Block}${
111
        Element extends string ? `__${Element}` : ""
112
      }--${Modifier}`
113
      : ""
114
    }`
115
  )
116
117
118
  function beming<ClassNames extends CssModule>() {
119
    const host: Bemer<ClassNames> = ((block, element?, modifier?) => {
120
      const elemened = element ? `${block}__${element}` : ""
121
      const moded = modifier ? ` ${element ? elemened : block}--${modifier}` : ""
122
  
123
      return `${
124
        block
125
      }${
126
        element ! ? " " : ""
127
      }${
128
        elemened
129
      }${
130
        moded
131
      }`
132
    }) as Bemer<ClassNames>
133
134
    return host
135
  }
136
    
137
138
  const bemer = beming<ClassNames>()
139
  , $return = bemer("Btn", "Icon", "big")
140
  , typed: typeof $return = "Btn Btn__Icon Btn__Icon--big"
141
142
  expect($return).toBe(typed)
143
})
144
145