Skip to content

Commit

Permalink
1.4.0 - add lint format
Browse files Browse the repository at this point in the history
  • Loading branch information
WezomDev committed Nov 16, 2019
1 parent b297495 commit 697daed
Show file tree
Hide file tree
Showing 8 changed files with 439 additions and 15 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
[![npm](https://img.shields.io/badge/npm-install-orange.svg)](https://www.npmjs.com/package/node-w3c-validator)
[![license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/dutchenkoOleg/node-w3c-validator/blob/master/LICENSE)
[![Build Status](https://travis-ci.org/dutchenkoOleg/node-w3c-validator.svg?branch=master)](https://travis-ci.org/dutchenkoOleg/node-w3c-validator)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)

> _Wrapper for [The Nu Html Checker (v.Nu)](https://www.npmjs.com/package/vnu-jar)_
Expand Down Expand Up @@ -70,7 +69,14 @@ Makes the checker exit zero even if errors are reported for any documents
Specifies the output format for reporting the results

default: `unset`
possible values: `gnu | xml | json | text | html`
possible values: `gnu | xml | json | text | html | lint`

> `lint` format is available from 1.4.0 version.
> `lint` format is designed for convenient error output to the terminal.

![lint format screenshot](./tests/assets/lint-format-screenshot.png)


#### `--filterfile <filename>`

Expand Down
29 changes: 23 additions & 6 deletions bin/cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ const nodeW3CValidator = require('../lib/validator');
program
.version(pkg.version)
.usage('[options] <pattern>')
.option('-i, --input [path]', 'Validate input path')
.option(
'-i, --input [path]',
'Validate input path'
)
.option(
'-a, --asciiquotes',
'Specifies whether ASCII quotation marks are substituted for Unicode smart quotation marks in messages.'
Expand All @@ -27,7 +30,9 @@ program
'-e, --errors-only',
'Specifies that only error-level messages and non-document-error messages are reported (so that warnings and info messages are not reported)'
)
.option('-q, --exit-zero-always', 'Makes the checker exit zero even if errors are reported for any documents')
.option(
'-q, --exit-zero-always',
'Makes the checker exit zero even if errors are reported for any documents')
.option(
'--filterfile [filename]',
'Specifies a filename. Each line of the file contains either a regular expression or starts with "#" to indicate the line is a comment. Any error message or warning message that matches a regular expression in the file is filtered out (dropped/suppressed)'
Expand All @@ -36,9 +41,18 @@ program
'--filterpattern [pattern]',
'Specifies a regular-expression pattern. Any error message or warning message that matches the pattern is filtered out (dropped/suppressed)'
)
.option('-f, --format [gnu|xml|json|text|html]', 'Specifies the output format for reporting the results')
.option('-s, --skip-non-html', 'Skip documents that don’t have *.html, *.htm, *.xhtml, or *.xht extensions.')
.option('-H, --html', 'Forces any *.xhtml or *.xht documents to be parsed using the HTML parser')
.option(
'-f, --format [gnu|xml|json|text|html|lint]',
'Specifies the output format for reporting the results'
)
.option(
'-s, --skip-non-html',
'Skip documents that don’t have *.html, *.htm, *.xhtml, or *.xht extensions.'
)
.option(
'-H, --html',
'Forces any *.xhtml or *.xht documents to be parsed using the HTML parser'
)
.option(
'--no-langdetect',
'Disables language detection, so that documents are not checked for missing or mislabeled html[lang] attributes.'
Expand All @@ -51,7 +65,10 @@ program
'-v, --verbose',
'Specifies "verbose" output (currently this just means that the names of files being checked are written to stdout)'
)
.option('-o, --output [path]', 'Write reporting result to the path')
.option(
'-o, --output [path]',
'Write reporting result to the path'
)
.option(
'-b, --buffersize <size>',
'1024 * <size> Increase maxBuffer size for child_process.exec, if result output truncated'
Expand Down
180 changes: 180 additions & 0 deletions lib/lint-format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// -----------------------------------------------------------------------------
// Deps
// -----------------------------------------------------------------------------

const path = require('path');
const chalk = require('chalk');
const eslintFormatter = require('eslint-formatter-pretty');

/**
* @param {string} filePath
* @returns {string}
*/
const getFilePath = (filePath) => {
if (/^file:/.test(filePath)) {
filePath = '.' + path.normalize(filePath)
.replace(/^file:[\\/]/, '')
.replace(process.cwd(), '')
.replace(/\\/g, '/');
}
return filePath;
};

/**
* @param message
* @returns {{warn: number, nonDocument: boolean, error: number, fatal: boolean}}
*/
const hasProblems = (message) => {
const result = {
nonDocument: false,
fatal: false,
warn: 0,
error: 0
};
switch (message.type) {
case 'info':
if (message.subType === 'warning') {
result.warn = 1;
}
break;
case 'error':
if (message.subType === 'fatal') {
result.fatal = true;
}
result.error = 1;
break;
case 'non-document-error':
result.nonDocument = true;
result.warn = 1;
break;
}
return result;
};

/**
* @param {Message[]} messages
* @return {Object<EslintResult>} files
*/
const parseMessages = (messages) => {
const files = {};
messages.forEach((message) => {
const { url } = message;
const { warn, error, fatal, nonDocument } = hasProblems(message);
if (warn || error) {
if (!files.hasOwnProperty(url)) {
const filePath = getFilePath(message.url);
files[url] = {
filePath,
messages: [],
errorCount: 0,
warningCount: 0
};
}
files[url].messages.push({
fatal,
ruleId: (message.message || 'Unknown error').replace(/(^\s+|\s+$)/, ''),
message: nonDocument ? 'Non Document Error' : error ? chalk.redBright('Error') : chalk.yellow('Warning'),
severity: error ? 2 : 1,
source: message.extract,
line: message.firstLine || message.lastLine,
column: message.firstColumn || message.lastColumn
});
files[url].errorCount += error;
files[url].warningCount += warn;
}
});
return files;
};

/**
* @param {OutputJSONFormat} json
* @returns {EslintReport} result
*/
const transformW3CtoEslint = (json) => {
const files = parseMessages(json.messages);
const report = {
results: [],
errorCount: 0,
warningCount: 0
};
for (let url in files) {
if (files.hasOwnProperty(url)) {
const file = files[url];
report.results.push(file);
report.errorCount += file.errorCount;
report.warningCount += file.warningCount;
}
}
return report;
};

/**
* @param {OutputJSONFormat} json
*/
const lintFormat = (json) => {
const report = transformW3CtoEslint(json);
return eslintFormatter(report.results);
};

module.exports = lintFormat;

// -----------------------------------------------------------------------------
// Defionitions
// -----------------------------------------------------------------------------

/**
* @typedef {Object} OutputJSONFormat
* @see {@link https://github.com/validator/validator/wiki/Output-%C2%BB-JSON}
* @property {Message[]} messages
* @property {string} [url]
* @property {Source} [source]
* @property {string} [language]
*/

/**
* @typedef {Object} Message
* @see {@link https://github.com/validator/validator/wiki/Output-%C2%BB-JSON#message-objects}
* @property {'info' | 'error' | 'non-document-error'} type
* @property {'warning' | 'fatal' | 'io' | 'schema' | 'internal'} [subType]
* @property {string} [message]
* @property {string} [extract]
* @property {number} [offset]
* @property {string} [url]
* @property {number} [firstLine]
* @property {number} [firstColumn]
* @property {number} [lastLine]
* @property {number} [lastColumn]
*/

/**
* @typedef {Object} Source
* @property {string} code
* @property {string} type
* @property {string} encoding
*/

/**
* @typedef {Object} EslintReport
* @property {EslintResult[]} results
* @param {number} errorCount
* @param {number} warningCount
*/

/**
* @typedef {Object} EslintResult
* @param {string} filePath
* @param {EslintMessage[]} messages
* @param {number} errorCount
* @param {number} warningCount
*/

/**
* @typedef {Object} EslintMessage
* @property {boolean} [fatal]
* @property {string} [ruleId]
* @property {1|2} [severity]
* @property {number} [line]
* @property {number} [column]
* @property {string} [message]
* @property {string} [source]
*/
16 changes: 14 additions & 2 deletions lib/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const mkdirp = require('mkdirp');
const fileset = require('fileset');
const exec = require('child_process').exec;
Expand All @@ -22,6 +23,7 @@ const ora = require('ora');
const pkg = require('../package.json');

const renderHtml = require('./render-html');
const lintFormat = require('./lint-format');

// ----------------------------------------
// Private
Expand Down Expand Up @@ -81,6 +83,10 @@ function getOptions (userOptions = {}) {
options.outputAsHtml = true;
options.verbose = true;
options.format = 'json';
} else if (options.format === 'lint') {
options.outputAsLint = true;
options.verbose = true;
options.format = 'json';
}

return options;
Expand Down Expand Up @@ -157,8 +163,14 @@ function nodeW3CValidator (validationPath, userOptions, done) {
spinner.succeed(`${pkg.name} - no errors`);
} else {
output = stderr;
if (options.outputAsHtml) {
output = renderHtml(output);
try {
if (options.outputAsHtml) {
output = renderHtml(output);
} else if (options.outputAsLint) {
output = lintFormat(JSON.parse(output));
}
} catch (e) {
console.log(chalk.red(e.message));
}
spinner.fail(`${pkg.name} - found errors`);
}
Expand Down
Loading

0 comments on commit 697daed

Please sign in to comment.