Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

postcss-sorting

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

postcss-sorting - npm Package Compare versions

Comparing version 2.0.1 to 2.1.0

lib/calcAtRulePatternPriority.js

5

CHANGELOG.md

@@ -5,2 +5,7 @@ # Change Log

## 2.1.0
* Added: `order` supports new `rule` extended object, which has new `selector` option. Rules in `order` can be specified by their selector.
* Fixed: Inconsistency with shared line comments.
* Fixed: Incorrect behaviour if `hasBlock` was set to `false` in extended at-rule object in `order`.
## 2.0.1

@@ -7,0 +12,0 @@ * Accept `null` for all options.

11

docs/at-rule-nested-empty-line-before.md

@@ -317,16 +317,17 @@ # at-rule-nested-empty-line-before

```js
[true, { ignoreAtRules: ["import"] }]
[true, { ignoreAtRules: ["else"] }]
```
Before:
```css
a {
@if (true) {
} @else {
}
@else {
}
}
```
After:
```css

@@ -333,0 +334,0 @@ a {

@@ -64,3 +64,3 @@ # comment-empty-line-before

except: ["first-nested"],
ignore: ["after-comment", "inside-single-line-block"]
ignore: ["after-comment", "stylelint-command"]
}

@@ -67,0 +67,0 @@ ]

@@ -5,3 +5,3 @@ # order

`array`: `["array", "of", "keywords", "or", "expanded", "at-rule", "objects"]`
`array`: `["array", "of", "keywords", "or", "expanded", "at-rule", "or", "rule" "objects"]`

@@ -27,2 +27,15 @@ Within an order array, you can include:

- extended rule objects:
```js
{
type: 'rule',
selector: 'div'
}
```
**Unlisted elements will be placed after all listed elements.** So if you specify an array and do not include `declarations`, that means that all declarations will be placed after any other element.
## Extended at-rule objects
Extended at-rule objects have different parameters and variations.

@@ -100,4 +113,39 @@

**Unlisted elements will be placed after all listed elements.** So if you specify an array and do not include `declarations`, that means that all declarations will be placed after any other element.
## Extended rule objects
Object parameters:
* `type`: always `"rule"`
* `selector`: `string`|`regex`. Selector pattern. A string will be translated into a RegExp — `new RegExp(yourString)` — so _be sure to escape properly_. Examples:
* `selector: /^&:[\w-]+$/` matches simple pseudo-classes. E. g., `&:hover`, `&:first-child`. Doesn't match complex pseudo-classes, e. g. `&:not(.is-visible)`.
* `selector: /^&::[\w-]+$/` matches pseudo-elements. E. g. `&::before`, `&::placeholder`.
Matches all rules:
```js
{
type: 'rule'
}
```
Or keyword `rules`.
Matches all rules with selector matching pattern:
```js
{
type: 'rule',
selector: 'div'
}
```
```js
{
type: 'rule',
selector: /^&:\w+$/
}
```
If more than one pattern can be applied to a node, and these patterns have equal “power” than the first matched pattern will be applied.
## Examples

@@ -309,2 +357,128 @@

[
{
type: 'rule',
selector: '^a'
},
{
type: 'rule',
selector: /^&/
},
'rules'
]
```
Before:
```scss
a {
a {}
&:hover {}
abbr {}
span {}
}
a {
span {}
&:hover {}
}
a {
span {}
abbr {}
}
```
After:
```scss
a {
a {}
abbr {}
&:hover {}
span {}
}
a {
&:hover {}
span {}
}
a {
abbr {}
span {}
}
```
---
Given:
```js
[
{
type: 'rule',
selector: /^&/
},
{
type: 'rule',
selector: /^&:\w/
}
]
```
This code won't change, because selector patterns have equal “power” for `&:hover` selector, so the first matching is used (`/^&/`):
```scss
a {
&:hover {}
& b {}
}
a {
& b {}
&:hover {}
}
```
---
Given:
```js
[
{
type: 'rule',
selector: /^&:\w/
},
{
type: 'rule',
selector: /^&/
}
]
```
Before:
```scss
a {
& b {}
&:hover {}
}
```
After:
```scss
a {
&:hover {}
& b {}
}
```
---
Given:
```js
[
'custom-properties',

@@ -311,0 +485,0 @@ {

@@ -6,26 +6,6 @@ 'use strict';

const isStandardSyntaxProperty = require('./lib/isStandardSyntaxProperty');
const isStandardSyntaxDeclaration = require('./lib/isStandardSyntaxDeclaration');
const isCustomProperty = require('./lib/isCustomProperty');
const isDollarVariable = require('./lib/isDollarVariable');
const isRuleWithNodes = require('./lib/isRuleWithNodes');
const features = require('./lib/features');
const isSet = require('./lib/isSet');
const normalizeOptions = require('./lib/normalizeOptions');
const validateOptions = require('./lib/validateOptions');
const createExpectedOrder = require('./lib/createExpectedOrder');
const createExpectedPropertiesOrder = require('./lib/createExpectedPropertiesOrder');
const processMostNodes = require('./lib/processMostNodes');
const processLastComments = require('./lib/processLastComments');
const getPropertiesOrderData = require('./lib/getPropertiesOrderData');
const sorting = require('./lib/sorting');
const getComments = require('./lib/getComments');
const cleanEmptyLines = require('./lib/cleanEmptyLines');
const emptyLineBeforeGroup = require('./lib/emptyLineBeforeGroup');
const isSingleLineBlock = require('./lib/isSingleLineBlock');
const isSingleLineString = require('./lib/isSingleLineString');
const hasEmptyLine = require('./lib/hasEmptyLine');
const createEmptyLines = require('./lib/createEmptyLines');
const isStandardSyntaxRule = require('./lib/isStandardSyntaxRule');
const hasBlock = require('./lib/hasBlock');
const hasNonSharedLineCommentBefore = require('./lib/hasNonSharedLineCommentBefore');
const hasSharedLineCommentBefore = require('./lib/hasSharedLineCommentBefore');

@@ -49,778 +29,9 @@ module.exports = postcss.plugin('postcss-sorting', function (opts) {

// Having this option before `properties-order`, because later one can add empty lines by `emptyLineBefore`
if (opts['clean-empty-lines'] === true) {
css.walk(function (node) {
if (isRuleWithNodes(node)) {
// Remove empty lines before every node
node.each(function (childNode) {
if (childNode.raws.before) {
childNode.raws.before = cleanEmptyLines(childNode.raws.before);
}
});
opts = normalizeOptions(opts);
// Remove empty lines after the last node
if (node.raws.after) {
node.raws.after = cleanEmptyLines(node.raws.after);
}
}
});
}
if (
!_.isUndefined(opts.order)
&& !_.isNull(opts.order)
) {
const expectedOrder = createExpectedOrder(opts.order);
css.walk(function (node) {
// Process only rules and atrules with nodes
if (isRuleWithNodes(node)) {
// Nodes for sorting
let processed = [];
// Add indexes to nodes
node.each(function (childNode, index) {
processed = processMostNodes(childNode, index, expectedOrder, processed);
});
// Add last comments in the rule. Need this because last comments are not belonging to anything
node.each(function (childNode, index) {
processed = processLastComments(childNode, index, processed);
});
// Sort declarations saved for sorting
processed.sort(sorting.sortByIndexes);
// Enforce semicolon for the last node
node.raws.semicolon = true;
// Replace rule content with sorted one
node.removeAll();
node.append(processed);
}
});
}
if (
!_.isUndefined(opts['properties-order'])
&& !_.isNull(opts['properties-order'])
) {
const isAlphabetical = opts['properties-order'] === 'alphabetical';
const expectedOrder = isAlphabetical ? null : createExpectedPropertiesOrder(opts['properties-order']);
let unspecifiedPropertiesPosition = opts['unspecified-properties-position'];
if (
_.isUndefined(unspecifiedPropertiesPosition)
|| _.isNull(unspecifiedPropertiesPosition)
) {
unspecifiedPropertiesPosition = 'bottom';
Object.keys(features).forEach(function (featureName) {
if (isSet(opts[featureName])) {
features[featureName](css, opts);
}
css.walk(function (node) {
// Process only rules and atrules with nodes
if (isRuleWithNodes(node)) {
const allRuleNodes = [];
let declarations = [];
node.each(function (childNode, index) {
if (
childNode.type === 'decl' &&
isStandardSyntaxProperty(childNode.prop) &&
!isCustomProperty(childNode.prop)
) {
const unprefixedPropName = postcss.vendor.unprefixed(childNode.prop);
const propData = {
name: childNode.prop,
unprefixedName: unprefixedPropName,
orderData: isAlphabetical ? null : getPropertiesOrderData(expectedOrder, unprefixedPropName),
node: childNode,
initialIndex: index,
unspecifiedPropertiesPosition,
};
// add a marker
childNode.sortProperty = true;
// If comment on separate line before node, use node's indexes for comment
const commentsBefore = getComments.beforeDeclaration([], childNode.prev(), propData);
// If comment on same line with the node and node, use node's indexes for comment
const commentsAfter = getComments.afterDeclaration([], childNode.next(), propData);
declarations = declarations.concat(commentsBefore, propData, commentsAfter);
}
});
if (isAlphabetical) {
declarations.sort(sorting.sortDeclarationsAlphabetically);
} else {
declarations.sort(sorting.sortDeclarations);
}
// Process empty line before group
declarations.forEach(emptyLineBeforeGroup);
let foundDeclarations = false;
node.each(function (childNode) {
if (childNode.sortProperty) {
if (!foundDeclarations) {
foundDeclarations = true;
declarations.forEach((item) => {
allRuleNodes.push(item.node);
});
}
} else {
allRuleNodes.push(childNode);
}
});
node.removeAll();
node.append(allRuleNodes);
}
});
}
if (
!_.isUndefined(opts['custom-property-empty-line-before'])
&& !_.isNull(opts['custom-property-empty-line-before'])
) {
let customPropertyEmptyLineBefore = opts['custom-property-empty-line-before'];
// Convert to common options format, e. g. `true` → `[true]`
if (!_.isArray(customPropertyEmptyLineBefore)) {
customPropertyEmptyLineBefore = [customPropertyEmptyLineBefore];
}
const optionName = 'custom-property-empty-line-before';
css.walkDecls(function (decl) {
const prop = decl.prop;
const parent = decl.parent;
if (!isStandardSyntaxDeclaration(decl) || !isCustomProperty(prop)) {
return;
}
// Optionally ignore the node if a comment precedes it
if (
checkOption(optionName, 'ignore', 'after-comment')
&& hasNonSharedLineCommentBefore(decl)
) {
return;
}
// Optionally ignore nodes inside single-line blocks
if (
checkOption(optionName, 'ignore', 'inside-single-line-block')
&& isSingleLineBlock(parent)
) {
return;
}
let expectEmptyLineBefore = customPropertyEmptyLineBefore[0];
// Optionally reverse the expectation for the first nested node
if (
checkOption(optionName, 'except', 'first-nested')
&& decl === parent.first
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
// Optionally reverse the expectation if a comment precedes this node
if (
checkOption(optionName, 'except', 'after-comment')
&& hasNonSharedLineCommentBefore(decl)
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
// Optionally reverse the expectation if a custom property precedes this node
if (
checkOption(optionName, 'except', 'after-custom-property')
&& decl.prev()
&& (
(
decl.prev().prop
&& isCustomProperty(decl.prev().prop)
)
|| (
hasSharedLineCommentBefore(decl)
&& decl.prev().prev()
&& decl.prev().prev().prop
&& isCustomProperty(decl.prev().prev().prop)
)
)
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
const hasEmptyLineBefore = hasEmptyLine(decl.raws.before);
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return;
}
if (expectEmptyLineBefore) {
if (decl.raws.before.indexOf('\n') === -1) {
decl.raws.before = `\n${decl.raws.before}`;
}
decl.raws.before = createEmptyLines(1) + decl.raws.before;
} else {
decl.raws.before = cleanEmptyLines(decl.raws.before);
}
});
}
if (
!_.isUndefined(opts['dollar-variable-empty-line-before'])
&& !_.isNull(opts['dollar-variable-empty-line-before'])
) {
let dollarVariableEmptyLineBefore = opts['dollar-variable-empty-line-before'];
// Convert to common options format, e. g. `true` → `[true]`
if (!_.isArray(dollarVariableEmptyLineBefore)) {
dollarVariableEmptyLineBefore = [dollarVariableEmptyLineBefore];
}
const optionName = 'dollar-variable-empty-line-before';
css.walkDecls(function (decl) {
const prop = decl.prop;
const parent = decl.parent;
if (!isDollarVariable(prop)) {
return;
}
// Optionally ignore the node if a comment precedes it
if (
checkOption(optionName, 'ignore', 'after-comment')
&& hasNonSharedLineCommentBefore(decl)
) {
return;
}
// Optionally ignore nodes inside single-line blocks
if (
checkOption(optionName, 'ignore', 'inside-single-line-block')
&& isSingleLineBlock(parent)
) {
return;
}
let expectEmptyLineBefore = dollarVariableEmptyLineBefore[0];
// Optionally reverse the expectation for the first nested node
if (
checkOption(optionName, 'except', 'first-nested')
&& decl === parent.first
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
// Optionally reverse the expectation if a comment precedes this node
if (
checkOption(optionName, 'except', 'after-comment')
&& hasNonSharedLineCommentBefore(decl)
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
// Optionally reverse the expectation if a dollar variable precedes this node
if (
checkOption(optionName, 'except', 'after-dollar-variable')
&& decl.prev()
&& (
(
decl.prev().prop
&& isDollarVariable(decl.prev().prop)
)
|| (
hasSharedLineCommentBefore(decl)
&& decl.prev().prev()
&& decl.prev().prev().prop
&& isDollarVariable(decl.prev().prev().prop)
)
)
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
const hasEmptyLineBefore = hasEmptyLine(decl.raws.before);
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return;
}
if (expectEmptyLineBefore) {
if (decl.raws.before.indexOf('\n') === -1) {
decl.raws.before = `\n${decl.raws.before}`;
}
decl.raws.before = createEmptyLines(1) + decl.raws.before;
} else {
decl.raws.before = cleanEmptyLines(decl.raws.before);
}
});
}
if (
!_.isUndefined(opts['declaration-empty-line-before'])
&& !_.isNull(opts['declaration-empty-line-before'])
) {
let declarationEmptyLineBefore = opts['declaration-empty-line-before'];
// Convert to common options format, e. g. `true` → `[true]`
if (!_.isArray(declarationEmptyLineBefore)) {
declarationEmptyLineBefore = [declarationEmptyLineBefore];
}
const optionName = 'declaration-empty-line-before';
css.walkDecls(function (decl) {
const prop = decl.prop;
const parent = decl.parent;
if (!isStandardSyntaxDeclaration(decl)) {
return;
}
if (isCustomProperty(prop)) {
return;
}
// Optionally ignore the node if a comment precedes it
if (
checkOption(optionName, 'ignore', 'after-comment')
&& hasNonSharedLineCommentBefore(decl)
) {
return;
}
// Optionally ignore the node if a declaration precedes it
if (
checkOption(optionName, 'ignore', 'after-declaration')
&& decl.prev()
&& (
isDeclarationBefore(decl.prev())
|| (
hasSharedLineCommentBefore(decl)
&& isDeclarationBefore(decl.prev().prev())
)
)
) {
return;
}
// Optionally ignore nodes inside single-line blocks
if (
checkOption(optionName, 'ignore', 'inside-single-line-block')
&& isSingleLineBlock(parent)
) {
return;
}
let expectEmptyLineBefore = declarationEmptyLineBefore[0];
// Optionally reverse the expectation for the first nested node
if (
checkOption(optionName, 'except', 'first-nested')
&& decl === parent.first
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
// Optionally reverse the expectation if a comment precedes this node
if (
checkOption(optionName, 'except', 'after-comment')
&& hasNonSharedLineCommentBefore(decl)
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
// Optionally reverse the expectation if a declaration precedes this node
if (
checkOption(optionName, 'except', 'after-declaration')
&& decl.prev()
&& (
isDeclarationBefore(decl.prev())
|| (
hasSharedLineCommentBefore(decl)
&& isDeclarationBefore(decl.prev().prev())
)
)
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
const hasEmptyLineBefore = hasEmptyLine(decl.raws.before);
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return;
}
if (expectEmptyLineBefore) {
if (decl.raws.before.indexOf('\n') === -1) {
decl.raws.before = `\n${decl.raws.before}`;
}
decl.raws.before = createEmptyLines(1) + decl.raws.before;
} else {
decl.raws.before = cleanEmptyLines(decl.raws.before);
}
function isDeclarationBefore(targetDeclaration) {
return targetDeclaration
&& targetDeclaration.prop
&& isStandardSyntaxDeclaration(targetDeclaration)
&& !isCustomProperty(targetDeclaration.prop);
}
});
}
if (
!_.isUndefined(opts['rule-nested-empty-line-before'])
&& !_.isNull(opts['rule-nested-empty-line-before'])
) {
let ruleNestedEmptyLineBefore = opts['rule-nested-empty-line-before'];
// Convert to common options format, e. g. `true` → `[true]`
if (!_.isArray(ruleNestedEmptyLineBefore)) {
ruleNestedEmptyLineBefore = [ruleNestedEmptyLineBefore];
}
const optionName = 'rule-nested-empty-line-before';
css.walkRules(function (rule) {
if (!isStandardSyntaxRule(rule)) {
return;
}
// Only attend to nested rule sets
if (rule.parent === css) {
return;
}
// Optionally ignore the expectation if a non-shared-line comment precedes this node
if (
checkOption(optionName, 'ignore', 'after-comment')
&& hasNonSharedLineCommentBefore(rule)
) {
return;
}
// Ignore if the expectation is for multiple and the rule is single-line
if (
(
_.isString(ruleNestedEmptyLineBefore[0])
&& ruleNestedEmptyLineBefore[0].indexOf('multi-line') !== -1
)
&& isSingleLineString(rule.toString())
) {
return;
}
let expectEmptyLineBefore = false;
if (
(
_.isString(ruleNestedEmptyLineBefore[0])
&& ruleNestedEmptyLineBefore[0].indexOf('always') !== -1
)
|| ruleNestedEmptyLineBefore[0] === true
) {
expectEmptyLineBefore = true;
}
// Optionally reverse the expectation for the first nested node
if (
checkOption(optionName, 'except', 'first-nested')
&& rule === rule.parent.first
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
// Optionally reverse the expectation if a rule precedes this node
if (
checkOption(optionName, 'except', 'after-rule')
&& rule.prev()
&& (
rule.prev().type === 'rule'
|| (
hasSharedLineCommentBefore(rule)
&& rule.prev().prev()
&& rule.prev().prev().type === 'rule'
)
)
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
const hasEmptyLineBefore = hasEmptyLine(rule.raws.before);
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return;
}
if (expectEmptyLineBefore) {
if (rule.raws.before.indexOf('\n') === -1) {
rule.raws.before = `\n${rule.raws.before}`;
}
rule.raws.before = createEmptyLines(1) + rule.raws.before;
} else {
rule.raws.before = cleanEmptyLines(rule.raws.before);
}
});
}
if (
!_.isUndefined(opts['at-rule-nested-empty-line-before'])
&& !_.isNull(opts['at-rule-nested-empty-line-before'])
) {
let atRuleNestedEmptyLineBefore = opts['at-rule-nested-empty-line-before'];
// Convert to common options format, e. g. `true` → `[true]`
if (!_.isArray(atRuleNestedEmptyLineBefore)) {
atRuleNestedEmptyLineBefore = [atRuleNestedEmptyLineBefore];
}
const optionName = 'at-rule-nested-empty-line-before';
css.walkAtRules(function (atRule) {
// Only attend to nested at-rules
if (atRule.parent === css) {
return;
}
// Return early if at-rule is to be ignored
if (checkOption(optionName, 'ignoreAtRules', atRule.name)) {
return;
}
// Optionally ignore the expectation if the node is blockless
if (
checkOption(optionName, 'ignore', 'blockless-after-blockless')
&& isBlocklessAfterBlockless()
) {
return;
}
const previousNode = atRule.prev();
// Optionally ignore the expection if the node is blockless
// and following another blockless at-rule with the same name
if (
checkOption(optionName, 'ignore', 'blockless-after-same-name-blockless')
&& isBlocklessAfterSameNameBlockless()
) {
return;
}
// Optionally ignore the expectation if a comment precedes this node
if (
checkOption(optionName, 'ignore', 'after-comment')
&& hasNonSharedLineCommentBefore(atRule)
) {
return;
}
let expectEmptyLineBefore = atRuleNestedEmptyLineBefore[0];
// Optionally reverse the expectation if any exceptions apply
if (
checkOption(optionName, 'except', 'first-nested')
&& isFirstNested()
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
if (
checkOption(optionName, 'except', 'blockless-after-blockless')
&& isBlocklessAfterBlockless()
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
if (
checkOption(optionName, 'except', 'blockless-after-same-name-blockless')
&& isBlocklessAfterSameNameBlockless()
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
if (
checkOption(optionName, 'except', 'after-same-name')
&& isAfterSameName()
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
const hasEmptyLineBefore = hasEmptyLine(atRule.raws.before);
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return;
}
if (expectEmptyLineBefore) {
if (atRule.raws.before.indexOf('\n') === -1) {
atRule.raws.before = `\n${atRule.raws.before}`;
}
atRule.raws.before = createEmptyLines(1) + atRule.raws.before;
} else {
atRule.raws.before = cleanEmptyLines(atRule.raws.before);
}
function isBlocklessAfterBlockless() {
return !hasBlock(atRule)
&& atRule.prev()
&& (
(
atRule.prev().type === 'atrule'
&& !hasBlock(atRule.prev())
&& !hasBlock(atRule)
)
|| (
hasSharedLineCommentBefore(atRule)
&& atRule.prev().prev()
&& atRule.prev().prev().type === 'atrule'
&& !hasBlock(atRule.prev().prev())
)
);
}
function isBlocklessAfterSameNameBlockless() {
return !hasBlock(atRule)
&& previousNode
&& (
(
previousNode.type === 'atrule'
&& previousNode.name === atRule.name
&& !hasBlock(previousNode)
)
|| (
hasSharedLineCommentBefore(atRule)
&& previousNode.prev()
&& previousNode.prev().type === 'atrule'
&& previousNode.prev().name === atRule.name
&& !hasBlock(previousNode.prev())
)
);
}
function isAfterSameName() {
return previousNode
&& (
(
previousNode.type === 'atrule'
&& previousNode.name === atRule.name
)
|| (
hasSharedLineCommentBefore(atRule)
&& previousNode.prev()
&& previousNode.prev().type === 'atrule'
&& previousNode.prev().name === atRule.name
)
);
}
function isFirstNested() {
return atRule === atRule.parent.first;
}
});
}
if (
!_.isUndefined(opts['comment-empty-line-before'])
&& !_.isNull(opts['comment-empty-line-before'])
) {
let commentEmptyLineBefore = opts['comment-empty-line-before'];
// Convert to common options format, e. g. `true` → `[true]`
if (!_.isArray(commentEmptyLineBefore)) {
commentEmptyLineBefore = [commentEmptyLineBefore];
}
const optionName = 'comment-empty-line-before';
css.walk(function (node) {
// Process only rules and atrules with nodes
if (isRuleWithNodes(node)) {
node.walkComments((comment) => {
// Optionally ignore stylelint commands
if (
comment.text.indexOf('stylelint-') === 0
&& checkOption(optionName, 'ignore', 'stylelint-command')
) {
return;
}
// Optionally ignore newlines between comments
const prev = comment.prev();
if (
prev
&& prev.type === 'comment'
&& checkOption(optionName, 'ignore', 'after-comment')
) {
return;
}
if (
comment.raws.inline
|| comment.inline
) {
return;
}
const before = comment.raws.before || '';
// Ignore shared-line comments
if (before.indexOf('\n') === -1) {
return;
}
const hasEmptyLineBefore = hasEmptyLine(before);
let expectEmptyLineBefore = commentEmptyLineBefore[0];
// Optionally reverse the expectation if any exceptions apply
if (
checkOption(optionName, 'except', 'first-nested')
&& comment === comment.parent.first
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
}
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return;
}
if (expectEmptyLineBefore) {
comment.raws.before = createEmptyLines(1) + comment.raws.before;
} else {
comment.raws.before = cleanEmptyLines(comment.raws.before);
}
});
}
});
}
function checkOption(primaryOption, secondaryOption, value) {
const secondaryOptionValues = _.get(opts[primaryOption][1], secondaryOption);
return _.includes(secondaryOptionValues, value);
}
});
}

@@ -12,10 +12,47 @@ 'use strict';

if (_.isString(item) && item !== 'at-rules') {
if (
_.isString(item)
&& item !== 'at-rules'
&& item !== 'rules'
) {
order[item] = {
position,
};
} else {
// If it's an object
// Currently 'at-rules' only
}
if (
item === 'rules'
|| item.type === 'rule'
) {
// Convert 'rules' into extended pattern
if (item === 'rules') {
item = {
type: 'rule',
};
}
// It there are no nodes like that create array for them
if (!order[item.type]) {
order[item.type] = [];
}
const nodeData = {
position,
};
if (item.selector) {
nodeData.selector = item.selector;
if (_.isString(item.selector)) {
nodeData.selector = new RegExp(item.selector);
}
}
order[item.type].push(nodeData);
}
if (
item === 'at-rules'
|| item.type === 'at-rule'
) {
// Convert 'at-rules' into extended pattern

@@ -49,3 +86,3 @@ if (item === 'at-rules') {

if (item.hasBlock) {
if (!_.isUndefined(item.hasBlock)) {
nodeData.hasBlock = item.hasBlock;

@@ -52,0 +89,0 @@ }

@@ -6,3 +6,4 @@ 'use strict';

const isDollarVariable = require('./isDollarVariable');
const calcPatternPriority = require('./calcPatternPriority');
const calcAtRulePatternPriority = require('./calcAtRulePatternPriority');
const calcRulePatternPriority = require('./calcRulePatternPriority');

@@ -21,3 +22,27 @@ module.exports = function getOrderData(expectedOrder, node) {

} else if (node.type === 'rule') {
nodeType = 'rules';
nodeType = {
type: 'rule',
selector: node.selector,
};
const rules = expectedOrder.rule;
// Looking for most specified pattern, because it can match many patterns
if (rules && rules.length) {
let prioritizedPattern;
let max = 0;
rules.forEach(function (pattern) {
const priority = calcRulePatternPriority(pattern, nodeType);
if (priority > max) {
max = priority;
prioritizedPattern = pattern;
}
});
if (max) {
return prioritizedPattern;
}
}
} else if (node.type === 'atrule') {

@@ -46,3 +71,3 @@ nodeType = {

atRules.forEach(function (pattern) {
const priority = calcPatternPriority(pattern, nodeType);
const priority = calcAtRulePatternPriority(pattern, nodeType);

@@ -49,0 +74,0 @@ if (priority > max) {

@@ -171,21 +171,29 @@ 'use strict';

if (item.type !== 'at-rule') {
if (item.type !== 'at-rule' && item.type !== 'rule') {
return false;
}
// if parameter is specified, name should be specified also
if (!_.isUndefined(item.parameter) && _.isUndefined(item.name)) {
return false;
}
if (item.type === 'at-rule') {
// if parameter is specified, name should be specified also
if (!_.isUndefined(item.parameter) && _.isUndefined(item.name)) {
return false;
}
if (!_.isUndefined(item.hasBlock)) {
result = item.hasBlock === true || item.hasBlock === false;
}
if (!_.isUndefined(item.hasBlock)) {
result = item.hasBlock === true || item.hasBlock === false;
}
if (!_.isUndefined(item.name)) {
result = _.isString(item.name) && item.name.length;
if (!_.isUndefined(item.name)) {
result = _.isString(item.name) && item.name.length;
}
if (!_.isUndefined(item.parameter)) {
result = (_.isString(item.parameter) && item.parameter.length) || _.isRegExp(item.parameter);
}
}
if (!_.isUndefined(item.parameter)) {
result = (_.isString(item.parameter) && item.parameter.length) || _.isRegExp(item.parameter);
if (item.type === 'rule') {
if (!_.isUndefined(item.selector)) {
result = (_.isString(item.selector) && item.selector.length) || _.isRegExp(item.selector);
}
}

@@ -192,0 +200,0 @@

{
"name": "postcss-sorting",
"version": "2.0.1",
"version": "2.1.0",
"description": "PostCSS plugin to keep rules and at-rules content in order.",

@@ -29,13 +29,23 @@ "keywords": [

"lodash": "^4.17.4",
"postcss": "^5.2.10"
"postcss": "^5.2.17"
},
"devDependencies": {
"ava": "^0.17.0",
"eslint": "^3.13.1"
"eslint": "^3.19.0",
"eslint-config-hudochenkov": "^1.3.0",
"jest": "^20.0.4"
},
"scripts": {
"test": "ava && eslint index.js lib/*.js test/*.js",
"ava": "ava",
"lint": "eslint index.js lib/*.js test/*.js"
"test": "jest && npm run lint",
"jest": "jest",
"watch": "jest --watch",
"coverage": "jest --coverage",
"lint": "eslint index.js lib/*.js __tests__/*.js"
},
"jest": {
"setupFiles": [
"./jest-setup.js"
],
"testEnvironment": "node",
"testRegex": "__tests__/.*\\.js$"
}
}

@@ -1,2 +0,2 @@

# PostCSS Sorting [![Build Status][ci-img]][ci]
# PostCSS Sorting [![Build Status][ci-img]][ci] [![npm version][npm-version-img]][npm] [![npm downloads last month][npm-downloads-img]][npm] [![Dependency status][dependencies-img]][dependencies-status]

@@ -243,5 +243,11 @@ [PostCSS] plugin to keep rules and at-rules content in order.

[PostCSS]: https://github.com/postcss/postcss
[ci-img]: https://travis-ci.org/hudochenkov/postcss-sorting.svg
[ci]: https://travis-ci.org/hudochenkov/postcss-sorting
[npm-version-img]: https://img.shields.io/npm/v/postcss-sorting.svg
[npm-downloads-img]: https://img.shields.io/npm/dm/postcss-sorting.svg
[dependencies-img]: https://img.shields.io/gemnasium/hudochenkov/postcss-sorting.svg
[dependencies-status]: https://gemnasium.com/github.com/hudochenkov/postcss-sorting
[npm]: https://www.npmjs.com/package/postcss-sorting
[PostCSS]: https://github.com/postcss/postcss
[Sublime Text plugin]: https://github.com/hudochenkov/sublime-postcss-sorting

@@ -248,0 +254,0 @@ [Atom plugin]: https://github.com/lysyi3m/atom-postcss-sorting

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc