@@ -3,6 +3,7 @@ interface Options {
3
3
modifierDelimiter : string ;
4
4
namespace : string | string [ ] ;
5
5
namespaceDelimiter : string ;
6
+ strict : boolean ;
6
7
}
7
8
8
9
type PartialOptions = Partial < Options > ;
@@ -12,13 +13,15 @@ const defaultOptions: Options = {
12
13
modifierDelimiter : "--" ,
13
14
namespace : "" ,
14
15
namespaceDelimiter : "-" ,
16
+ strict : true ,
15
17
} ;
16
18
17
19
export function setup ( {
18
20
elementDelimiter,
19
21
modifierDelimiter,
20
22
namespace,
21
23
namespaceDelimiter,
24
+ strict,
22
25
} : PartialOptions ) : void {
23
26
if ( elementDelimiter ) {
24
27
defaultOptions . elementDelimiter = elementDelimiter ;
@@ -32,6 +35,9 @@ export function setup({
32
35
if ( namespaceDelimiter ) {
33
36
defaultOptions . namespaceDelimiter = namespaceDelimiter ;
34
37
}
38
+ if ( typeof strict === "boolean" ) {
39
+ defaultOptions . strict = strict ;
40
+ }
35
41
}
36
42
37
43
type Modifiers =
@@ -42,8 +48,22 @@ type Modifiers =
42
48
43
49
type BemBlockFunction = ( elementOrModifiers ?: string | Modifiers , modifiers ?: Modifiers ) => string ;
44
50
51
+ const uniqueChars = ( list : string [ ] ) : string [ ] =>
52
+ list
53
+ . join ( "" )
54
+ . split ( "" )
55
+ . filter ( ( value , index , self ) => self . indexOf ( value ) === index ) ;
56
+
57
+ const includesChars = ( str : string , chars : string [ ] ) : boolean =>
58
+ chars . some ( char => str . includes ( char ) ) ;
59
+
60
+ const invalidMessage = ( subject : string , subjectValue : string , delimiters : string [ ] ) : string => {
61
+ const delims = `"${ delimiters . join ( '", "' ) } "` ;
62
+ return `The ${ subject } ("${ subjectValue } ") must not use the characters contained within the delimiters (${ delims } ).` ;
63
+ } ;
64
+
45
65
export default function bem ( block : string , options : PartialOptions = { } ) : BemBlockFunction {
46
- const { elementDelimiter, modifierDelimiter, namespace, namespaceDelimiter } = {
66
+ const { elementDelimiter, modifierDelimiter, namespace, namespaceDelimiter, strict } = {
47
67
...defaultOptions ,
48
68
...options ,
49
69
} ;
@@ -53,32 +73,46 @@ export default function bem(block: string, options: PartialOptions = {}): BemBlo
53
73
. filter ( Boolean ) // compact
54
74
. reduce ( ( joined , ns ) => joined + `${ ns } ${ namespaceDelimiter } ` , "" ) ;
55
75
56
- const baseBlock = `${ namespaces } ${ block } ` ;
76
+ const namespaceBlock = `${ namespaces } ${ block } ` ;
77
+
78
+ const delimiters = strict ? [ namespaceDelimiter , elementDelimiter , modifierDelimiter ] : [ ] ;
79
+ const delimiterChars = strict ? uniqueChars ( delimiters ) : [ ] ;
57
80
58
81
return function bemBlock ( elementOrModifiers , modifiers ) {
59
82
if ( ! elementOrModifiers ) {
60
- return baseBlock ;
83
+ return namespaceBlock ;
61
84
}
62
85
63
- const base =
64
- typeof elementOrModifiers === "string"
65
- ? `${ baseBlock } ${ elementDelimiter } ${ elementOrModifiers } `
66
- : baseBlock ;
86
+ const element = typeof elementOrModifiers === "string" ? elementOrModifiers : null ;
87
+
88
+ if ( strict && element && includesChars ( element , delimiterChars ) ) {
89
+ throw new Error ( invalidMessage ( "element" , element , delimiters ) ) ;
90
+ }
91
+
92
+ const base = element ? `${ namespaceBlock } ${ elementDelimiter } ${ element } ` : namespaceBlock ;
93
+
67
94
const mods = typeof elementOrModifiers === "string" ? modifiers : elementOrModifiers ;
68
95
69
96
if ( ! mods ) {
70
97
return base ;
71
98
}
72
99
73
- const reducer = ( result : string , modifier : string | null | undefined ) : string =>
74
- modifier ? `${ result } ${ base } ${ modifierDelimiter } ${ modifier } ` : result ;
100
+ const addModifiers = ( className : string , modifier : string | null | undefined ) : string => {
101
+ if ( modifier ) {
102
+ if ( strict && includesChars ( modifier , delimiterChars ) ) {
103
+ throw new Error ( invalidMessage ( "modifier" , modifier , delimiters ) ) ;
104
+ }
105
+ return `${ className } ${ base } ${ modifierDelimiter } ${ modifier } ` ;
106
+ }
107
+ return className ;
108
+ } ;
75
109
76
110
if ( Array . isArray ( mods ) ) {
77
- return mods . reduce ( reducer , base ) ;
111
+ return mods . reduce ( addModifiers , base ) ;
78
112
}
79
113
80
114
return Object . keys ( mods )
81
115
. filter ( mod => mods [ mod ] )
82
- . reduce ( reducer , base ) ;
116
+ . reduce ( addModifiers , base ) ;
83
117
} ;
84
118
}
0 commit comments