From 2374f768410d00a0298ddf2e8043b0a383b0a686 Mon Sep 17 00:00:00 2001 From: Denis Payase Date: Mon, 26 Aug 2013 17:21:50 +0400 Subject: [PATCH] option: sort-order --- lib/options/sort-order.js | 159 ++++++++++++++++++++++++++++++++++++++ test/integral.expect.css | 30 ++++--- test/sort-order.js | 118 ++++++++++++++++++++++++++++ 3 files changed, 296 insertions(+), 11 deletions(-) create mode 100644 lib/options/sort-order.js create mode 100644 test/sort-order.js diff --git a/lib/options/sort-order.js b/lib/options/sort-order.js new file mode 100644 index 00000000..21f8a360 --- /dev/null +++ b/lib/options/sort-order.js @@ -0,0 +1,159 @@ +module.exports = { + + /** + * Internal. Smart split bunch on '\n' symbols; + * @param {Array} bunch + * @param {Boolean} isPrevDeclExists + */ + _splitBunch: function(bunch, isPrevDeclExists) { + + var nextBunch = []; + var declAlready = false; + var flag = false; + + for (var i = 0; i < bunch.length; ++i) { + if (flag) { nextBunch.push(bunch[i]); continue; } + if (bunch[i][0] == 'declaration') { declAlready = true; } + + if (isPrevDeclExists && !declAlready) continue; + if (bunch[i][0] != 's') continue; + + var indexOfNewLine = bunch[i][1].indexOf('\n'); + + if (indexOfNewLine == -1) continue; + + nextBunch.push(['s', bunch[i][1].substr(indexOfNewLine + 1)]); + bunch[i][1] = bunch[i][1].substr(0, indexOfNewLine + 1); + + flag = i + 1; + } + + flag && bunch.splice(flag); + + return nextBunch; + + }, + + /** + * Internal. Create extended node in format of list + * { + * data:[,,declaration,] + * decl: declarationPropertyName + * }; + * @param {node} node + */ + _createNodeExt: function(node) { + + var extNode = []; + var bunch = []; + var nextBunch; + var prevDeclPropertyName; + + node.forEach(function(node) { + + if (node[0] == 'declaration') { + nextBunch = this._splitBunch(bunch, prevDeclPropertyName); + extNode.push({ data: bunch, decl: prevDeclPropertyName }); + bunch = nextBunch; + prevDeclPropertyName = node[1][1][1]; + } + + bunch.push(node); + + }, this); + + nextBunch = this._splitBunch(bunch, prevDeclPropertyName); + extNode.push({ data: bunch, decl: prevDeclPropertyName }); + nextBunch.length && extNode.push({ data: nextBunch }); + + return extNode; + + }, + + /** + * Internal. Add delimiter at the end of groups of properties + * @param {extNode} extNodePrev + * @param {extNode} extNode + */ + _addGroupDelimiter: function(extNodePrev, extNode) { + + if (!extNodePrev.decl || !extNode.decl) return; + + var indexA = this._order[extNodePrev.decl]; + var indexB = this._order[extNode.decl]; + + if (indexA && indexB && indexA.group != indexB.group) + extNode.data.unshift(['s', '\n']); + + }, + + /** + * Internal. apply extNode back at common format node. + * @param {node} node + * @param {extNode} extNode + */ + _applyNodeExt: function(node, extNode) { + + node.splice(0, node.length); + + extNode.forEach(function(item, i) { + i > 0 && this._addGroupDelimiter(extNode[i - 1], item); + node.push.apply(node, item.data); + }, this); + + }, + + /** + * Sets handler value. + * + * @param {Array} value Option value + * @returns {Object} + */ + setValue: function(value) { + + if (!value) return; + + this._order = {} + value.forEach(function(group, groupIndex){ + group.forEach(function(prop, propIndex) { + this._order[prop] = { group: groupIndex, prop: propIndex }; + }, this) + }, this); + + return this; + + }, + + /** + * Processes tree node. + * @param {String} nodeType + * @param {node} node + */ + process: function(nodeType, node) { + + if (nodeType !== 'block') return; + + var order = this._order; + var nodeExt = this._createNodeExt(node); + var firstSymbols = nodeExt[0].decl || nodeExt.shift(); + var lastSymbols = nodeExt[nodeExt.length - 1].decl || nodeExt.pop(); + + nodeExt.sort(function(a, b) { + + var indexA = order[a.decl] || { prop: -1 }; + var indexB = order[b.decl] || { prop: -1 }; + var groupDelta = indexA.group - indexB.group; + var propDelta = indexA.prop - indexB.prop; + + return groupDelta !== 0 ? groupDelta : propDelta; + + }); + + firstSymbols && nodeExt.unshift(firstSymbols); + lastSymbols && nodeExt.push(lastSymbols); + + this._applyNodeExt(node, nodeExt); + + } + +}; diff --git a/test/integral.expect.css b/test/integral.expect.css index d5a4651c..838aed90 100644 --- a/test/integral.expect.css +++ b/test/integral.expect.css @@ -1,58 +1,66 @@ /* Фигурные скобки. Вариант 1 */ a, b, i /* foobar */ { - padding: 0; margin: 0; + padding: 0; } div p { - font-size: 1px; top: 0; + + font-size: 1px; } div p em { - font-style: italic; border-bottom: 1px solid red; + + font-style: italic; } /* Фигурные скобки. Вариант 2 */ div { - padding: 0; margin: 0; + padding: 0; } div p { - font-size: 1px; top: 0; + + font-size: 1px; } div p em { - font-style: italic;/* inline comment*/ border-bottom: 1px solid red; + + font-style: italic;/* inline comment*/ } /* Фигурные скобки. Вариант 3 */ div { - padding: 0; margin: 0; + padding: 0; } /* foo */ div p { - font-size: 1px; top: 0; + + font-size: 1px; } div p em { + border-bottom: 1px solid red; /* trololo */ /* trololo */ + /* upline comment*/ font-style: italic; - border-bottom: 1px solid red; /* trololo */ /* trololo */ } a { -top: 0;/* ololo */margin: 0;} +top: 0;/* ololo */ +margin: 0;} b { -top: 0/* trololo */;margin: 0;} +top: 0/* trololo */; +margin: 0;} diff --git a/test/sort-order.js b/test/sort-order.js new file mode 100644 index 00000000..9d97ac4a --- /dev/null +++ b/test/sort-order.js @@ -0,0 +1,118 @@ +var Comb = require('../lib/csscomb'); +var assert = require('assert'); + +describe('options/sort-order', function() { + var comb; + + beforeEach(function() { + comb = new Comb(); + }); + + it('Should be in expected order in case of 1 group', function() { + + var config = { + 'sort-order': [ + ['position', 'z-index'] + ] + }; + + var input = 'a\n' + + '{\n' + + '\tz-index: 2;\n' + + '\tposition: absolute;\n' + + '}'; + + var expected = 'a\n' + + '{\n' + + '\tposition: absolute;\n' + + '\tz-index: 2;\n' + + '}'; + + comb.configure(config); + assert.equal(comb.processString(input), expected); + + }); + + it('Shuld be in expected order in case of multiple groups', function() { + + var config = { + 'sort-order': [ + ['position', 'z-index'], + ['width', 'height'] + ] + }; + + var input = 'a\n' + + '{\n' + + '\tz-index: 2;\n' + + '\tposition: absolute;\n' + + '\theight: 2px;\n' + + '\twidth: 2px;\n' + + '}'; + + var expected = 'a\n' + + '{\n' + + '\tposition: absolute;\n' + + '\tz-index: 2;\n' + + '\n' + + '\twidth: 2px;\n' + + '\theight: 2px;\n' + + '}'; + + comb.configure(config); + assert.equal(comb.processString(input), expected); + + }); + + it('Should work correctly with comments in case of 1 group', function() { + + var config = { + 'sort-order': [ + ['border-bottom', 'font-style'], + ] + }; + + var input = 'div p em {\n' + + '\t/* upline comment */\n' + + '\tfont-style:italic;\n' + + '\tborder-bottom:1px solid red /* trololo */ /* trololo */\n' + + '}'; + + var expected = 'div p em {\n' + + '\tborder-bottom:1px solid red /* trololo */ /* trololo */\n' + + '\t/* upline comment */\n' + + '\tfont-style:italic;\n' + + '}'; + + comb.configure(config); + assert.equal(comb.processString(input), expected); + + }); + + it('Should work correctly with comments in case of multiple groups', function() { + + var config = { + 'sort-order': [ + ['margin'], + ['padding'] + ] + }; + + var input = 'a, b, i /* foobar */\n' + + '{\n' + + ' padding: 0;\n' + + ' margin: 0;\n' + + ' }'; + + var expected = 'a, b, i /* foobar */\n' + + '{\n' + + ' margin: 0;\n' + + '\n' + + ' padding: 0;\n' + + ' }'; + + comb.configure(config); + assert.equal(comb.processString(input), expected); + + }); +});