UNPKG

node-less

Version:

Less Compiler For Node

350 lines (336 loc) 15 kB
(function (module) { var DebugInfo = require('./debugInfo.js'), Rule = require('./rule.js'), Media = require('./media.js'), Directive = require('./directive.js'), Selector = require('./selector.js'), Import = require('./import.js'), Comment = require('./comment.js'), Element = require('./element.js'); var Ruleset = function (selectors, rules, strictImports) { this.selectors = selectors; this.rules = rules; this._lookups = {}; this.strictImports = strictImports; }; Ruleset.prototype = { type: "Ruleset", accept: function (visitor) { this.selectors = visitor.visit(this.selectors); this.rules = visitor.visit(this.rules); }, eval: function (env) { var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); var ruleset = new Ruleset(selectors, this.rules.slice(0), this.strictImports); var rules; var mixin = require('./mixin.js'); ruleset.originalRuleset = this; ruleset.root = this.root; ruleset.firstRoot = this.firstRoot; ruleset.allowImports = this.allowImports; if(this.debugInfo) { ruleset.debugInfo = this.debugInfo; } env.frames.unshift(ruleset); if (!env.selectors) { env.selectors = []; } env.selectors.unshift(this.selectors); if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { ruleset.evalImports(env); } for (var i = 0; i < ruleset.rules.length; i++) { if (ruleset.rules[i] instanceof mixin.Definition) { ruleset.rules[i].frames = env.frames.slice(0); } } var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; for (var i = 0; i < ruleset.rules.length; i++) { if (ruleset.rules[i] instanceof mixin.Call) { rules = ruleset.rules[i].eval(env).filter(function(r) { if ((r instanceof Rule) && r.variable) { return !(ruleset.variable(r.name)); } return true; }); ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); i += rules.length-1; ruleset.resetCache(); } } for (var i = 0, rule; i < ruleset.rules.length; i++) { rule = ruleset.rules[i]; if (! (rule instanceof mixin.Definition)) { ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; } } env.frames.shift(); env.selectors.shift(); if (env.mediaBlocks) { for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { env.mediaBlocks[i].bubbleSelectors(selectors); } } return ruleset; }, evalImports: function(env) { var i, rules; for (i = 0; i < this.rules.length; i++) { if (this.rules[i] instanceof Import) { rules = this.rules[i].eval(env); if (typeof rules.length === "number") { this.rules.splice.apply(this.rules, [i, 1].concat(rules)); i+= rules.length-1; } else { this.rules.splice(i, 1, rules); } this.resetCache(); } } }, makeImportant: function() { return new Ruleset(this.selectors, this.rules.map(function (r) { if (r.makeImportant) { return r.makeImportant(); } else { return r; } }), this.strictImports); }, matchArgs: function (args) { return !args || args.length === 0; }, resetCache: function () { this._rulesets = null; this._variables = null; this._lookups = {}; }, variables: function () { if (this._variables) { return this._variables } else { return this._variables = this.rules.reduce(function (hash, r) { if (r instanceof Rule && r.variable === true) { hash[r.name] = r; } return hash; }, {}); } }, variable: function (name) { return this.variables()[name]; }, rulesets: function () { return this.rules.filter(function (r) { return (r instanceof Ruleset) || (r instanceof mixin.Definition); }); }, find: function (selector, self) { self = self || this; var rules = [], rule, match, key = selector.toCSS(); if (key in this._lookups) { return this._lookups[key] } this.rulesets().forEach(function (rule) { if (rule !== self) { for (var j = 0; j < rule.selectors.length; j++) { if (match = selector.match(rule.selectors[j])) { if (selector.elements.length > rule.selectors[j].elements.length) { Array.prototype.push.apply(rules, rule.find( new Selector(selector.elements.slice(1)), self)); } else { rules.push(rule); } break; } } } }); return this._lookups[key] = rules; }, toCSS: function (env) { var css = [], // The CSS output rules = [], // node.Rule instances _rules = [], // rulesets = [], // node.Ruleset instances selector, // The fully rendered selector debugInfo, // Line number debugging rule; for (var i = 0; i < this.rules.length; i++) { rule = this.rules[i]; if (rule.rules || (rule instanceof Media)) { rulesets.push(rule.toCSS(env)); } else if (rule instanceof Directive) { var cssValue = rule.toCSS(env); if (rule.name === "@charset") { if (env.charset) { if (rule.debugInfo) { rulesets.push(DebugInfo(env, rule)); rulesets.push(new Comment("/*"+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); } continue; } env.charset = true; } rulesets.push(cssValue); } else if (rule instanceof Comment) { if (!rule.silent) { if (this.root) { rulesets.push(rule.toCSS(env)); } else { rules.push(rule.toCSS(env)); } } } else { if (rule.toCSS && !rule.variable) { if (this.firstRoot && rule instanceof Rule) { throw { message: "properties must be inside selector blocks, they cannot be in the root.", index: rule.index, filename: rule.currentFileInfo ? rule.currentFileInfo.filename : null}; } rules.push(rule.toCSS(env)); } else if (rule.value && !rule.variable) { rules.push(rule.value.toString()); } } } if (env.compress && rules.length) { rule = rules[rules.length - 1]; if (rule.charAt(rule.length - 1) === ';') { rules[rules.length - 1] = rule.substring(0, rule.length - 1); } } rulesets = rulesets.join(''); if (this.root) { css.push(rules.join(env.compress ? '' : '\n')); } else { if (rules.length > 0) { debugInfo = DebugInfo(env, this); selector = this.paths.map(function (p) { return p.map(function (s) { return s.toCSS(env); }).join('').trim(); }).join(env.compress ? ',' : ',\n'); for (var i = rules.length - 1; i >= 0; i--) { if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) { _rules.unshift(rules[i]); } } rules = _rules; css.push(debugInfo + selector + (env.compress ? '{' : ' {\n ') + rules.join(env.compress ? '' : '\n ') + (env.compress ? '}' : '\n}\n')); } } css.push(rulesets); return css.join('') + (env.compress ? '\n' : ''); }, joinSelectors: function (paths, context, selectors) { for (var s = 0; s < selectors.length; s++) { this.joinSelector(paths, context, selectors[s]); } }, joinSelector: function (paths, context, selector) { var i, j, k, hasParentSelector, newSelectors, el, sel, parentSel, newSelectorPath, afterParentJoin, newJoinedSelector, newJoinedSelectorEmpty, lastSelector, currentElements, selectorsMultiplied; for (i = 0; i < selector.elements.length; i++) { el = selector.elements[i]; if (el.value === '&') { hasParentSelector = true; } } if (!hasParentSelector) { if (context.length > 0) { for(i = 0; i < context.length; i++) { paths.push(context[i].concat(selector)); } } else { paths.push([selector]); } return; } currentElements = []; newSelectors = [[]]; for (i = 0; i < selector.elements.length; i++) { el = selector.elements[i]; if (el.value !== "&") { currentElements.push(el); } else { selectorsMultiplied = []; if (currentElements.length > 0) { this.mergeElementsOnToSelectors(currentElements, newSelectors); } for(j = 0; j < newSelectors.length; j++) { sel = newSelectors[j]; if (context.length == 0) { if (sel.length > 0) { sel[0].elements = sel[0].elements.slice(0); sel[0].elements.push(new Element(el.combinator, '', 0)); //new Element(el.Combinator, "")); } selectorsMultiplied.push(sel); } else { for(k = 0; k < context.length; k++) { parentSel = context[k]; newSelectorPath = []; afterParentJoin = []; newJoinedSelectorEmpty = true; if (sel.length > 0) { newSelectorPath = sel.slice(0); lastSelector = newSelectorPath.pop(); newJoinedSelector = new Selector(lastSelector.elements.slice(0), selector.extendList); newJoinedSelectorEmpty = false; } else { newJoinedSelector = new Selector([], selector.extendList); } if (parentSel.length > 1) { afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); } if (parentSel.length > 0) { newJoinedSelectorEmpty = false; newJoinedSelector.elements.push(new Element(el.combinator, parentSel[0].elements[0].value, 0)); newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); } if (!newJoinedSelectorEmpty) { newSelectorPath.push(newJoinedSelector); } newSelectorPath = newSelectorPath.concat(afterParentJoin); selectorsMultiplied.push(newSelectorPath); } } } newSelectors = selectorsMultiplied; currentElements = []; } } if (currentElements.length > 0) { this.mergeElementsOnToSelectors(currentElements, newSelectors); } for(i = 0; i < newSelectors.length; i++) { if (newSelectors[i].length > 0) { paths.push(newSelectors[i]); } } }, mergeElementsOnToSelectors: function(elements, selectors) { var i, sel, extendList; if (selectors.length == 0) { selectors.push([ new Selector(elements) ]); return; } for(i = 0; i < selectors.length; i++) { sel = selectors[i]; if (sel.length > 0) { sel[sel.length - 1] = new Selector(sel[sel.length - 1].elements.concat(elements), sel[sel.length - 1].extendList); } else { sel.push(new Selector(elements)); } } } }; module.exports = Ruleset; })(module);