Skip to content

Commit

Permalink
support glob pattern for cli
Browse files Browse the repository at this point in the history
  • Loading branch information
yaniswang committed Oct 10, 2015
1 parent aaf4354 commit a69029f
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 75 deletions.
4 changes: 3 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
node_modules
coverage
npm-debug.log
npm-debug.log
src
test
5 changes: 3 additions & 2 deletions CHANGE.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
HTMLHint change log
====================

## ver 0.9.10 (2015-10-9)
## ver 0.9.10 (2015-10-10)

add:

1. attr-unsafe-chars(rule): show unsafe code in message
2. support glob pattern for cli

fix:

1. title-require(rule): report error when `<html><title>test</title><head></head><body></body></html>`
1. title-require(rule): report error when `<html><head><title></title></head><body></body></html>`
2. title-require(rule): report error when `<html><head><title></title></head><body></body></html>`

## ver 0.9.9 (2015-10-9)

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ Quick start
npm install htmlhint -g
htmlhint -V
htmlhint --help
htmlhint test.html
htmlhint www
htmlhint www/test.html
htmlhint www/**/*.xhtml

2. results

Expand Down
176 changes: 105 additions & 71 deletions bin/htmlhint
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ var fs = require('fs');
var path = require('path');
var stripJsonComments = require('strip-json-comments');
var async = require('async');
var glob = require("glob");
var parseGlob = require('parse-glob');

var HTMLHint = require("../index").HTMLHint;
var pkg = require('../package.json');
Expand All @@ -14,28 +16,34 @@ require('colors');
function map(val) {
var objMap = {};
val.split(',').forEach(function(item){
var arrItem = item.split(/\s*=\s*/);
objMap[arrItem[0]] = arrItem[1]?arrItem[1]:true;
var arrItem = item.split(/\s*=\s*/);
objMap[arrItem[0]] = arrItem[1]?arrItem[1]:true;
});
return objMap;
}

program.on('--help', function(){
console.log(' Examples:');
console.log('');
console.log(' htmlhint');
console.log(' htmlhint www');
console.log(' htmlhint www/test.html');
console.log(' htmlhint www/**/*.xhtml');
console.log(' htmlhint --list');
console.log(' htmlhint --rules tag-pair,id-class-value=underline test.html');
console.log(' htmlhint --config .htmlhintrc test.html');
console.log(' htmlhint --ignore **/build/**,**/test/**');
console.log('');
});

program
.version(pkg.version)
.usage('<file ...> [options]')
.usage('<file|folder|pattern ...> [options]')
.option('-l, --list', 'show all of the rules available.')
.option('-c, --config <file>', 'custom configuration file.')
.option('-r, --rules <ruleid, ruleid=value ...>', 'set all of the rules available.', map)
.option('-j, --json', 'output messages as raw JSON')
.option('-i, --ignore <pattern, pattern ...>', 'Add pattern to exclude matches')
.parse(process.argv);

