css-declaration-sorter
Advanced tools
Comparing version 4.0.1 to 5.0.0
@@ -5,2 +5,13 @@ # Changelog | ||
## [5.0.0] - 2019-12-16 | ||
### Added | ||
- Option `keepOverrides` to keep overrides in place, useful for legacy CSS where shorthand declarations override longhand declarations. | ||
### Changed | ||
- Default sorting order renamed to `alphabetical`. | ||
- Custom sorting order as a JSON file replaced in favor of the option to pass a custom sorting function. | ||
### Removed | ||
- Node.js 6 and 8 support. | ||
## [4.0.1] - 2018-07-30 | ||
@@ -16,3 +27,3 @@ ### Fixed | ||
### Removed | ||
- Node 4 support. | ||
- Node.js 4 support. | ||
- Deprecated grid properties. | ||
@@ -46,2 +57,3 @@ | ||
[5.0.0]: https://github.com/Siilwyn/css-declaration-sorter/compare/v4.0.1...v5.0.0 | ||
[4.0.1]: https://github.com/Siilwyn/css-declaration-sorter/compare/v4.0.0...v4.0.1 | ||
@@ -48,0 +60,0 @@ [4.0.0]: https://github.com/Siilwyn/css-declaration-sorter/compare/v3.0.1...v4.0.0 |
{ | ||
"name": "css-declaration-sorter", | ||
"version": "4.0.1", | ||
"version": "5.0.0", | ||
"description": "Sorts CSS declarations fast and automatically in a certain order.", | ||
@@ -8,2 +8,3 @@ "main": "src/index.js", | ||
"src/index.js", | ||
"src/shorthand-data.js", | ||
"orders" | ||
@@ -20,3 +21,3 @@ ], | ||
"dependencies": { | ||
"postcss": "^7.0.1", | ||
"postcss": "^7.0.25", | ||
"timsort": "^0.3.0" | ||
@@ -26,7 +27,7 @@ }, | ||
"benchmark": "^2.1.4", | ||
"eslint": "^5.0.0", | ||
"tape": "^4.2.1" | ||
"eslint": "^6.7.2", | ||
"tape": "^4.11.0" | ||
}, | ||
"engines": { | ||
"node": ">4" | ||
"node": ">= 10" | ||
}, | ||
@@ -33,0 +34,0 @@ "repository": { |
@@ -5,8 +5,15 @@ <img alt='CSS declaration sorter logo' src='https://cdn.rawgit.com/Siilwyn/css-declaration-sorter/master/logo.svg' height='260' align='right'> | ||
[![Travis Build Status][travis-icon]][travis] | ||
[![npm version][npm-icon]][npm] | ||
[![David Dependencies Status][david-icon]][david] | ||
[![David devDependencies Status][david-dev-icon]][david-dev] | ||
[![LGTM Grade][lgtm-icon]][lgtm] | ||
[![npm][npm-icon]][npm] | ||
A Node.js module and [PostCSS] plugin to sort CSS declarations based on their property names. Ensuring the CSS is organized, more consistent and in order... Besides, sorted CSS is smaller when gzipped because there will be more similar strings. The intention of this module is to sort the source CSS code of a project in the build process. Check out [the Atom package](https://github.com/Siilwyn/css-declaration-sorter-atom) for individual usage. | ||
A Node.js module and [PostCSS] plugin to sort CSS, SCSS or Less declarations based on their property names. Ensuring styling is organized, more consistent and in order... The goal of this package is to sort the source code of a project in the build process or to decrease the distributed CSS gzipped size. Check out [the Atom package](https://github.com/Siilwyn/css-declaration-sorter-atom) for individual usage. | ||
## Niceness | ||
- Up-to-date CSS properties fetched from the [MDN Web Platform](https://developer.mozilla.org/). | ||
- Choose your wanted order or provide your own. | ||
- Nested rules sorting support. | ||
- SCSS and Less support when combined with either [postcss-scss](https://github.com/postcss/postcss-scss) or [postcss-less](https://github.com/webschik/postcss-less). | ||
- Thought-out sorting orders out of the box, **approved by their authors**. | ||
## Alphabetical example | ||
@@ -33,35 +40,25 @@ Input: | ||
## Niceness | ||
- Up-to-date CSS properties fetched from the [MDN Web Platform](https://developer.mozilla.org/). | ||
- Sort using your own defined order. | ||
- Nested rules sorting support. | ||
- Less and SCSS support when combined with either [postcss-scss](https://github.com/postcss/postcss-scss) or [postcss-less](https://github.com/webschik/postcss-less). | ||
- Thought-out sorting orders out of the box, **approved by their authors**. | ||
## Built-in sorting orders | ||
- Alphabetical | ||
`alphabetical` | ||
*Default, order in a simple alphabetical manner from a - z.* | ||
## Sorting orders | ||
- Alphabetically | ||
`alphabetically` | ||
*Default, ordering in a simple alphabetical manner from a - z.* | ||
- [SMACSS](https://smacss.com/book/formatting#grouping) | ||
`smacss` | ||
*Ordering from most important, flow affecting properties, to least important properties.* | ||
- Box | ||
- Border | ||
- Background | ||
- Text | ||
- Other | ||
*Order from most important, flow affecting properties, to least important properties.* | ||
1. Box | ||
2. Border | ||
3. Background | ||
4. Text | ||
5. Other | ||
- [Concentric CSS](https://github.com/brandon-rhodes/Concentric-CSS) | ||
`concentric-css` | ||
*Starts outside the box model, moves inward.* | ||
- Positioning | ||
- Visibility | ||
- Box model | ||
- Dimensions | ||
- Text | ||
*Order properties applying outside the box model, moving inward to intrinsic changes.* | ||
1. Positioning | ||
2. Visibility | ||
3. Box model | ||
4. Dimensions | ||
5. Text | ||
- Custom order | ||
*Provide your own order by passing the location of a JSON file containing an array.* | ||
## Usage | ||
@@ -71,3 +68,3 @@ `npm install css-declaration-sorter --save-dev` | ||
### CLI | ||
This module does not include its own CLI but works with the official [PostCSS CLI](https://github.com/postcss/postcss-cli). To use the examples below, install `postcss-cli` or prefix with `npx`. | ||
This module does not include its own CLI but works with the official [PostCSS CLI](https://github.com/postcss/postcss-cli). To use the examples below, the `postcss-cli` package is a required dependency. | ||
@@ -82,3 +79,2 @@ Piping out result from file: | ||
```js | ||
const fs = require('fs'); | ||
const postcss = require('postcss'); | ||
@@ -88,6 +84,6 @@ const cssDeclarationSorter = require('css-declaration-sorter'); | ||
postcss([cssDeclarationSorter({order: 'smacss'})]) | ||
.process(fs.readFileSync('some.css')) | ||
.then(function (result) { | ||
fs.writeFileSync('some.css', result.css); | ||
}); | ||
.process('a { color: hyperblue; display: block; }') | ||
.then(result => | ||
result.css === 'a { display: block; color: hyperblue; }' | ||
); | ||
``` | ||
@@ -109,10 +105,25 @@ | ||
## API | ||
### cssDeclarationSorter({ order, keepOverrides }) | ||
#### order | ||
Type: `string` or `function` | ||
Default: `alphabetical` | ||
Options: `alphabetical`, `smacss`, `concentric-css` | ||
Provide the name of one of the built-in sort orders or a comparison function that is passed to ([`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)). This function receives two declaration names and is expected to return `-1`, `0` or `1` depending on the wanted order. | ||
#### keepOverrides | ||
Type: `Boolean` | ||
Default: `false` | ||
To prevent breaking legacy CSS where shorthand declarations override longhand declarations this option can enabled. For example `animation-name: some; animation: greeting;` will be kept in this order when `keepOverrides` is `true`. | ||
[PostCSS]: https://github.com/postcss/postcss | ||
[travis]: https://travis-ci.org/Siilwyn/css-declaration-sorter | ||
[travis-icon]: https://img.shields.io/travis/Siilwyn/css-declaration-sorter/master.svg?style=flat-square | ||
[npm]: https://npmjs.com/css-declaration-sorter | ||
[lgtm]: https://lgtm.com/projects/g/Siilwyn/css-declaration-sorter/ | ||
[lgtm-icon]: https://img.shields.io/lgtm/grade/javascript/g/Siilwyn/css-declaration-sorter.svg?style=flat-square | ||
[npm]: https://www.npmjs.com/package/css-declaration-sorter | ||
[npm-icon]: https://img.shields.io/npm/v/css-declaration-sorter.svg?style=flat-square | ||
[david]: https://david-dm.org/Siilwyn/css-declaration-sorter | ||
[david-icon]: https://img.shields.io/david/Siilwyn/css-declaration-sorter.svg?style=flat-square | ||
[david-dev]: https://david-dm.org/Siilwyn/css-declaration-sorter?type=dev | ||
[david-dev-icon]: https://img.shields.io/david/dev/Siilwyn/css-declaration-sorter.svg?style=flat-square |
130
src/index.js
'use strict'; | ||
const fs = require('fs'); | ||
const { readFile } = require('fs').promises; | ||
const path = require('path'); | ||
@@ -9,35 +9,50 @@ | ||
module.exports = postcss.plugin('css-declaration-sorter', function (options) { | ||
return function (css) { | ||
let sortOrderPath; | ||
const builtInOrders = [ | ||
'alphabetical', | ||
'concentric-css', | ||
'smacss', | ||
]; | ||
options = options || {}; | ||
module.exports = postcss.plugin( | ||
'css-declaration-sorter', | ||
({ order = 'alphabetical', keepOverrides = false } = {}) => css => { | ||
let withKeepOverrides = comparator => comparator; | ||
if (keepOverrides) { | ||
const shorthandData = require('./shorthand-data.js'); | ||
withKeepOverrides = withOverridesComparator(shorthandData); | ||
} | ||
// Use included sorting order if order is passed and not alphabetically | ||
if (options.order && options.order !== 'alphabetically') { | ||
sortOrderPath = path.join(__dirname, '../orders/', options.order) + '.json'; | ||
} else if (options.customOrder) { | ||
sortOrderPath = options.customOrder; | ||
} else { | ||
// Fallback to the default sorting order | ||
return processCss(css, 'alphabetically'); | ||
if (typeof order === 'function') { | ||
return processCss({ css, comparator: withKeepOverrides(order) }); | ||
} | ||
// Load in the array containing the order from a JSON file | ||
return new Promise(function (resolve, reject) { | ||
fs.readFile(sortOrderPath, function (error, data) { | ||
if (error) return reject(error); | ||
resolve(data); | ||
if (!builtInOrders.includes(order)) | ||
return Promise.reject( | ||
Error([ | ||
`Invalid built-in order '${order}' provided.`, | ||
`Available built-in orders are: ${builtInOrders}`, | ||
].join('\n')) | ||
); | ||
if (order === 'alphabetical') { | ||
return processCss({ | ||
css, | ||
comparator: withKeepOverrides(defaultComparator), | ||
}); | ||
}).then(function (data) { | ||
return processCss(css, JSON.parse(data)); | ||
}); | ||
}; | ||
}); | ||
} | ||
function processCss (css, sortOrder) { | ||
// Load in the array containing the order from a JSON file | ||
return readFile(path.join(__dirname, '..', 'orders', order) + '.json') | ||
.then(data => processCss({ | ||
css, | ||
comparator: withKeepOverrides(orderComparator(JSON.parse(data))), | ||
})); | ||
} | ||
); | ||
function processCss ({ css, comparator }) { | ||
const comments = []; | ||
const rulesCache = []; | ||
css.walk(function (node) { | ||
css.walk(node => { | ||
const nodes = node.nodes; | ||
@@ -48,3 +63,3 @@ const type = node.type; | ||
// Don't do anything to root comments or the last newline comment | ||
const isNewlineNode = ~node.raws.before.indexOf('\n'); | ||
const isNewlineNode = node.raws.before && ~node.raws.before.indexOf('\n'); | ||
const lastNewlineNode = isNewlineNode && !node.next(); | ||
@@ -90,8 +105,8 @@ const onlyNode = !node.prev() && !node.next(); | ||
// Perform a sort once all comment nodes are removed | ||
rulesCache.forEach(function (nodes) { | ||
sortCssDecls(nodes, sortOrder); | ||
rulesCache.forEach(nodes => { | ||
sortCssDeclarations({ nodes, comparator }); | ||
}); | ||
// Add comments back to the nodes they are paired with | ||
comments.forEach(function (node) { | ||
comments.forEach(node => { | ||
const pairedNode = node.pairedNode; | ||
@@ -103,26 +118,33 @@ node.comment.remove(); | ||
// Sort CSS declarations alphabetically or using the set sorting order | ||
function sortCssDecls (cssDecls, sortOrder) { | ||
if (sortOrder === 'alphabetically') { | ||
timsort(cssDecls, function (a, b) { | ||
if (a.type === 'decl' && b.type === 'decl') { | ||
return comparator(a.prop, b.prop); | ||
} else { | ||
return compareDifferentType(a, b); | ||
} | ||
}); | ||
} else { | ||
timsort(cssDecls, function (a, b) { | ||
if (a.type === 'decl' && b.type === 'decl') { | ||
const aIndex = sortOrder.indexOf(a.prop); | ||
const bIndex = sortOrder.indexOf(b.prop); | ||
return comparator(aIndex, bIndex); | ||
} else { | ||
return compareDifferentType(a, b); | ||
} | ||
}); | ||
} | ||
function sortCssDeclarations ({ nodes, comparator }) { | ||
timsort(nodes, (a, b) => { | ||
if (a.type === 'decl' && b.type === 'decl') { | ||
return comparator(a.prop, b.prop); | ||
} else { | ||
return compareDifferentType(a, b); | ||
} | ||
}); | ||
} | ||
function comparator (a, b) { | ||
function withOverridesComparator (shorthandData) { | ||
return function (comparator) { | ||
return function (a, b) { | ||
if (shorthandData[a] && shorthandData[a].includes(b)) return 0; | ||
if (shorthandData[b] && shorthandData[b].includes(a)) return 0; | ||
return comparator(a, b); | ||
}; | ||
}; | ||
} | ||
function orderComparator (order) { | ||
return function (a, b) { | ||
const aIndex = order.indexOf(a); | ||
const bIndex = order.indexOf(b); | ||
return defaultComparator(aIndex, bIndex); | ||
}; | ||
} | ||
function defaultComparator (a, b) { | ||
return a === b ? 0 : a < b ? -1 : 1; | ||
@@ -132,5 +154,7 @@ } | ||
function compareDifferentType (a, b) { | ||
if (b.type === 'atrule') { return 0; } | ||
if (b.type === 'atrule') { | ||
return 0; | ||
} | ||
return (a.type === 'decl') ? -1 : (b.type === 'decl') ? 1 : 0; | ||
return a.type === 'decl' ? -1 : b.type === 'decl' ? 1 : 0; | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
35285
9
1247
124
Updatedpostcss@^7.0.25