You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
5.8 KiB
211 lines
5.8 KiB
/* 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); |
|
} |
|
};
|
|
|