-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCompiler.js
138 lines (118 loc) · 3.31 KB
/
Compiler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// @flow
import React from 'react';
// types
import type {Element, Node} from 'react';
export type TConfig = {
elementDelimiter: string,
modifierDelimiter: string,
isStrict: boolean
};
class Compiler {
config: TConfig;
static defaultConfig: TConfig = {
elementDelimiter: '_',
modifierDelimiter: '--',
isStrict: true
};
constructor(config: TConfig = {}) {
this.config = {
...Compiler.defaultConfig,
...config
};
}
setDelimeters = (elementDelimiter: string, modifierDelimiter: string) => {
this.config = {
...this.config,
elementDelimiter,
modifierDelimiter
};
};
buildClassName(parentClass: string, config: string | Object) {
const {elementDelimiter, modifierDelimiter} = this.config;
let element = null;
let modifiers = null;
let newParentClass = parentClass;
let modifiersClasses = '';
if (typeof config == 'string') {
if (this.config.isStrict) {
// pass if strict mode is enabled
return {
parentClass: newParentClass,
className: config
};
} else {
element = config;
}
} else {
element = config.element;
modifiers = config.modifiers;
}
if (element) {
if (newParentClass) {
newParentClass += `${elementDelimiter}${element}`;
} else {
newParentClass = element;
}
}
if (modifiers) {
modifiersClasses = Object.entries(modifiers)
.reduce((res: string[], [key, value]: [string, mixed]) => {
if (value) {
return [...res, `${newParentClass}${modifierDelimiter}${key}`];
}
return res;
}, [])
.join(' ');
}
return {
parentClass: newParentClass,
className: [
config.className || '',
element ? newParentClass : undefined,
modifiersClasses
]
.filter((val: any) => Boolean(val))
.join(' ')
};
}
traverseChild(child: Element<any>, parentClass: string, rootChildren: Node) {
if (React.isValidElement(child)) {
let {className} = child.props;
let usedParentClass = parentClass;
const props = {};
if (className) {
// generate new parentClass to pass it futher and new className
const generatedClassName = this.buildClassName(parentClass, className);
props.className = generatedClassName.className;
usedParentClass = generatedClassName.parentClass;
}
return React.cloneElement(
child,
props,
child.props.children === rootChildren
? child.props.children
: Array.isArray(child.props.children)
? React.Children.map(child.props.children, (child: Element<any>) => {
return this.traverseChild(child, usedParentClass, rootChildren);
})
: this.traverseChild(
child.props.children,
usedParentClass,
rootChildren
)
);
} else {
return child;
}
}
traverse = (root: Element<any>, rootChildren: Node) => {
return this.traverseChild(root, '', rootChildren);
};
setStrict(isStrict: boolean) {
this.config.isStrict = isStrict;
}
static setDefaults(config: TConfig) {
Compiler.defaultConfig = {...Compiler.defaultConfig, ...config};
}
}
export default Compiler;