@@ -26,94 +26,120 @@ SOFTWARE.
26
26
27
27
exports . JSON = function ( cssText ) {
28
28
const cleanedCSS = cssText . replace ( / \/ \* [ \s \S ] * ?\* \/ / g, '' ) ;
29
-
30
29
let index = 0 ;
30
+ const length = cleanedCSS . length ;
31
31
32
32
function skipWhitespace ( ) {
33
- while ( / \s / . test ( cleanedCSS [ index ] ) ) index ++ ;
33
+ while ( index < length && / \s / . test ( cleanedCSS [ index ] ) ) index ++ ;
34
34
}
35
35
36
- function parseRules ( ) {
37
- const rules = [ ] ;
36
+ function peek ( ) {
37
+ return cleanedCSS [ index ] ;
38
+ }
38
39
39
- while ( index < cleanedCSS . length ) {
40
- skipWhitespace ( ) ;
40
+ function consume ( ) {
41
+ return cleanedCSS [ index ++ ] ;
42
+ }
41
43
42
- if ( cleanedCSS [ index ] === '}' ) {
43
- index ++ ;
44
- break ;
45
- }
44
+ const stack = [ ] ;
45
+ const rules = [ ] ;
46
46
47
- if ( cleanedCSS [ index ] === '@' ) {
48
- rules . push ( parseAtRule ( ) ) ;
49
- } else {
50
- rules . push ( parseSelectorOrNested ( ) ) ;
51
- }
47
+ while ( index < length ) {
48
+ skipWhitespace ( ) ;
49
+
50
+ if ( index >= length ) break ;
51
+
52
+ const char = peek ( ) ;
53
+
54
+ if ( char === '}' ) {
55
+ index ++ ;
56
+ if ( stack . length > 0 ) stack . pop ( ) ;
57
+ continue ;
52
58
}
53
59
54
- return rules ;
60
+ if ( char === '@' ) {
61
+ rules . push ( parseAtRule ( ) ) ;
62
+ } else {
63
+ rules . push ( parseSelectorOrNested ( ) ) ;
64
+ }
55
65
}
56
66
57
67
function parseAtRule ( ) {
58
68
let start = index ;
59
- while ( index < cleanedCSS . length && cleanedCSS [ index ] !== '{' && cleanedCSS [ index ] !== ';' ) {
69
+ while ( index < length && cleanedCSS [ index ] !== '{' && cleanedCSS [ index ] !== ';' ) {
60
70
index ++ ;
61
71
}
62
72
const atRuleHeader = cleanedCSS . slice ( start , index ) . trim ( ) ;
63
73
64
74
if ( cleanedCSS [ index ] === '{' ) {
65
75
index ++ ;
66
- const nestedRules = parseRules ( ) ;
67
- return {
68
- type : 'at-rule' ,
69
- name : atRuleHeader ,
70
- rules : nestedRules
71
- } ;
76
+ const nestedRules = [ ] ;
77
+ stack . push ( { type : 'at-rule' , name : atRuleHeader , rules : nestedRules } ) ;
78
+
79
+ while ( index < length ) {
80
+ skipWhitespace ( ) ;
81
+ if ( peek ( ) === '}' ) {
82
+ index ++ ;
83
+ break ;
84
+ }
85
+ nestedRules . push ( parseSelectorOrNested ( ) ) ;
86
+ }
87
+
88
+ return { type : 'at-rule' , name : atRuleHeader , rules : nestedRules } ;
89
+
72
90
} else if ( cleanedCSS [ index ] === ';' ) {
73
91
index ++ ;
74
- return {
75
- type : 'at-rule' ,
76
- name : atRuleHeader ,
77
- rules : [ ]
78
- } ;
92
+ return { type : 'at-rule' , name : atRuleHeader , rules : [ ] } ;
79
93
}
94
+
80
95
return { type : 'at-rule' , name : atRuleHeader , rules : [ ] } ;
81
96
}
82
97
83
98
function parseSelectorOrNested ( ) {
84
99
let start = index ;
85
- while ( index < cleanedCSS . length && cleanedCSS [ index ] !== '{' && cleanedCSS [ index ] !== '}' ) {
100
+ while (
101
+ index < length &&
102
+ cleanedCSS [ index ] !== '{' &&
103
+ cleanedCSS [ index ] !== '}'
104
+ ) {
86
105
index ++ ;
87
106
}
107
+
88
108
const selectorText = cleanedCSS . slice ( start , index ) . trim ( ) ;
89
109
90
110
if ( cleanedCSS [ index ] === '{' ) {
91
111
index ++ ;
92
112
93
113
skipWhitespace ( ) ;
94
-
114
+
95
115
if ( / ^ [ . # a - z A - Z 0 - 9 \- \s , : ] + $ / . test ( selectorText ) ) {
96
116
const selectors = selectorText . split ( ',' ) . map ( s => s . trim ( ) ) ;
97
-
98
117
const properties = { } ;
99
- while ( true ) {
118
+ const nestedRules = [ ] ;
119
+
120
+ while ( index < length ) {
100
121
skipWhitespace ( ) ;
101
- if ( cleanedCSS [ index ] === '}' ) {
122
+ if ( peek ( ) === '}' ) {
102
123
index ++ ;
103
124
break ;
104
125
}
105
- if ( cleanedCSS [ index ] === '@' ) {
106
- const atRule = parseAtRule ( ) ;
107
- return { type : 'rule' , selectors, properties, nestedRules : [ atRule ] } ;
108
- }
109
126
127
+ if ( peek ( ) === '@' ) {
128
+ nestedRules . push ( parseAtRule ( ) ) ;
129
+ continue ;
130
+ }
131
+
110
132
let propStart = index ;
111
- while ( index < cleanedCSS . length && cleanedCSS [ index ] !== ';' && cleanedCSS [ index ] !== '}' ) {
133
+ while (
134
+ index < length &&
135
+ cleanedCSS [ index ] !== ';' &&
136
+ cleanedCSS [ index ] !== '}'
137
+ ) {
112
138
index ++ ;
113
139
}
114
140
115
141
const line = cleanedCSS . slice ( propStart , index ) . trim ( ) ;
116
-
142
+
117
143
if ( ! line ) break ;
118
144
119
145
if ( line . includes ( ':' ) ) {
@@ -123,12 +149,10 @@ exports.JSON = function(cssText) {
123
149
} else if ( cleanedCSS [ index ] === '}' ) {
124
150
index ++ ;
125
151
break ;
126
- } else if ( / ^ [ . # a - z A - Z 0 - 9 \- \s , : ] + $ / . test ( line ) ) {
127
- break ;
128
152
}
129
153
}
130
-
131
- return { type : 'rule' , selectors, properties } ;
154
+
155
+ return { type : 'rule' , selectors, properties, nestedRules } ;
132
156
133
157
} else {
134
158
return null ;
@@ -139,6 +163,5 @@ exports.JSON = function(cssText) {
139
163
}
140
164
}
141
165
142
- const ast = parseRules ( ) ;
143
- return ast ;
166
+ return rules ;
144
167
}
0 commit comments