list-selectors
Advanced tools
Comparing version 0.1.2 to 0.2.0
# Changelog | ||
## v0.2.0 | ||
- Major refactor, including upgrade to PostCSS 4.1. | ||
## v0.1.2 | ||
@@ -4,0 +7,0 @@ - Updated dependencies. |
225
index.js
'use strict'; | ||
var _ = require('lodash'); | ||
var fs = require('fs'); | ||
var globby = require('globby'); | ||
var request = require('request'); | ||
var postcss = require('postcss'); | ||
var chalk = require('chalk'); | ||
var selectorProcessors = require('./lib/selectorProcessors'); | ||
var standalone = require('./lib/standalone'); | ||
standalone.plugin = require('./lib/plugin'); | ||
var logPrefix = chalk.bold('list-selectors: '); | ||
// These are the valid `include` option values. | ||
var TOP_LEVEL_INCLUDES = [ | ||
'selectors', | ||
'simpleSelectors', | ||
'simple' | ||
]; | ||
var SIMPLE_INCLUDES = [ | ||
'classes', | ||
'ids', | ||
'attributes', | ||
'types' | ||
]; | ||
/** | ||
* The function that people will run. | ||
* | ||
* Arguments are polymorphic, and the process will change based on the type of input. | ||
* | ||
* @param {string|string[]} [source] - If the first argument is a string or array (of strings), | ||
* you are passing a glob (or globs) or a URL -- in which case, you are not using it as a | ||
* postcss plugin, but as a standalone function via node or the CLI. If this argument is | ||
* absent, we have to assume you're using the function a as a postcss plugin. | ||
* @param {object} [options] | ||
* @param {function} [cb] - A callback that will receive the result list of selectors | ||
* as an argument. | ||
* @return {undefined|function} If used as a postcss plugin, listSelectors will return a | ||
* function that fits into your postcss piping. Otherwise, it doesn't return anything: | ||
* its results are available for use in the callback. | ||
*/ | ||
function listSelectors(/* [source, options, cb] */) { | ||
var firstArg = arguments[0]; | ||
var source = (!_.isPlainObject(firstArg) && !_.isFunction(firstArg)) ? firstArg : false; | ||
var polyIndex = (source) ? 1 : 0; | ||
var mysteryArg = arguments[polyIndex]; // Could be options or callback | ||
var opts = (_.isPlainObject(mysteryArg)) ? mysteryArg : {}; | ||
var cb = (_.isFunction(mysteryArg)) ? mysteryArg : arguments[polyIndex + 1] || _.noop; | ||
// Standalone function is indicated by the initial file glob | ||
if (source) { | ||
listSelectorsStandalone(source, opts, cb); | ||
} else { | ||
return _.partial(listSelectorsPostcss, opts, cb); | ||
} | ||
} | ||
/** | ||
* The postcss plugin at the heart of everything. | ||
* | ||
* @param {object} opts | ||
* @param {function} cb | ||
* @param {object} cssTree - CSS Node Tree from PostCSS processing | ||
* @return {object} The same cssTree we got in, for other PostCSS plugins | ||
* that follow to use | ||
*/ | ||
function listSelectorsPostcss(opts, cb, cssTree) { | ||
var result = {}; | ||
// Run through all the rules and accumulate a list of selectors | ||
// parsed out by PostCSS | ||
var accumulatedSelectors = []; | ||
cssTree.eachRule(function(rule) { | ||
// Ignore keyframes, which can log e.g. 10%, 20% as selectors | ||
if (rule.parent.type === 'atrule' && /keyframes/.test(rule.parent.name)) { return; } | ||
rule.selectors.forEach(function(selector) { | ||
accumulatedSelectors.push(selector); | ||
}); | ||
}); | ||
// If no selectors were found, results are an empty object | ||
if (_.isEmpty(accumulatedSelectors)) { | ||
console.log(logPrefix + chalk.red( | ||
'Failed to find any selectors at all in the source files you provided. ' + | ||
'You are going to get an empty selector list.' | ||
)); | ||
cb({}); | ||
return cssTree; | ||
} | ||
// Add sorted, unique selectors to results | ||
result.selectors = _.sortBy(_.uniq(accumulatedSelectors), selectorSortFn); | ||
// Add sorted, unique simple selectors to results | ||
result.simpleSelectors = {}; | ||
result.simpleSelectors.all = _.sortBy( | ||
_.uniq(selectorProcessors.reduceToSimpleSelectors(result.selectors)), | ||
selectorSortFn | ||
); | ||
// Add all of the category lists (of simple selectors) to results | ||
result.simpleSelectors.ids = selectorProcessors.getIds(result.simpleSelectors.all); | ||
result.simpleSelectors.classes = selectorProcessors.getClasses(result.simpleSelectors.all); | ||
result.simpleSelectors.attributes = selectorProcessors.getAttributes(result.simpleSelectors.all); | ||
result.simpleSelectors.types = selectorProcessors.getTypes(result.simpleSelectors.all); | ||
// Refine the results according to any `include` options passed | ||
result = processIncludes(result, opts.include); | ||
// Call the callback as promised, passing the result as an argument | ||
cb(result); | ||
return cssTree; | ||
} | ||
/** | ||
* When using this as a standalone function or via CLI, we'll need | ||
* to get the source files before processing. They might be local, | ||
* identified with globs, or remote, identified with a URL. | ||
* Get the source files, accumulate them into a string, then | ||
* run that string through postcss with listSelectors as a plugin. | ||
* | ||
* Parameters are the same as for listSelectors, above | ||
* (and listSelectors will pass them). | ||
*/ | ||
function listSelectorsStandalone(source, opts, cb) { | ||
var fullCss = ''; | ||
if (_.startsWith(source, 'http')) { | ||
processRemoteCss(); | ||
} else { | ||
processLocalCss(); | ||
} | ||
function processRemoteCss() { | ||
var url = (_.isArray(source)) ? source[0] : source; | ||
request(url, function(err, resp, body) { | ||
if (err) { throw chalk.red(err); } | ||
if (resp.statusCode !== 200) { | ||
console.log( | ||
logPrefix + chalk.red('Failed to fetch ') + chalk.yellow.underline(url) + | ||
chalk.red('. Maybe you flubbed the url?') | ||
); | ||
body = ''; | ||
} | ||
postcss(listSelectors(opts, cb)).process(body); | ||
}); | ||
} | ||
function processLocalCss() { | ||
globby(source, function(err, filePaths) { | ||
if (err) { throw chalk.red(err); } | ||
if (!filePaths.length) { | ||
console.log( | ||
logPrefix + chalk.red('Failed to find any files matching your glob ') + | ||
chalk.yellow.underline(source) | ||
); | ||
} | ||
filePaths.forEach(function(filePath) { | ||
fullCss += fs.readFileSync(filePath, { encoding: 'utf8' }); | ||
}); | ||
postcss(listSelectors(opts, cb)).process(fullCss); | ||
}); | ||
} | ||
} | ||
/** | ||
* Used to sort selectors alphabetically, ignoring initial category | ||
* distinguishing punctuation like `#`, `.`, and `[`. | ||
* | ||
* @param {string} selector | ||
* @return {string} The sortable selector: | ||
* lowercased, stripped of initial punctuation | ||
*/ | ||
function selectorSortFn(selector) { | ||
var lowerNoPseudo = selector.split(':')[0].toLowerCase(); | ||
return (/^[#\.\[]/.test(selector)) ? lowerNoPseudo.substr(1) : lowerNoPseudo; | ||
} | ||
/** | ||
* Filter a full selector list according to specific `include` options. | ||
* | ||
* @param {object} selectorList | ||
* @param {string|object} includes | ||
* @return {object} The filtered selectorList | ||
*/ | ||
function processIncludes(selectorList, includes) { | ||
if (!includes) { return selectorList; } | ||
if (_.isString(includes)) { includes = [includes]; } | ||
var result = _.reduce(includes, function(r, include) { | ||
if (_.contains(TOP_LEVEL_INCLUDES, include)) { | ||
if (_.contains(['simple', 'simpleSelectors'], include)) { | ||
r.simpleSelectors = selectorList.simpleSelectors.all; | ||
} else { | ||
r[include] = selectorList[include]; | ||
} | ||
return r; | ||
} | ||
if (_.contains(SIMPLE_INCLUDES, include)) { | ||
r[include] = selectorList.simpleSelectors[include]; | ||
return r; | ||
} | ||
console.log(logPrefix + chalk.red('Invalid include "' + include + '" passed. ' + | ||
'The possibilities are: ' + | ||
TOP_LEVEL_INCLUDES.concat(SIMPLE_INCLUDES).join(', ') + '. ' + | ||
'You\'ll get the full selector list now.' | ||
)); | ||
return selectorList; | ||
}, {}); | ||
return result; | ||
} | ||
module.exports = listSelectors; | ||
module.exports = standalone; |
@@ -12,3 +12,3 @@ { | ||
"license": "MIT", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"description": "List the selectors used in your CSS. Use as a standalone function, CLI, or PostCSS plugin.", | ||
@@ -40,13 +40,14 @@ "homepage": "https://github.com/davidtheclark/list-selectors", | ||
"devDependencies": { | ||
"eslint": "0.15.1", | ||
"tape": "3.5.0" | ||
"eslint": "0.18.0", | ||
"tape": "4.0.0" | ||
}, | ||
"dependencies": { | ||
"chalk": "^1.0", | ||
"globby": "^1.0", | ||
"globby": "^2.0", | ||
"lodash": "^3.0", | ||
"minimist": "^1.0", | ||
"postcss": "^4.0", | ||
"postcss": "^4.1.0", | ||
"postcss-log-warnings": "^0.1.2", | ||
"request": "^2.0" | ||
} | ||
} |
@@ -13,2 +13,4 @@ # list-selectors [![Build Status](https://travis-ci.org/davidtheclark/list-selectors.svg?branch=master)](https://travis-ci.org/davidtheclark/list-selectors) | ||
*The latest version should be used as a PostCSS plugin only with PostCSS v4.1+.* | ||
## Installation | ||
@@ -43,3 +45,3 @@ | ||
// It also ignores capitalization. So you'd get | ||
// `['#goo', '.faz', '.Freedom', '[href="..."]']` in that order. | ||
// `['#goo', '.faz', '.Freedom', '[href="..."]']` in that order. | ||
{ | ||
@@ -176,2 +178,8 @@ selectors: [ | ||
Just use the `plugin` method: | ||
```js | ||
var listSelectorPlugin = require('list-selectors').plugin; | ||
``` | ||
Pass it (optional) options and a callback that will receive the output object. Then have your way with it. | ||
@@ -186,3 +194,3 @@ | ||
var gulpPostcss = require('gulp-postcss'); | ||
var listSelectors = require('listSelectors'); | ||
var listSelectorsPlugin = require('listSelectors').plugin; | ||
var customProperties = require('postcss-custom-properties'); | ||
@@ -194,3 +202,3 @@ | ||
customProperties(), | ||
listSelectors(doSomethingWithList) | ||
listSelectorsPlugin(doSomethingWithList) | ||
])) | ||
@@ -200,4 +208,4 @@ .pipe(gulp.dest('./dest')); | ||
function doSomethingWithList(list) { | ||
console.log(list); | ||
function doSomethingWithList(mySelectorList) { | ||
console.log(mySelectorList); | ||
// ... do other things | ||
@@ -211,11 +219,13 @@ } | ||
var postcss = require('postcss'); | ||
var listSelectors = require('listSelectors'); | ||
var listSelectorsPlugin = require('listSelectors').plugin; | ||
var result; | ||
var mySelectorList; | ||
var css = fs.readFileSync('foo.css', 'utf8'); | ||
var listOpts = { include: 'ids' }; | ||
postcss(listSimpleSelectors(listOpts, function(list) { result = list; })) | ||
.process(css); | ||
console.log(result); | ||
// ... do other things with result | ||
postcss(listSelectorsPlugin(listOpts, function(list) { mySelectorList = list; })) | ||
.process(css) | ||
.then(function() { | ||
console.log(mySelectorList); | ||
// ... do other things with result | ||
}); | ||
``` | ||
@@ -222,0 +232,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
20749
11
278
245
7
1
+ Addedpostcss-log-warnings@^0.1.2
+ Addedasync@1.5.2(transitive)
+ Addedglob@5.0.15(transitive)
+ Addedglobby@2.1.0(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedobject-assign@3.0.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedpostcss-log-warnings@0.1.3(transitive)
- Removedasync@0.9.2(transitive)
- Removedglob@4.5.3(transitive)
- Removedglobby@1.2.0(transitive)
- Removedminimatch@2.0.10(transitive)
- Removedobject-assign@2.1.1(transitive)
Updatedglobby@^2.0
Updatedpostcss@^4.1.0