1 |
|
2 |
|
3 | var COMMENT_REGEX = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g;
|
4 |
|
5 | var NEWLINE_REGEX = /\n/g;
|
6 | var WHITESPACE_REGEX = /^\s*/;
|
7 |
|
8 |
|
9 | var PROPERTY_REGEX = /^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/;
|
10 | var COLON_REGEX = /^:\s*/;
|
11 | var VALUE_REGEX = /^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/;
|
12 | var SEMICOLON_REGEX = /^[;\s]*/;
|
13 |
|
14 |
|
15 | var TRIM_REGEX = /^\s+|\s+$/g;
|
16 |
|
17 |
|
18 | var NEWLINE = '\n';
|
19 | var FORWARD_SLASH = '/';
|
20 | var ASTERISK = '*';
|
21 | var EMPTY_STRING = '';
|
22 |
|
23 |
|
24 | var TYPE_COMMENT = 'comment';
|
25 | var TYPE_DECLARATION = 'declaration';
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | module.exports = function (style, options) {
|
35 | if (typeof style !== 'string') {
|
36 | throw new TypeError('First argument must be a string');
|
37 | }
|
38 |
|
39 | if (!style) return [];
|
40 |
|
41 | options = options || {};
|
42 |
|
43 | |
44 |
|
45 |
|
46 | var lineno = 1;
|
47 | var column = 1;
|
48 |
|
49 | |
50 |
|
51 |
|
52 |
|
53 |
|
54 | function updatePosition(str) {
|
55 | var lines = str.match(NEWLINE_REGEX);
|
56 | if (lines) lineno += lines.length;
|
57 | var i = str.lastIndexOf(NEWLINE);
|
58 | column = ~i ? str.length - i : column + str.length;
|
59 | }
|
60 |
|
61 | |
62 |
|
63 |
|
64 |
|
65 |
|
66 | function position() {
|
67 | var start = { line: lineno, column: column };
|
68 | return function (node) {
|
69 | node.position = new Position(start);
|
70 | whitespace();
|
71 | return node;
|
72 | };
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | function Position(start) {
|
84 | this.start = start;
|
85 | this.end = { line: lineno, column: column };
|
86 | this.source = options.source;
|
87 | }
|
88 |
|
89 | |
90 |
|
91 |
|
92 | Position.prototype.content = style;
|
93 |
|
94 | var errorsList = [];
|
95 |
|
96 | |
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 | function error(msg) {
|
103 | var err = new Error(
|
104 | options.source + ':' + lineno + ':' + column + ': ' + msg
|
105 | );
|
106 | err.reason = msg;
|
107 | err.filename = options.source;
|
108 | err.line = lineno;
|
109 | err.column = column;
|
110 | err.source = style;
|
111 |
|
112 | if (options.silent) {
|
113 | errorsList.push(err);
|
114 | } else {
|
115 | throw err;
|
116 | }
|
117 | }
|
118 |
|
119 | |
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 | function match(re) {
|
126 | var m = re.exec(style);
|
127 | if (!m) return;
|
128 | var str = m[0];
|
129 | updatePosition(str);
|
130 | style = style.slice(str.length);
|
131 | return m;
|
132 | }
|
133 |
|
134 | |
135 |
|
136 |
|
137 | function whitespace() {
|
138 | match(WHITESPACE_REGEX);
|
139 | }
|
140 |
|
141 | |
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | function comments(rules) {
|
148 | var c;
|
149 | rules = rules || [];
|
150 | while ((c = comment())) {
|
151 | if (c !== false) {
|
152 | rules.push(c);
|
153 | }
|
154 | }
|
155 | return rules;
|
156 | }
|
157 |
|
158 | |
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | function comment() {
|
165 | var pos = position();
|
166 | if (FORWARD_SLASH != style.charAt(0) || ASTERISK != style.charAt(1)) return;
|
167 |
|
168 | var i = 2;
|
169 | while (
|
170 | EMPTY_STRING != style.charAt(i) &&
|
171 | (ASTERISK != style.charAt(i) || FORWARD_SLASH != style.charAt(i + 1))
|
172 | ) {
|
173 | ++i;
|
174 | }
|
175 | i += 2;
|
176 |
|
177 | if (EMPTY_STRING === style.charAt(i - 1)) {
|
178 | return error('End of comment missing');
|
179 | }
|
180 |
|
181 | var str = style.slice(2, i - 2);
|
182 | column += 2;
|
183 | updatePosition(str);
|
184 | style = style.slice(i);
|
185 | column += 2;
|
186 |
|
187 | return pos({
|
188 | type: TYPE_COMMENT,
|
189 | comment: str
|
190 | });
|
191 | }
|
192 |
|
193 | |
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 | function declaration() {
|
200 | var pos = position();
|
201 |
|
202 |
|
203 | var prop = match(PROPERTY_REGEX);
|
204 | if (!prop) return;
|
205 | comment();
|
206 |
|
207 |
|
208 | if (!match(COLON_REGEX)) return error("property missing ':'");
|
209 |
|
210 |
|
211 | var val = match(VALUE_REGEX);
|
212 |
|
213 | var ret = pos({
|
214 | type: TYPE_DECLARATION,
|
215 | property: trim(prop[0].replace(COMMENT_REGEX, EMPTY_STRING)),
|
216 | value: val
|
217 | ? trim(val[0].replace(COMMENT_REGEX, EMPTY_STRING))
|
218 | : EMPTY_STRING
|
219 | });
|
220 |
|
221 |
|
222 | match(SEMICOLON_REGEX);
|
223 |
|
224 | return ret;
|
225 | }
|
226 |
|
227 | |
228 |
|
229 |
|
230 |
|
231 |
|
232 | function declarations() {
|
233 | var decls = [];
|
234 |
|
235 | comments(decls);
|
236 |
|
237 |
|
238 | var decl;
|
239 | while ((decl = declaration())) {
|
240 | if (decl !== false) {
|
241 | decls.push(decl);
|
242 | comments(decls);
|
243 | }
|
244 | }
|
245 |
|
246 | return decls;
|
247 | }
|
248 |
|
249 | whitespace();
|
250 | return declarations();
|
251 | };
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 | function trim(str) {
|
260 | return str ? str.replace(TRIM_REGEX, EMPTY_STRING) : EMPTY_STRING;
|
261 | }
|