Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
ライブエディター
未変更行を折りたたむ
折り返しなし
レイアウト
分割
統合
比較精度
スマート
単語
文字
シンタックスハイライト
構文を選択
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
Untitled diff
作成日
11 年前
差分は期限切れになりません
クリア
エクスポート
共有
説明
8 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
382 行
すべてコピー
27 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
398 行
すべてコピー
module.exports = {
module.exports = {
name: 'sort-order',
name: 'sort-order',
runBefore: 'space-before-closing-brace',
runBefore: 'space-before-closing-brace',
syntax: ['css', 'less', 'sass', 'scss'],
syntax: ['css', 'less', 'sass', 'scss'],
/**
/**
* Sets handler value.
* Sets handler value.
*
*
* @param {Array} value Option value
* @param {Array} value Option value
* @returns {Array}
* @returns {Array}
*/
*/
setValue: function(value) {
setValue: function(value) {
if (!Array.isArray(value)) throw new Error('The option accepts only array of properties.');
if (!Array.isArray(value)) throw new Error('The option accepts only array of properties.');
var order = {};
var order = {};
if (typeof value[0] === 'string') {
if (typeof value[0] === 'string') {
value.forEach(function(prop, propIndex) {
value.forEach(function(prop, propIndex) {
order[prop] = { group: 0, prop: propIndex };
order[prop] = { group: 0, prop: propIndex };
});
});
} else {
} else {
value.forEach(function(group, groupIndex) {
value.forEach(function(group, groupIndex) {
コピー
コピー済み
コピー
コピー済み
var groupComment,
isComment = /\/\*(.*?)\*\//g;
group.forEach(function(prop, propIndex) {
group.forEach(function(prop, propIndex) {
コピー
コピー済み
コピー
コピー済み
order[prop] = { group: groupIndex, prop: propIndex };
if (isComment.test(prop)) {
groupComment = prop.replace(isComment, '$1');
} else {
order[prop] = {
comment: groupComment,
group: groupIndex,
prop: propIndex
};
}
});
});
});
});
}
}
return order;
return order;
},
},
/**
/**
* Processes tree node.
* Processes tree node.
* @param {String} nodeType
* @param {String} nodeType
* @param {node} node
* @param {node} node
*/
*/
process: function(nodeType, node) {
process: function(nodeType, node) {
var _this = this;
var _this = this;
// Types of nodes that can be sorted:
// Types of nodes that can be sorted:
var NODES = ['atruleb', 'atruler', 'atrules', 'commentML', 'commentSL',
var NODES = ['atruleb', 'atruler', 'atrules', 'commentML', 'commentSL',
'declaration', 's', 'include'];
'declaration', 's', 'include'];
// Spaces and comments:
// Spaces and comments:
var SC = ['commentML', 'commentSL', 's'];
var SC = ['commentML', 'commentSL', 's'];
var currentNode;
var currentNode;
// Sort order of properties:
// Sort order of properties:
var order = this.getValue('sort-order');
var order = this.getValue('sort-order');
var syntax = this.getSyntax();
var syntax = this.getSyntax();
// List of declarations that should be sorted:
// List of declarations that should be sorted:
var sorted = [];
var sorted = [];
// list of nodes that should be removed from parent node:
// list of nodes that should be removed from parent node:
var deleted = [];
var deleted = [];
// List of spaces and comments that go before declaration/@-rule:
// List of spaces and comments that go before declaration/@-rule:
var sc0 = [];
var sc0 = [];
// Value to search in sort order: either a declaration's property name
// Value to search in sort order: either a declaration's property name
// (e.g. `color`), or @-rule's special keyword (e.g. `$import`):
// (e.g. `color`), or @-rule's special keyword (e.g. `$import`):
var propertyName;
var propertyName;
// Index to place the nodes that shouldn't be sorted
// Index to place the nodes that shouldn't be sorted
var lastGroupIndex = order['...'] ? order['...'].group : Infinity;
var lastGroupIndex = order['...'] ? order['...'].group : Infinity;
var lastPropertyIndex = order['...'] ? order['...'].prop : Infinity;
var lastPropertyIndex = order['...'] ? order['...'].prop : Infinity;
// Counters for loops:
// Counters for loops:
var i;
var i;
var l;
var l;
var j;
var j;
var nl;
var nl;
/**
/**
* Check if there are any comments or spaces before
* Check if there are any comments or spaces before
* the declaration/@-rule.
* the declaration/@-rule.
* @returns {Array} List of nodes with spaces and comments
* @returns {Array} List of nodes with spaces and comments
*/
*/
var checkSC0 = function() {
var checkSC0 = function() {
// List of nodes with spaces and comments:
// List of nodes with spaces and comments:
var sc = [];
var sc = [];
// List of nodes that can be later deleted from parent node:
// List of nodes that can be later deleted from parent node:
var d = [];
var d = [];
for (; i < l; i++) {
for (; i < l; i++) {
currentNode = node[i];
currentNode = node[i];
// If there is no node left,
// If there is no node left,
// stop and do nothing with previously found spaces/comments:
// stop and do nothing with previously found spaces/comments:
if (!currentNode) {
if (!currentNode) {
return false;
return false;
}
}
// Remove any empty lines:
// Remove any empty lines:
if (currentNode[0] === 's') {
if (currentNode[0] === 's') {
currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n');
currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n');
}
}
// If the node is declaration or @-rule, stop and return all
// If the node is declaration or @-rule, stop and return all
// found nodes with spaces and comments (if there are any):
// found nodes with spaces and comments (if there are any):
if (SC.indexOf(currentNode[0]) === -1) break;
if (SC.indexOf(currentNode[0]) === -1) break;
sc.push(currentNode);
sc.push(currentNode);
d.push(i);
d.push(i);
}
}
deleted = deleted.concat(d);
deleted = deleted.concat(d);
return sc;
return sc;
};
};
/**
/**
* Check if there are any comments or spaces after
* Check if there are any comments or spaces after
* the declaration/@-rule.
* the declaration/@-rule.
* @returns {Array} List of nodes with spaces and comments
* @returns {Array} List of nodes with spaces and comments
* @private
* @private
*/
*/
var checkSC1 = function() {
var checkSC1 = function() {
// List of nodes with spaces and comments:
// List of nodes with spaces and comments:
var sc = [];
var sc = [];
// List of nodes that can be later deleted from parent node:
// List of nodes that can be later deleted from parent node:
var d = [];
var d = [];
// Position of `\n` symbol inside a node with spaces:
// Position of `\n` symbol inside a node with spaces:
var lbIndex;
var lbIndex;
// Check every next node:
// Check every next node:
for (; i < l; i++) {
for (; i < l; i++) {
currentNode = node[i + 1];
currentNode = node[i + 1];
// If there is no node, or it is nor spaces neither comment, stop:
// If there is no node, or it is nor spaces neither comment, stop:
if (!currentNode || SC.indexOf(currentNode[0]) === -1) break;
if (!currentNode || SC.indexOf(currentNode[0]) === -1) break;
// Remove any empty lines:
// Remove any empty lines:
if (currentNode[0] === 's') {
if (currentNode[0] === 's') {
currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n');
currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n');
}
}
if (['commentML', 'commentSL'].indexOf(currentNode[0]) > -1) {
if (['commentML', 'commentSL'].indexOf(currentNode[0]) > -1) {
sc.push(currentNode);
sc.push(currentNode);
d.push(i + 1);
d.push(i + 1);
continue;
continue;
}
}
lbIndex = currentNode[1].indexOf('\n');
lbIndex = currentNode[1].indexOf('\n');
// If there are any line breaks in a node with spaces, stop and
// If there are any line breaks in a node with spaces, stop and
// split the node into two: one with spaces before line break
// split the node into two: one with spaces before line break
// and one with `\n` symbol and everything that goes after.
// and one with `\n` symbol and everything that goes after.
// Combine the first one with declaration/@-rule's node:
// Combine the first one with declaration/@-rule's node:
if (lbIndex > -1) {
if (lbIndex > -1) {
// TODO: Don't push an empty array
// TODO: Don't push an empty array
sc.push(['s', currentNode[1].substring(0, lbIndex)]);
sc.push(['s', currentNode[1].substring(0, lbIndex)]);
currentNode[1] = currentNode[1].substring(lbIndex);
currentNode[1] = currentNode[1].substring(lbIndex);
break;
break;
}
}
sc.push(currentNode);
sc.push(currentNode);
d.push(i + 1);
d.push(i + 1);
}
}
deleted = deleted.concat(d);
deleted = deleted.concat(d);
return sc;
return sc;
};
};
/**
/**
* Combine declaration/@-rule's node with other relevant information:
* Combine declaration/@-rule's node with other relevant information:
* property index, semicolon, spaces and comments.
* property index, semicolon, spaces and comments.
* @returns {Object} Extended node
* @returns {Object} Extended node
*/
*/
var extendNode = function() {
var extendNode = function() {
currentNode = node[i];
currentNode = node[i];
var nextNode = node[i + 1];
var nextNode = node[i + 1];
// Object containing current node, all corresponding spaces,
// Object containing current node, all corresponding spaces,
// comments and other information:
// comments and other information:
var extendedNode;
var extendedNode;
// Check if current node's property name is in sort order.
// Check if current node's property name is in sort order.
// If it is, save information about its indices:
// If it is, save information about its indices:
var orderProperty = order[propertyName];
var orderProperty = order[propertyName];
extendedNode = {
extendedNode = {
i: i,
i: i,
node: currentNode,
node: currentNode,
sc0: sc0,
sc0: sc0,
sc1: [],
sc1: [],
sc2: [],
sc2: [],
コピー
コピー済み
コピー
コピー済み
delim: []
delim: []
,
groupComment: orderProperty.comment
};
};
// If the declaration's property is in order's list, save its
// If the declaration's property is in order's list, save its
// group and property indices. Otherwise set them to 10000, so
// group and property indices. Otherwise set them to 10000, so
// declaration appears at the bottom of a sorted list:
// declaration appears at the bottom of a sorted list:
extendedNode.groupIndex = orderProperty && orderProperty.group > -1 ?
extendedNode.groupIndex = orderProperty && orderProperty.group > -1 ?
orderProperty.group : lastGroupIndex;
orderProperty.group : lastGroupIndex;
extendedNode.propertyIndex = orderProperty && orderProperty.prop > -1 ?
extendedNode.propertyIndex = orderProperty && orderProperty.prop > -1 ?
orderProperty.prop : lastPropertyIndex;
orderProperty.prop : lastPropertyIndex;
// Mark current node to remove it later from parent node:
// Mark current node to remove it later from parent node:
deleted.push(i);
deleted.push(i);
extendedNode.sc1 = checkSC1();
extendedNode.sc1 = checkSC1();
if (extendedNode.sc1.length) {
if (extendedNode.sc1.length) {
currentNode = node[i];
currentNode = node[i];
nextNode = node[i + 1];
nextNode = node[i + 1];
}
}
// If there is `;` right after the declaration, save it with the
// If there is `;` right after the declaration, save it with the
// declaration and mark it for removing from parent node:
// declaration and mark it for removing from parent node:
if (currentNode && nextNode && nextNode[0] === 'declDelim') {
if (currentNode && nextNode && nextNode[0] === 'declDelim') {
extendedNode.delim.push(nextNode);
extendedNode.delim.push(nextNode);
deleted.push(i + 1);
deleted.push(i + 1);
i++;
i++;
if (syntax === 'sass') return extendedNode;
if (syntax === 'sass') return extendedNode;
// Save spaces and comments which follow right after the declaration
// Save spaces and comments which follow right after the declaration
// and mark them for removing from parent node:
// and mark them for removing from parent node:
extendedNode.sc2 = checkSC1();
extendedNode.sc2 = checkSC1();
}
}
return extendedNode;
return extendedNode;
};
};
/**
/**
* Sorts properties alphabetically.
* Sorts properties alphabetically.
*
*
* @param {Object} a First extended node
* @param {Object} a First extended node
* @param {Object} b Second extended node
* @param {Object} b Second extended node
* @returns {Number} `-1` if properties should go in order `a, b`. `1`
* @returns {Number} `-1` if properties should go in order `a, b`. `1`
* if properties should go in order `b, a`.
* if properties should go in order `b, a`.
*/
*/
var sortLeftovers = function(a, b) {
var sortLeftovers = function(a, b) {
var prefixes = ['-webkit-', '-moz-', '-ms-', '-o-', ''];
var prefixes = ['-webkit-', '-moz-', '-ms-', '-o-', ''];
var prefixesRegExp = /^(-webkit-|-moz-|-ms-|-o-)(.*)$/;
var prefixesRegExp = /^(-webkit-|-moz-|-ms-|-o-)(.*)$/;
// Get property name (i.e. `color`, `-o-animation`):
// Get property name (i.e. `color`, `-o-animation`):
a = a.node[1][1][1];
a = a.node[1][1][1];
b = b.node[1][1][1];
b = b.node[1][1][1];
// Get prefix and unprefixed part. For example:
// Get prefix and unprefixed part. For example:
// ['-o-animation', '-o-', 'animation']
// ['-o-animation', '-o-', 'animation']
// ['color', '', 'color']
// ['color', '', 'color']
a = a.match(prefixesRegExp) || [a, '', a];
a = a.match(prefixesRegExp) || [a, '', a];
b = b.match(prefixesRegExp) || [b, '', b];
b = b.match(prefixesRegExp) || [b, '', b];
if (a[2] !== b[2]) {
if (a[2] !== b[2]) {
// If unprefixed parts are different (i.e. `border` and
// If unprefixed parts are different (i.e. `border` and
// `color`), compare them:
// `color`), compare them:
return a[2] < b[2] ? -1 : 1;
return a[2] < b[2] ? -1 : 1;
} else {
} else {
// If unprefixed parts are identical (i.e. `border` in
// If unprefixed parts are identical (i.e. `border` in
// `-moz-border` and `-o-border`), compare prefixes (they
// `-moz-border` and `-o-border`), compare prefixes (they
// should go in the same order they are set in `prefixes` array):
// should go in the same order they are set in `prefixes` array):
return prefixes.indexOf(a[1]) < prefixes.indexOf(b[1]) ? -1 : 1;
return prefixes.indexOf(a[1]) < prefixes.indexOf(b[1]) ? -1 : 1;
}
}
};
};
// TODO: Think it through!
// TODO: Think it through!
// Sort properties only inside blocks:
// Sort properties only inside blocks:
if (nodeType !== 'block') return;
if (nodeType !== 'block') return;
// Check every child node.
// Check every child node.
// If it is declaration (property-value pair, e.g. `color: tomato`),
// If it is declaration (property-value pair, e.g. `color: tomato`),
// or @-rule (e.g. `@include nani`),
// or @-rule (e.g. `@include nani`),
// combine it with spaces, semicolon and comments and move them from
// combine it with spaces, semicolon and comments and move them from
// current node to a separate list for further sorting:
// current node to a separate list for further sorting:
for (i = 0, l = node.length; i < l; i++) {
for (i = 0, l = node.length; i < l; i++) {
if (NODES.indexOf(node[i][0]) === -1) continue;
if (NODES.indexOf(node[i][0]) === -1) continue;
// Save preceding spaces and comments, if there are any, and mark
// Save preceding spaces and comments, if there are any, and mark
// them for removing from parent node:
// them for removing from parent node:
sc0 = checkSC0();
sc0 = checkSC0();
if (!sc0) continue;
if (!sc0) continue;
// If spaces/comments are the last nodes, stop and go to sorting:
// If spaces/comments are the last nodes, stop and go to sorting:
if (!node[i]) {
if (!node[i]) {
deleted.splice(deleted.length - sc0.length, deleted.length + 1);
deleted.splice(deleted.length - sc0.length, deleted.length + 1);
break;
break;
}
}
// Check if the node needs to be sorted:
// Check if the node needs to be sorted:
// it should be a special @-rule (e.g. `@include`) or a declaration
// it should be a special @-rule (e.g. `@include`) or a declaration
// with a valid property (e.g. `color` or `$width`).
// with a valid property (e.g. `color` or `$width`).
// If not, proceed with the next node:
// If not, proceed with the next node:
propertyName = null;
propertyName = null;
// Look for includes:
// Look for includes:
if (node[i][0] === 'include') {
if (node[i][0] === 'include') {
propertyName = '$include';
propertyName = '$include';
} else {
} else {
for (j = 1, nl = node[i].length; j < nl; j++) {
for (j = 1, nl = node[i].length; j < nl; j++) {
currentNode = node[i][j];
currentNode = node[i][j];
if (currentNode[0] === 'property') {
if (currentNode[0] === 'property') {
propertyName = currentNode[1][0] === 'variable' ?
propertyName = currentNode[1][0] === 'variable' ?
'$variable' : currentNode[1][1];
'$variable' : currentNode[1][1];
break;
break;
} else if (currentNode[0] === 'atkeyword' &&
} else if (currentNode[0] === 'atkeyword' &&
currentNode[1][1] === 'import') { // Look for imports
currentNode[1][1] === 'import') { // Look for imports
propertyName = '$import';
propertyName = '$import';
break;
break;
}
}
}
}
}
}
// If current node is not property-value pair or import or include,
// If current node is not property-value pair or import or include,
// skip it and continue with the next node:
// skip it and continue with the next node:
if (!propertyName) {
if (!propertyName) {
deleted.splice(deleted.length - sc0.length, deleted.length + 1);
deleted.splice(deleted.length - sc0.length, deleted.length + 1);
continue;
continue;
}
}
// Make an extended node and move it to a separate list for further
// Make an extended node and move it to a separate list for further
// sorting:
// sorting:
sorted.push(extendNode());
sorted.push(extendNode());
}
}
// Remove all nodes, that were moved to a `sorted` list, from parent node:
// Remove all nodes, that were moved to a `sorted` list, from parent node:
for (i = deleted.length - 1; i > -1; i--) {
for (i = deleted.length - 1; i > -1; i--) {
node.splice(deleted[i], 1);
node.splice(deleted[i], 1);
}
}
// Sort declarations saved for sorting:
// Sort declarations saved for sorting:
sorted.sort(function(a, b) {
sorted.sort(function(a, b) {
// If a's group index is higher than b's group index, in a sorted
// If a's group index is higher than b's group index, in a sorted
// list a appears after b:
// list a appears after b:
if (a.groupIndex !== b.groupIndex) return a.groupIndex - b.groupIndex;
if (a.groupIndex !== b.groupIndex) return a.groupIndex - b.groupIndex;
// If a and b belong to leftovers and `sort-order-fallback` option
// If a and b belong to leftovers and `sort-order-fallback` option
// is set to `abc`, sort properties alphabetically:
// is set to `abc`, sort properties alphabetically:
if (a.groupIndex === lastGroupIndex &&
if (a.groupIndex === lastGroupIndex &&
_this.getValue('sort-order-fallback')) {
_this.getValue('sort-order-fallback')) {
return sortLeftovers(a, b);
return sortLeftovers(a, b);
}
}
// If a and b have the same group index, and a's property index is
// If a and b have the same group index, and a's property index is
// higher than b's property index, in a sorted list a appears after
// higher than b's property index, in a sorted list a appears after
// b:
// b:
if (a.propertyIndex !== b.propertyIndex) return a.propertyIndex - b.propertyIndex;
if (a.propertyIndex !== b.propertyIndex) return a.propertyIndex - b.propertyIndex;
// If a and b have the same group index and the same property index,
// If a and b have the same group index and the same property index,
// in a sorted list they appear in the same order they were in
// in a sorted list they appear in the same order they were in
// original array:
// original array:
return a.i - b.i;
return a.i - b.i;
});
});
// Build all nodes back together. First go sorted declarations, then
// Build all nodes back together. First go sorted declarations, then
// everything else:
// everything else:
if (sorted.length > 0) {
if (sorted.length > 0) {
for (i = sorted.length - 1, l = -1; i > l; i--) {
for (i = sorted.length - 1, l = -1; i > l; i--) {
currentNode = sorted[i];
currentNode = sorted[i];
var prevNode = sorted[i - 1];
var prevNode = sorted[i - 1];
sc0 = currentNode.sc0;
sc0 = currentNode.sc0;
var sc1 = currentNode.sc1;
var sc1 = currentNode.sc1;
var sc2 = currentNode.sc2;
var sc2 = currentNode.sc2;
コピー
コピー済み
コピー
コピー済み
Text moved to lines 379-382
sc0.reverse();
// Divide declarations from different groups with
group comments (if defined) and/or
an empty line:
sc1.reverse();
if (
(currentNode.groupComment && !prevNode) || (
prevNode && currentNode.groupIndex > prevNode.groupIndex)
)
{
sc2.reverse();
// Divide declarations from different groups with
an empty line:
if (
prevNode && currentNode.groupIndex > prevNode.groupIndex)
{
if (sc0[0] && sc0[0][0] === 's' &&
if (sc0[0] && sc0[0][0] === 's' &&
(this.syntax === 'sass' ||
(this.syntax === 'sass' ||
sc0[0][1].match(/\n/g) &&
sc0[0][1].match(/\n/g) &&
sc0[0][1].match(/\n/g).length < 2)) {
sc0[0][1].match(/\n/g).length < 2)) {
コピー
コピー済み
コピー
コピー済み
sc0[0][1] = '\n' + sc0[0][1]
;
if (currentNode.groupComment) {
if (!sc0[1] || (sc0[1] && !currentNode.groupComment.match(sc0[1][1]))) {
sc0.unshift([SC[0], currentNode.groupComment]);
sc0.unshift(sc0[sc0.length - 1]);
}
}
sc0.unshift(sc0[sc0.length - 1])
;
}
}
}
}
コピー
コピー済み
コピー
コピー済み
Text moved from lines 352-355
sc0.reverse();
sc1.reverse();
sc2.reverse();
for (j = 0, nl = sc2.length; j < nl; j++) {
for (j = 0, nl = sc2.length; j < nl; j++) {
node.unshift(sc2[j]);
node.unshift(sc2[j]);
}
}
if (currentNode.delim.length > 0) node.unshift(['declDelim']);
if (currentNode.delim.length > 0) node.unshift(['declDelim']);
for (j = 0, nl = sc1.length; j < nl; j++) {
for (j = 0, nl = sc1.length; j < nl; j++) {
node.unshift(sc1[j]);
node.unshift(sc1[j]);
}
}
node.unshift(currentNode.node);
node.unshift(currentNode.node);
コピー
コピー済み
コピー
コピー済み
for (j = 0, nl = sc0.length; j < nl; j++) {
for (j = 0, nl = sc0.length; j < nl; j++) {
node.unshift(sc0[j]);
node.unshift(sc0[j]);
}
}
}
}
}
}
}
}
};
};
保存された差分
原文
ファイルを開く
module.exports = { name: 'sort-order', runBefore: 'space-before-closing-brace', syntax: ['css', 'less', 'sass', 'scss'], /** * Sets handler value. * * @param {Array} value Option value * @returns {Array} */ setValue: function(value) { if (!Array.isArray(value)) throw new Error('The option accepts only array of properties.'); var order = {}; if (typeof value[0] === 'string') { value.forEach(function(prop, propIndex) { order[prop] = { group: 0, prop: propIndex }; }); } else { value.forEach(function(group, groupIndex) { group.forEach(function(prop, propIndex) { order[prop] = { group: groupIndex, prop: propIndex }; }); }); } return order; }, /** * Processes tree node. * @param {String} nodeType * @param {node} node */ process: function(nodeType, node) { var _this = this; // Types of nodes that can be sorted: var NODES = ['atruleb', 'atruler', 'atrules', 'commentML', 'commentSL', 'declaration', 's', 'include']; // Spaces and comments: var SC = ['commentML', 'commentSL', 's']; var currentNode; // Sort order of properties: var order = this.getValue('sort-order'); var syntax = this.getSyntax(); // List of declarations that should be sorted: var sorted = []; // list of nodes that should be removed from parent node: var deleted = []; // List of spaces and comments that go before declaration/@-rule: var sc0 = []; // Value to search in sort order: either a declaration's property name // (e.g. `color`), or @-rule's special keyword (e.g. `$import`): var propertyName; // Index to place the nodes that shouldn't be sorted var lastGroupIndex = order['...'] ? order['...'].group : Infinity; var lastPropertyIndex = order['...'] ? order['...'].prop : Infinity; // Counters for loops: var i; var l; var j; var nl; /** * Check if there are any comments or spaces before * the declaration/@-rule. * @returns {Array} List of nodes with spaces and comments */ var checkSC0 = function() { // List of nodes with spaces and comments: var sc = []; // List of nodes that can be later deleted from parent node: var d = []; for (; i < l; i++) { currentNode = node[i]; // If there is no node left, // stop and do nothing with previously found spaces/comments: if (!currentNode) { return false; } // Remove any empty lines: if (currentNode[0] === 's') { currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n'); } // If the node is declaration or @-rule, stop and return all // found nodes with spaces and comments (if there are any): if (SC.indexOf(currentNode[0]) === -1) break; sc.push(currentNode); d.push(i); } deleted = deleted.concat(d); return sc; }; /** * Check if there are any comments or spaces after * the declaration/@-rule. * @returns {Array} List of nodes with spaces and comments * @private */ var checkSC1 = function() { // List of nodes with spaces and comments: var sc = []; // List of nodes that can be later deleted from parent node: var d = []; // Position of `\n` symbol inside a node with spaces: var lbIndex; // Check every next node: for (; i < l; i++) { currentNode = node[i + 1]; // If there is no node, or it is nor spaces neither comment, stop: if (!currentNode || SC.indexOf(currentNode[0]) === -1) break; // Remove any empty lines: if (currentNode[0] === 's') { currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n'); } if (['commentML', 'commentSL'].indexOf(currentNode[0]) > -1) { sc.push(currentNode); d.push(i + 1); continue; } lbIndex = currentNode[1].indexOf('\n'); // If there are any line breaks in a node with spaces, stop and // split the node into two: one with spaces before line break // and one with `\n` symbol and everything that goes after. // Combine the first one with declaration/@-rule's node: if (lbIndex > -1) { // TODO: Don't push an empty array sc.push(['s', currentNode[1].substring(0, lbIndex)]); currentNode[1] = currentNode[1].substring(lbIndex); break; } sc.push(currentNode); d.push(i + 1); } deleted = deleted.concat(d); return sc; }; /** * Combine declaration/@-rule's node with other relevant information: * property index, semicolon, spaces and comments. * @returns {Object} Extended node */ var extendNode = function() { currentNode = node[i]; var nextNode = node[i + 1]; // Object containing current node, all corresponding spaces, // comments and other information: var extendedNode; // Check if current node's property name is in sort order. // If it is, save information about its indices: var orderProperty = order[propertyName]; extendedNode = { i: i, node: currentNode, sc0: sc0, sc1: [], sc2: [], delim: [] }; // If the declaration's property is in order's list, save its // group and property indices. Otherwise set them to 10000, so // declaration appears at the bottom of a sorted list: extendedNode.groupIndex = orderProperty && orderProperty.group > -1 ? orderProperty.group : lastGroupIndex; extendedNode.propertyIndex = orderProperty && orderProperty.prop > -1 ? orderProperty.prop : lastPropertyIndex; // Mark current node to remove it later from parent node: deleted.push(i); extendedNode.sc1 = checkSC1(); if (extendedNode.sc1.length) { currentNode = node[i]; nextNode = node[i + 1]; } // If there is `;` right after the declaration, save it with the // declaration and mark it for removing from parent node: if (currentNode && nextNode && nextNode[0] === 'declDelim') { extendedNode.delim.push(nextNode); deleted.push(i + 1); i++; if (syntax === 'sass') return extendedNode; // Save spaces and comments which follow right after the declaration // and mark them for removing from parent node: extendedNode.sc2 = checkSC1(); } return extendedNode; }; /** * Sorts properties alphabetically. * * @param {Object} a First extended node * @param {Object} b Second extended node * @returns {Number} `-1` if properties should go in order `a, b`. `1` * if properties should go in order `b, a`. */ var sortLeftovers = function(a, b) { var prefixes = ['-webkit-', '-moz-', '-ms-', '-o-', '']; var prefixesRegExp = /^(-webkit-|-moz-|-ms-|-o-)(.*)$/; // Get property name (i.e. `color`, `-o-animation`): a = a.node[1][1][1]; b = b.node[1][1][1]; // Get prefix and unprefixed part. For example: // ['-o-animation', '-o-', 'animation'] // ['color', '', 'color'] a = a.match(prefixesRegExp) || [a, '', a]; b = b.match(prefixesRegExp) || [b, '', b]; if (a[2] !== b[2]) { // If unprefixed parts are different (i.e. `border` and // `color`), compare them: return a[2] < b[2] ? -1 : 1; } else { // If unprefixed parts are identical (i.e. `border` in // `-moz-border` and `-o-border`), compare prefixes (they // should go in the same order they are set in `prefixes` array): return prefixes.indexOf(a[1]) < prefixes.indexOf(b[1]) ? -1 : 1; } }; // TODO: Think it through! // Sort properties only inside blocks: if (nodeType !== 'block') return; // Check every child node. // If it is declaration (property-value pair, e.g. `color: tomato`), // or @-rule (e.g. `@include nani`), // combine it with spaces, semicolon and comments and move them from // current node to a separate list for further sorting: for (i = 0, l = node.length; i < l; i++) { if (NODES.indexOf(node[i][0]) === -1) continue; // Save preceding spaces and comments, if there are any, and mark // them for removing from parent node: sc0 = checkSC0(); if (!sc0) continue; // If spaces/comments are the last nodes, stop and go to sorting: if (!node[i]) { deleted.splice(deleted.length - sc0.length, deleted.length + 1); break; } // Check if the node needs to be sorted: // it should be a special @-rule (e.g. `@include`) or a declaration // with a valid property (e.g. `color` or `$width`). // If not, proceed with the next node: propertyName = null; // Look for includes: if (node[i][0] === 'include') { propertyName = '$include'; } else { for (j = 1, nl = node[i].length; j < nl; j++) { currentNode = node[i][j]; if (currentNode[0] === 'property') { propertyName = currentNode[1][0] === 'variable' ? '$variable' : currentNode[1][1]; break; } else if (currentNode[0] === 'atkeyword' && currentNode[1][1] === 'import') { // Look for imports propertyName = '$import'; break; } } } // If current node is not property-value pair or import or include, // skip it and continue with the next node: if (!propertyName) { deleted.splice(deleted.length - sc0.length, deleted.length + 1); continue; } // Make an extended node and move it to a separate list for further // sorting: sorted.push(extendNode()); } // Remove all nodes, that were moved to a `sorted` list, from parent node: for (i = deleted.length - 1; i > -1; i--) { node.splice(deleted[i], 1); } // Sort declarations saved for sorting: sorted.sort(function(a, b) { // If a's group index is higher than b's group index, in a sorted // list a appears after b: if (a.groupIndex !== b.groupIndex) return a.groupIndex - b.groupIndex; // If a and b belong to leftovers and `sort-order-fallback` option // is set to `abc`, sort properties alphabetically: if (a.groupIndex === lastGroupIndex && _this.getValue('sort-order-fallback')) { return sortLeftovers(a, b); } // If a and b have the same group index, and a's property index is // higher than b's property index, in a sorted list a appears after // b: if (a.propertyIndex !== b.propertyIndex) return a.propertyIndex - b.propertyIndex; // If a and b have the same group index and the same property index, // in a sorted list they appear in the same order they were in // original array: return a.i - b.i; }); // Build all nodes back together. First go sorted declarations, then // everything else: if (sorted.length > 0) { for (i = sorted.length - 1, l = -1; i > l; i--) { currentNode = sorted[i]; var prevNode = sorted[i - 1]; sc0 = currentNode.sc0; var sc1 = currentNode.sc1; var sc2 = currentNode.sc2; sc0.reverse(); sc1.reverse(); sc2.reverse(); // Divide declarations from different groups with an empty line: if (prevNode && currentNode.groupIndex > prevNode.groupIndex) { if (sc0[0] && sc0[0][0] === 's' && (this.syntax === 'sass' || sc0[0][1].match(/\n/g) && sc0[0][1].match(/\n/g).length < 2)) { sc0[0][1] = '\n' + sc0[0][1]; } } for (j = 0, nl = sc2.length; j < nl; j++) { node.unshift(sc2[j]); } if (currentNode.delim.length > 0) node.unshift(['declDelim']); for (j = 0, nl = sc1.length; j < nl; j++) { node.unshift(sc1[j]); } node.unshift(currentNode.node); for (j = 0, nl = sc0.length; j < nl; j++) { node.unshift(sc0[j]); } } } } };
変更されたテキスト
ファイルを開く
module.exports = { name: 'sort-order', runBefore: 'space-before-closing-brace', syntax: ['css', 'less', 'sass', 'scss'], /** * Sets handler value. * * @param {Array} value Option value * @returns {Array} */ setValue: function(value) { if (!Array.isArray(value)) throw new Error('The option accepts only array of properties.'); var order = {}; if (typeof value[0] === 'string') { value.forEach(function(prop, propIndex) { order[prop] = { group: 0, prop: propIndex }; }); } else { value.forEach(function(group, groupIndex) { var groupComment, isComment = /\/\*(.*?)\*\//g; group.forEach(function(prop, propIndex) { if (isComment.test(prop)) { groupComment = prop.replace(isComment, '$1'); } else { order[prop] = { comment: groupComment, group: groupIndex, prop: propIndex }; } }); }); } return order; }, /** * Processes tree node. * @param {String} nodeType * @param {node} node */ process: function(nodeType, node) { var _this = this; // Types of nodes that can be sorted: var NODES = ['atruleb', 'atruler', 'atrules', 'commentML', 'commentSL', 'declaration', 's', 'include']; // Spaces and comments: var SC = ['commentML', 'commentSL', 's']; var currentNode; // Sort order of properties: var order = this.getValue('sort-order'); var syntax = this.getSyntax(); // List of declarations that should be sorted: var sorted = []; // list of nodes that should be removed from parent node: var deleted = []; // List of spaces and comments that go before declaration/@-rule: var sc0 = []; // Value to search in sort order: either a declaration's property name // (e.g. `color`), or @-rule's special keyword (e.g. `$import`): var propertyName; // Index to place the nodes that shouldn't be sorted var lastGroupIndex = order['...'] ? order['...'].group : Infinity; var lastPropertyIndex = order['...'] ? order['...'].prop : Infinity; // Counters for loops: var i; var l; var j; var nl; /** * Check if there are any comments or spaces before * the declaration/@-rule. * @returns {Array} List of nodes with spaces and comments */ var checkSC0 = function() { // List of nodes with spaces and comments: var sc = []; // List of nodes that can be later deleted from parent node: var d = []; for (; i < l; i++) { currentNode = node[i]; // If there is no node left, // stop and do nothing with previously found spaces/comments: if (!currentNode) { return false; } // Remove any empty lines: if (currentNode[0] === 's') { currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n'); } // If the node is declaration or @-rule, stop and return all // found nodes with spaces and comments (if there are any): if (SC.indexOf(currentNode[0]) === -1) break; sc.push(currentNode); d.push(i); } deleted = deleted.concat(d); return sc; }; /** * Check if there are any comments or spaces after * the declaration/@-rule. * @returns {Array} List of nodes with spaces and comments * @private */ var checkSC1 = function() { // List of nodes with spaces and comments: var sc = []; // List of nodes that can be later deleted from parent node: var d = []; // Position of `\n` symbol inside a node with spaces: var lbIndex; // Check every next node: for (; i < l; i++) { currentNode = node[i + 1]; // If there is no node, or it is nor spaces neither comment, stop: if (!currentNode || SC.indexOf(currentNode[0]) === -1) break; // Remove any empty lines: if (currentNode[0] === 's') { currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n'); } if (['commentML', 'commentSL'].indexOf(currentNode[0]) > -1) { sc.push(currentNode); d.push(i + 1); continue; } lbIndex = currentNode[1].indexOf('\n'); // If there are any line breaks in a node with spaces, stop and // split the node into two: one with spaces before line break // and one with `\n` symbol and everything that goes after. // Combine the first one with declaration/@-rule's node: if (lbIndex > -1) { // TODO: Don't push an empty array sc.push(['s', currentNode[1].substring(0, lbIndex)]); currentNode[1] = currentNode[1].substring(lbIndex); break; } sc.push(currentNode); d.push(i + 1); } deleted = deleted.concat(d); return sc; }; /** * Combine declaration/@-rule's node with other relevant information: * property index, semicolon, spaces and comments. * @returns {Object} Extended node */ var extendNode = function() { currentNode = node[i]; var nextNode = node[i + 1]; // Object containing current node, all corresponding spaces, // comments and other information: var extendedNode; // Check if current node's property name is in sort order. // If it is, save information about its indices: var orderProperty = order[propertyName]; extendedNode = { i: i, node: currentNode, sc0: sc0, sc1: [], sc2: [], delim: [], groupComment: orderProperty.comment }; // If the declaration's property is in order's list, save its // group and property indices. Otherwise set them to 10000, so // declaration appears at the bottom of a sorted list: extendedNode.groupIndex = orderProperty && orderProperty.group > -1 ? orderProperty.group : lastGroupIndex; extendedNode.propertyIndex = orderProperty && orderProperty.prop > -1 ? orderProperty.prop : lastPropertyIndex; // Mark current node to remove it later from parent node: deleted.push(i); extendedNode.sc1 = checkSC1(); if (extendedNode.sc1.length) { currentNode = node[i]; nextNode = node[i + 1]; } // If there is `;` right after the declaration, save it with the // declaration and mark it for removing from parent node: if (currentNode && nextNode && nextNode[0] === 'declDelim') { extendedNode.delim.push(nextNode); deleted.push(i + 1); i++; if (syntax === 'sass') return extendedNode; // Save spaces and comments which follow right after the declaration // and mark them for removing from parent node: extendedNode.sc2 = checkSC1(); } return extendedNode; }; /** * Sorts properties alphabetically. * * @param {Object} a First extended node * @param {Object} b Second extended node * @returns {Number} `-1` if properties should go in order `a, b`. `1` * if properties should go in order `b, a`. */ var sortLeftovers = function(a, b) { var prefixes = ['-webkit-', '-moz-', '-ms-', '-o-', '']; var prefixesRegExp = /^(-webkit-|-moz-|-ms-|-o-)(.*)$/; // Get property name (i.e. `color`, `-o-animation`): a = a.node[1][1][1]; b = b.node[1][1][1]; // Get prefix and unprefixed part. For example: // ['-o-animation', '-o-', 'animation'] // ['color', '', 'color'] a = a.match(prefixesRegExp) || [a, '', a]; b = b.match(prefixesRegExp) || [b, '', b]; if (a[2] !== b[2]) { // If unprefixed parts are different (i.e. `border` and // `color`), compare them: return a[2] < b[2] ? -1 : 1; } else { // If unprefixed parts are identical (i.e. `border` in // `-moz-border` and `-o-border`), compare prefixes (they // should go in the same order they are set in `prefixes` array): return prefixes.indexOf(a[1]) < prefixes.indexOf(b[1]) ? -1 : 1; } }; // TODO: Think it through! // Sort properties only inside blocks: if (nodeType !== 'block') return; // Check every child node. // If it is declaration (property-value pair, e.g. `color: tomato`), // or @-rule (e.g. `@include nani`), // combine it with spaces, semicolon and comments and move them from // current node to a separate list for further sorting: for (i = 0, l = node.length; i < l; i++) { if (NODES.indexOf(node[i][0]) === -1) continue; // Save preceding spaces and comments, if there are any, and mark // them for removing from parent node: sc0 = checkSC0(); if (!sc0) continue; // If spaces/comments are the last nodes, stop and go to sorting: if (!node[i]) { deleted.splice(deleted.length - sc0.length, deleted.length + 1); break; } // Check if the node needs to be sorted: // it should be a special @-rule (e.g. `@include`) or a declaration // with a valid property (e.g. `color` or `$width`). // If not, proceed with the next node: propertyName = null; // Look for includes: if (node[i][0] === 'include') { propertyName = '$include'; } else { for (j = 1, nl = node[i].length; j < nl; j++) { currentNode = node[i][j]; if (currentNode[0] === 'property') { propertyName = currentNode[1][0] === 'variable' ? '$variable' : currentNode[1][1]; break; } else if (currentNode[0] === 'atkeyword' && currentNode[1][1] === 'import') { // Look for imports propertyName = '$import'; break; } } } // If current node is not property-value pair or import or include, // skip it and continue with the next node: if (!propertyName) { deleted.splice(deleted.length - sc0.length, deleted.length + 1); continue; } // Make an extended node and move it to a separate list for further // sorting: sorted.push(extendNode()); } // Remove all nodes, that were moved to a `sorted` list, from parent node: for (i = deleted.length - 1; i > -1; i--) { node.splice(deleted[i], 1); } // Sort declarations saved for sorting: sorted.sort(function(a, b) { // If a's group index is higher than b's group index, in a sorted // list a appears after b: if (a.groupIndex !== b.groupIndex) return a.groupIndex - b.groupIndex; // If a and b belong to leftovers and `sort-order-fallback` option // is set to `abc`, sort properties alphabetically: if (a.groupIndex === lastGroupIndex && _this.getValue('sort-order-fallback')) { return sortLeftovers(a, b); } // If a and b have the same group index, and a's property index is // higher than b's property index, in a sorted list a appears after // b: if (a.propertyIndex !== b.propertyIndex) return a.propertyIndex - b.propertyIndex; // If a and b have the same group index and the same property index, // in a sorted list they appear in the same order they were in // original array: return a.i - b.i; }); // Build all nodes back together. First go sorted declarations, then // everything else: if (sorted.length > 0) { for (i = sorted.length - 1, l = -1; i > l; i--) { currentNode = sorted[i]; var prevNode = sorted[i - 1]; sc0 = currentNode.sc0; var sc1 = currentNode.sc1; var sc2 = currentNode.sc2; // Divide declarations from different groups with group comments (if defined) and/or an empty line: if ((currentNode.groupComment && !prevNode) || (prevNode && currentNode.groupIndex > prevNode.groupIndex)) { if (sc0[0] && sc0[0][0] === 's' && (this.syntax === 'sass' || sc0[0][1].match(/\n/g) && sc0[0][1].match(/\n/g).length < 2)) { if (currentNode.groupComment) { if (!sc0[1] || (sc0[1] && !currentNode.groupComment.match(sc0[1][1]))) { sc0.unshift([SC[0], currentNode.groupComment]); sc0.unshift(sc0[sc0.length - 1]); } } sc0.unshift(sc0[sc0.length - 1]); } } sc0.reverse(); sc1.reverse(); sc2.reverse(); for (j = 0, nl = sc2.length; j < nl; j++) { node.unshift(sc2[j]); } if (currentNode.delim.length > 0) node.unshift(['declDelim']); for (j = 0, nl = sc1.length; j < nl; j++) { node.unshift(sc1[j]); } node.unshift(currentNode.node); for (j = 0, nl = sc0.length; j < nl; j++) { node.unshift(sc0[j]); } } } } };
違いを見つける