123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /* eslint no-param-reassign: off */
-
- const Comment = require('postcss/lib/comment');
- const Parser = require('postcss/lib/parser');
-
- const { isInlineComment } = require('./nodes/inline-comment');
- const { interpolation } = require('./nodes/interpolation');
- const { isMixinToken } = require('./nodes/mixin');
- const importNode = require('./nodes/import');
- const variableNode = require('./nodes/variable');
-
- const importantPattern = /(!\s*important)$/i;
-
- module.exports = class LessParser extends Parser {
- constructor(...args) {
- super(...args);
-
- this.lastNode = null;
- }
-
- atrule(token) {
- if (interpolation.bind(this)(token)) {
- return;
- }
-
- super.atrule(token);
- importNode(this.lastNode);
- variableNode(this.lastNode);
- }
-
- decl(...args) {
- super.decl(...args);
-
- // #123: add `extend` decorator to nodes
- const extendPattern = /extend\(.+\)/i;
-
- if (extendPattern.test(this.lastNode.value)) {
- this.lastNode.extend = true;
- }
- }
-
- each(tokens) {
- // prepend a space so the `name` will be parsed correctly
- tokens[0][1] = ` ${tokens[0][1]}`;
-
- const firstParenIndex = tokens.findIndex((t) => t[0] === '(');
- const lastParen = tokens.reverse().find((t) => t[0] === ')');
- const lastParenIndex = tokens.reverse().indexOf(lastParen);
- const paramTokens = tokens.splice(firstParenIndex, lastParenIndex);
- const params = paramTokens.map((t) => t[1]).join('');
-
- for (const token of tokens.reverse()) {
- this.tokenizer.back(token);
- }
-
- this.atrule(this.tokenizer.nextToken());
- this.lastNode.function = true;
- this.lastNode.params = params;
- }
-
- init(node, line, column) {
- super.init(node, line, column);
- this.lastNode = node;
- }
-
- inlineComment(token) {
- const node = new Comment();
- const text = token[1].slice(2);
-
- this.init(node, token[2], token[3]);
-
- node.source.end = { line: token[4], column: token[5] };
- node.inline = true;
- node.raws.begin = '//';
-
- if (/^\s*$/.test(text)) {
- node.text = '';
- node.raws.left = text;
- node.raws.right = '';
- } else {
- const match = text.match(/^(\s*)([^]*[^\s])(\s*)$/);
- [, node.raws.left, node.text, node.raws.right] = match;
- }
- }
-
- mixin(tokens) {
- const [first] = tokens;
- const identifier = first[1].slice(0, 1);
- const bracketsIndex = tokens.findIndex((t) => t[0] === 'brackets');
- const firstParenIndex = tokens.findIndex((t) => t[0] === '(');
- let important = '';
-
- // fix for #86. if rulesets are mixin params, they need to be converted to a brackets token
- if ((bracketsIndex < 0 || bracketsIndex > 3) && firstParenIndex > 0) {
- const lastParenIndex = tokens.reduce((last, t, i) => (t[0] === ')' ? i : last));
-
- const contents = tokens.slice(firstParenIndex, lastParenIndex + firstParenIndex);
- const brackets = contents.map((t) => t[1]).join('');
- const [paren] = tokens.slice(firstParenIndex);
- const start = [paren[2], paren[3]];
- const [last] = tokens.slice(lastParenIndex, lastParenIndex + 1);
- const end = [last[2], last[3]];
- const newToken = ['brackets', brackets].concat(start, end);
-
- const tokensBefore = tokens.slice(0, firstParenIndex);
- const tokensAfter = tokens.slice(lastParenIndex + 1);
- tokens = tokensBefore;
- tokens.push(newToken);
- tokens = tokens.concat(tokensAfter);
- }
-
- const importantTokens = [];
-
- for (const token of tokens) {
- if (token[1] === '!' || importantTokens.length) {
- importantTokens.push(token);
- }
-
- if (token[1] === 'important') {
- break;
- }
- }
-
- if (importantTokens.length) {
- const [bangToken] = importantTokens;
- const bangIndex = tokens.indexOf(bangToken);
- const last = importantTokens[importantTokens.length - 1];
- const start = [bangToken[2], bangToken[3]];
- const end = [last[4], last[5]];
- const combined = importantTokens.map((t) => t[1]).join('');
- const newToken = ['word', combined].concat(start, end);
- tokens.splice(bangIndex, importantTokens.length, newToken);
- }
-
- const importantIndex = tokens.findIndex((t) => importantPattern.test(t[1]));
-
- if (importantIndex > 0) {
- [, important] = tokens[importantIndex];
- tokens.splice(importantIndex, 1);
- }
-
- for (const token of tokens.reverse()) {
- this.tokenizer.back(token);
- }
-
- this.atrule(this.tokenizer.nextToken());
- this.lastNode.mixin = true;
- this.lastNode.raws.identifier = identifier;
-
- if (important) {
- this.lastNode.important = true;
- this.lastNode.raws.important = important;
- }
- }
-
- other(token) {
- if (!isInlineComment.bind(this)(token)) {
- super.other(token);
- }
- }
-
- rule(tokens) {
- const last = tokens[tokens.length - 1];
- const prev = tokens[tokens.length - 2];
-
- if (prev[0] === 'at-word' && last[0] === '{') {
- this.tokenizer.back(last);
- if (interpolation.bind(this)(prev)) {
- const newToken = this.tokenizer.nextToken();
-
- tokens = tokens.slice(0, tokens.length - 2).concat([newToken]);
-
- for (const tokn of tokens.reverse()) {
- this.tokenizer.back(tokn);
- }
-
- return;
- }
- }
-
- super.rule(tokens);
-
- // #123: add `extend` decorator to nodes
- const extendPattern = /:extend\(.+\)/i;
-
- if (extendPattern.test(this.lastNode.selector)) {
- this.lastNode.extend = true;
- }
- }
-
- unknownWord(tokens) {
- // NOTE: keep commented for examining unknown structures
- // console.log('unknown', tokens);
-
- const [first] = tokens;
-
- // #121 support `each` - http://lesscss.org/functions/#list-functions-each
- if (tokens[0][1] === 'each' && tokens[1][0] === '(') {
- this.each(tokens);
- return;
- }
-
- // TODO: move this into a util function/file
- if (isMixinToken(first)) {
- this.mixin(tokens);
- return;
- }
-
- super.unknownWord(tokens);
- }
- };
|