UNPKG

13.9 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8
9var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
10
11var _comment = require('postcss/lib/comment');
12
13var _comment2 = _interopRequireDefault(_comment);
14
15var _import2 = require('./import');
16
17var _import3 = _interopRequireDefault(_import2);
18
19var _parser = require('postcss/lib/parser');
20
21var _parser2 = _interopRequireDefault(_parser);
22
23var _rule = require('./rule');
24
25var _rule2 = _interopRequireDefault(_rule);
26
27var _root = require('./root');
28
29var _root2 = _interopRequireDefault(_root);
30
31var _findExtendRule = require('./find-extend-rule');
32
33var _findExtendRule2 = _interopRequireDefault(_findExtendRule);
34
35var _isMixinToken = require('./is-mixin-token');
36
37var _isMixinToken2 = _interopRequireDefault(_isMixinToken);
38
39var _lessTokenize = require('./less-tokenize');
40
41var _lessTokenize2 = _interopRequireDefault(_lessTokenize);
42
43function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
44
45function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
46
47function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
48
49function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
50
51var blockCommentEndPattern = /\*\/$/;
52
53var LessParser = function (_Parser) {
54 _inherits(LessParser, _Parser);
55
56 function LessParser(input) {
57 _classCallCheck(this, LessParser);
58
59 var _this = _possibleConstructorReturn(this, (LessParser.__proto__ || Object.getPrototypeOf(LessParser)).call(this, input));
60
61 _this.root = new _root2.default();
62 _this.current = _this.root;
63 _this.root.source = { input: input, start: { line: 1, column: 1 } };
64 return _this;
65 }
66
67 _createClass(LessParser, [{
68 key: 'atrule',
69 value: function atrule(token) {
70 if (token[1] === '@import') {
71 this.import(token);
72 } else {
73 _get(LessParser.prototype.__proto__ || Object.getPrototypeOf(LessParser.prototype), 'atrule', this).call(this, token);
74 }
75 }
76 }, {
77 key: 'comment',
78 value: function comment(token) {
79 var node = new _comment2.default();
80 var content = token[1];
81 var text = content.slice(2).replace(blockCommentEndPattern, '');
82
83 this.init(node, token[2], token[3]);
84 node.source.end = {
85 line: token[4],
86 column: token[5]
87 };
88
89 node.raws.content = content;
90 node.raws.begin = content[0] + content[1];
91 node.inline = token[6] === 'inline';
92 node.block = !node.inline;
93
94 if (/^\s*$/.test(text)) {
95 node.text = '';
96 node.raws.left = text;
97 node.raws.right = '';
98 } else {
99 var match = text.match(/^(\s*)([^]*[^\s])(\s*)$/);
100
101 node.text = match[2];
102
103 // Add extra spaces to generate a comment in a common style /*[space][text][space]*/
104 node.raws.left = match[1] || ' ';
105 node.raws.right = match[3] || ' ';
106 }
107 }
108
109 /**
110 * @description Create a Declaration
111 * @param options {{start: number}}
112 */
113
114 }, {
115 key: 'createDeclaration',
116 value: function createDeclaration(options) {
117 this.decl(this.tokens.slice(options.start, this.pos + 1));
118 }
119
120 /**
121 * @description Create a Rule node
122 * @param options {{start: number, params: Array}}
123 */
124
125 }, {
126 key: 'createRule',
127 value: function createRule(options) {
128
129 var semi = this.tokens[this.pos][0] === ';';
130 var end = this.pos + (options.empty && semi ? 2 : 1);
131 var tokens = this.tokens.slice(options.start, end);
132 var node = this.rule(tokens);
133
134 /**
135 * By default in PostCSS `Rule.params` is `undefined`.
136 * To preserve compability with PostCSS:
137 * - Don't set empty params for a Rule.
138 * - Set params for a Rule only if it can be a mixin or &:extend rule.
139 */
140 if (options.params[0] && (options.mixin || options.extend)) {
141 this.raw(node, 'params', options.params);
142 }
143
144 if (options.empty) {
145 // if it's an empty mixin or extend, it must have a semicolon
146 // (that's the only way we get to this point)
147 if (semi) {
148 node.raws.semicolon = this.semicolon = true;
149 node.selector = node.selector.replace(/;$/, '');
150 }
151
152 if (options.extend) {
153 node.extend = true;
154 }
155
156 if (options.mixin) {
157 node.mixin = true;
158 }
159
160 /**
161 * @description Mark mixin without declarations.
162 * @type {boolean}
163 */
164 node.empty = true;
165
166 // eslint-disable-next-line
167 delete this.current.nodes;
168
169 if (/!\s*important/i.test(node.selector)) {
170 node.important = true;
171
172 if (/\s*!\s+important/i.test(node.selector)) {
173 node.raws.important = node.selector.match(/(\s*!\s+important)/i)[1];
174 }
175
176 node.selector = node.selector.replace(/\s*!\s*important/i, '');
177 }
178
179 // rules don't have trailing semicolons in vanilla css, so they get
180 // added to this.spaces by the parser loop, so don't step back.
181 if (!semi) {
182 this.pos--;
183 }
184
185 this.end(this.tokens[this.pos]);
186 }
187 }
188 }, {
189 key: 'end',
190 value: function end(token) {
191 var node = this.current;
192
193 // if a Rule contains other Rules (mixins, extends) and those have
194 // semicolons, assert that the parent Rule has a semicolon
195 if (node.nodes && node.nodes.length && node.last.raws.semicolon && !node.last.nodes) {
196 this.semicolon = true;
197 }
198
199 _get(LessParser.prototype.__proto__ || Object.getPrototypeOf(LessParser.prototype), 'end', this).call(this, token);
200 }
201 }, {
202 key: 'import',
203 value: function _import(token) {
204 /* eslint complexity: 0 */
205 var last = false,
206 open = false,
207 end = { line: 0, column: 0 };
208
209 var directives = [];
210 var node = new _import3.default();
211
212 node.name = token[1].slice(1);
213
214 this.init(node, token[2], token[3]);
215
216 this.pos += 1;
217
218 while (this.pos < this.tokens.length) {
219 var tokn = this.tokens[this.pos];
220
221 if (tokn[0] === ';') {
222 end = { line: tokn[2], column: tokn[3] };
223 node.raws.semicolon = true;
224 break;
225 } else if (tokn[0] === '{') {
226 open = true;
227 break;
228 } else if (tokn[0] === '}') {
229 this.end(tokn);
230 break;
231 } else if (tokn[0] === 'brackets') {
232 if (node.urlFunc) {
233 node.importPath = tokn[1].replace(/[()]/g, '');
234 } else {
235 directives.push(tokn);
236 }
237 } else if (tokn[0] === 'space') {
238 if (directives.length) {
239 node.raws.between = tokn[1];
240 } else if (node.urlFunc) {
241 node.raws.beforeUrl = tokn[1];
242 } else if (node.importPath) {
243 if (node.urlFunc) {
244 node.raws.afterUrl = tokn[1];
245 } else {
246 node.raws.after = tokn[1];
247 }
248 } else {
249 node.raws.afterName = tokn[1];
250 }
251 } else if (tokn[0] === 'word' && tokn[1] === 'url') {
252 node.urlFunc = true;
253 } else {
254 if (tokn[0] !== '(' && tokn[0] !== ')') {
255 node.importPath = tokn[1];
256 }
257 }
258
259 if (this.pos === this.tokens.length) {
260 last = true;
261 break;
262 }
263
264 this.pos += 1;
265 }
266
267 if (node.raws.between && !node.raws.afterName) {
268 node.raws.afterName = node.raws.between;
269 node.raws.between = '';
270 }
271
272 node.source.end = end;
273
274 if (directives.length) {
275 this.raw(node, 'directives', directives);
276
277 if (last) {
278 token = directives[directives.length - 1];
279 node.source.end = { line: token[4], column: token[5] };
280 this.spaces = node.raws.between;
281 node.raws.between = '';
282 }
283 } else {
284 node.directives = '';
285 }
286
287 if (open) {
288 node.nodes = [];
289 this.current = node;
290 }
291 }
292
293 /* eslint-disable max-statements, complexity */
294
295 }, {
296 key: 'other',
297 value: function other() {
298 var brackets = [];
299 var params = [];
300 var start = this.pos;
301
302 var end = false,
303 colon = false,
304 bracket = null;
305
306 // we need pass "()" as spaces
307 // However we can override method Parser.loop, but it seems less maintainable
308 if (this.tokens[start][0] === 'brackets') {
309 this.spaces += this.tokens[start][1];
310 return;
311 }
312
313 var mixin = (0, _isMixinToken2.default)(this.tokens[start]);
314 var extend = Boolean((0, _findExtendRule2.default)(this.tokens, start));
315
316 while (this.pos < this.tokens.length) {
317 var token = this.tokens[this.pos];
318 var type = token[0];
319
320 if (type === '(' || type === '[') {
321 if (!bracket) {
322 bracket = token;
323 }
324
325 brackets.push(type === '(' ? ')' : ']');
326 } else if (brackets.length === 0) {
327 if (type === ';') {
328 var foundEndOfRule = this.ruleEnd({
329 start: start,
330 params: params,
331 colon: colon,
332 mixin: mixin,
333 extend: extend
334 });
335
336 if (foundEndOfRule) {
337 return;
338 }
339
340 break;
341 } else if (type === '{') {
342 this.createRule({ start: start, params: params, mixin: mixin });
343 return;
344 } else if (type === '}') {
345 this.pos -= 1;
346 end = true;
347 break;
348 } else if (type === ':') {
349 colon = true;
350 }
351 } else if (type === brackets[brackets.length - 1]) {
352 brackets.pop();
353 if (brackets.length === 0) {
354 bracket = null;
355 }
356 }
357
358 // we don't want to add params for pseudo-selectors that utilize parens (#56)
359 if ((extend || !colon) && (brackets.length > 0 || type === 'brackets' || params[0])) {
360 params.push(token);
361 }
362
363 this.pos += 1;
364 }
365
366 if (this.pos === this.tokens.length) {
367 this.pos -= 1;
368 end = true;
369 }
370
371 if (brackets.length > 0) {
372 this.unclosedBracket(bracket);
373 }
374
375 // dont process an end of rule if there's only one token and it's unknown (#64)
376 if (end && this.tokens.length > 1) {
377 // Handle the case where the there is only a single token in the end rule.
378 if (start === this.pos) {
379 this.pos += 1;
380 }
381
382 var _foundEndOfRule = this.ruleEnd({
383 start: start,
384 params: params,
385 colon: colon,
386 mixin: mixin,
387 extend: extend,
388 isEndOfBlock: true
389 });
390
391 if (_foundEndOfRule) {
392 return;
393 }
394 }
395
396 this.unknownWord(start);
397 }
398 }, {
399 key: 'rule',
400 value: function rule(tokens) {
401 tokens.pop();
402
403 var node = new _rule2.default();
404
405 this.init(node, tokens[0][2], tokens[0][3]);
406
407 //node.raws.between = this.spacesFromEnd(tokens);
408 node.raws.between = this.spacesAndCommentsFromEnd(tokens);
409
410 this.raw(node, 'selector', tokens);
411 this.current = node;
412
413 return node;
414 }
415 }, {
416 key: 'ruleEnd',
417 value: function ruleEnd(options) {
418 var start = options.start;
419
420
421 if (options.extend || options.mixin) {
422 this.createRule(Object.assign(options, { empty: true }));
423 return true;
424 }
425
426 if (options.colon) {
427 if (options.isEndOfBlock) {
428 while (this.pos > start) {
429 var token = this.tokens[this.pos][0];
430
431 if (token !== 'space' && token !== 'comment') {
432 break;
433 }
434
435 this.pos -= 1;
436 }
437 }
438
439 this.createDeclaration({ start: start });
440 return true;
441 }
442
443 return false;
444 }
445 }, {
446 key: 'tokenize',
447 value: function tokenize() {
448 this.tokens = (0, _lessTokenize2.default)(this.input);
449 }
450
451 /* eslint-enable max-statements, complexity */
452
453 }]);
454
455 return LessParser;
456}(_parser2.default);
457
458exports.default = LessParser;
459module.exports = exports['default'];
\No newline at end of file