if(program.list){
Expand All @@ -45,18 +53,19 @@ if(program.list){

var arrTargets = program.args;
if(arrTargets.length === 0){
arrTargets.push(process.cwd());
arrTargets.push('.');
}

hintTargets(arrTargets, {
ruleset: program.rules,
json: program.json
json: program.json,
ignore: program.ignore
});

// list all rules
function listRules(){
var rules = HTMLHint.rules;
var rule;
var rule;
console.log(' All rules:');
console.log(' ==================================================');
for (var id in rules){
Expand All @@ -67,7 +76,9 @@ function listRules(){

function hintTargets(arrTargets, options){
var allFileCount = 0;
var allHintFileCount = 0;
var allHintCount = 0;
var startTime = new Date().getTime();

// json mode
var json = options.json;
Expand All @@ -78,28 +89,27 @@ function hintTargets(arrTargets, options){
}
var arrTasks = [];
arrTargets.forEach(function(target){
target = path.resolve(target);
if(fs.existsSync(target)){
arrTasks.push(function(next){
hintAllFiles(target, options, function(result){
allFileCount += result.targetFileCount;
allHintCount += result.targetHintCount;
arrJson = arrJson.concat(result.arrJson);
next();
});
arrTasks.push(function(next){
hintAllFiles(target, options, function(result){
allFileCount += result.targetFileCount;
allHintFileCount += result.targetHintFileCount;
allHintCount += result.targetHintCount;
arrJson = arrJson.concat(result.arrJson);
next();
});
}
});
});
async.series(arrTasks, function(){
if(json){
console.log(JSON.stringify(arrJson));
}
else{
var spendTime = new Date().getTime() - startTime;
if(allHintCount > 0){
console.log('%d errors in %d files'.red, allHintCount, allFileCount);
console.log('Scan %d files, found %d errors in %d files (%d ms)'.red, allFileCount, allHintCount, allHintFileCount, spendTime);
}
else{
console.log('Done, without errors.'.green);
console.log('Scan %d files, without errors (%d ms).'.green, allFileCount, spendTime);
}
}
process.exit(allHintCount > 0 ? 1: 0);
Expand All @@ -108,8 +118,12 @@ function hintTargets(arrTargets, options){

// hint all files
function hintAllFiles(target, options, onFinised){
var globInfo = getGlobInfo(target);
globInfo.ignore = options.ignore;

// hint count
var targetFileCount = 0;
var targetHintFileCount = 0;
var targetHintCount = 0;

// json mode
Expand All @@ -119,7 +133,7 @@ function hintAllFiles(target, options, onFinised){
// init ruleset
var ruleset = options.ruleset;
if(ruleset === undefined){
ruleset = getConfig(program.config, target, json);
ruleset = getConfig(program.config, globInfo.base, json);
}

// hint queue
Expand All @@ -131,7 +145,7 @@ function hintAllFiles(target, options, onFinised){
arrJson.push({'file': filepath, 'messages': messages});
}
else{
console.log(' '+path.relative(process.cwd(), filepath).white);
console.log(' '+filepath.white);
messages.forEach(function(hint){
var leftWindow = 40;
var rightWindow = leftWindow + 20;
Expand Down Expand Up @@ -166,28 +180,22 @@ function hintAllFiles(target, options, onFinised){
});
console.log('');
}
targetFileCount ++;
targetHintFileCount ++;
targetHintCount += hintCount;
}
targetFileCount ++;
setImmediate(next);
}, 10);
// start hint
var isWalkDone = false;
var isHintDone = true;
var stats = fs.statSync(target);
if(stats.isDirectory()){
walkPath(target, function onPath(filepath){
isHintDone = false;
hintQueue.push(filepath);
}, function onFinised(){
isWalkDone = true;
checkAllHinted();
});
}
else{
walkPath(globInfo, function(filepath){
isHintDone = false;
hintQueue.push(filepath);
}, function(){
isWalkDone = true;
hintQueue.push(target);
}
checkAllHinted();
});
hintQueue.drain = function() {
isHintDone = true;
checkAllHinted();
Expand All @@ -196,27 +204,61 @@ function hintAllFiles(target, options, onFinised){
if(isWalkDone && isHintDone){
onFinised({
targetFileCount: targetFileCount,
targetHintFileCount: targetHintFileCount,
targetHintCount: targetHintCount,
arrJson: arrJson
});
}
}
}

// split target to base & glob
function getGlobInfo(target){
// fix windows sep
target = target.replace(/\\/g, '/');
var globInfo = parseGlob(target);
var base = globInfo.base;
base += /\/$/.test(base) ? '' : '/';
var pattern = globInfo.glob;
var globPath = globInfo.path;
var defaultGlob = '*.{htm,html}';
if(globInfo.is.glob === true){
// no basename
if(globPath.basename === ''){
pattern += defaultGlob;
}
}
else{
// no basename
if(globPath.basename === ''){
pattern += '**/' + defaultGlob;
}
// detect directory
else if(fs.existsSync(target) && fs.statSync(target).isDirectory()){
base += globPath.basename + '/';
pattern = '**/' + defaultGlob;
}
}
return {
base: base,
pattern: pattern
};
}

// search and load config
function getConfig(configFile, target, json){
if(configFile === undefined){
function getConfig(configFile, base, json){
if(configFile === undefined && fs.existsSync(base)){
// find default config file in parent directory
if(fs.statSync(target).isDirectory() === false){
target = path.dirname(target);
if(fs.statSync(base).isDirectory() === false){
base = path.dirname(base);
}
while(target){
var tmpConfigFile = path.resolve(target+path.sep, '.htmlhintrc');
while(base){
var tmpConfigFile = path.resolve(base+path.sep, '.htmlhintrc');
if(fs.existsSync(tmpConfigFile)){
configFile = tmpConfigFile;
break;
}
target = target.substring(0,target.lastIndexOf(path.sep));
base = base.substring(0,base.lastIndexOf(path.sep));
}
}

Expand All @@ -236,37 +278,29 @@ function getConfig(configFile, target, json){
}

// walk path
function walkPath(dir, callback, onFinish) {
fs.readdir(dir, function (err, files) {
var arrTasks = [];
files.forEach(function(file){
arrTasks.push(function(next){
var pathname = path.join(dir, file);
fs.stat(pathname, function (err, stats) {
if(stats){
if (stats.isDirectory()) {
if(/^(\.svn|\.git|\.build|node_modules)$/i.test(file) === false){
walkPath(pathname, callback, next);
}
else{
next();
}
} else {
if(/\.html?$/i.test(file)){
callback(path.normalize(pathname));
}
next();
}
}
else{
next();
}
});
});
});
async.series(arrTasks, function(){
onFinish && onFinish();
function walkPath(globInfo, callback, onFinish) {
var base = globInfo.base;
var pattern = globInfo.pattern;
var ignore = globInfo.ignore;
var arrIgnores = ['**/node_modules/**'];
if(ignore){
ignore.split(',').forEach(function(pattern){
arrIgnores.push(pattern);
});
}
var walk = glob(pattern, {
'cwd': base,
'dot': false,
'ignore': arrIgnores,
'nodir': true,
'strict': false,
'silent': true
},function() {
onFinish();
});
walk.on('match', function(file){
base = base.replace(/^.\//, '');
callback(base + file);
});
}

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"colors": "1.0.3",
"commander": "2.6.0",
"csslint": "0.10.0",
"glob": "5.0.15",
"jshint": "2.8.0",
"parse-glob": "3.0.4",
"strip-json-comments": "1.0.4"
},
"devDependencies": {
Expand Down

0 comments on commit a69029f

Please sign in to comment.