├── .gitignore
├── .jshintrc
├── .travis.yml
├── Gruntfile.js
├── LICENSE-MIT
├── README.md
├── package.json
├── tasks
├── html_validation.js
└── lib
│ └── remoteval.js
└── test
├── html
├── 404.html
├── about.html
├── home.html
└── index.html
└── html_validation_test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /_tempvlidation.html
3 | /node_modules/
4 | /npm-debug.log
5 | /validation-files.json
6 | /validation-report.json
7 | /validation-status.json
8 | tmp
9 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "curly": true,
3 | "eqeqeq": true,
4 | "immed": true,
5 | "indent": 4,
6 | "latedef": true,
7 | "newcap": true,
8 | "noarg": true,
9 | "node": true,
10 | "quotmark": "single",
11 | "undef": true,
12 | "unused": true
13 | }
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.8"
4 | - "0.10"
5 | before_install:
6 | - npm update npm -g
7 | - npm install -g grunt-cli
8 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*
2 | * grunt-html-validation
3 | * https://github.com/praveen/grunt-html-validation
4 | *
5 | * Copyright (c) 2013 - 2014 praveenvijayan
6 | * Licensed under the MIT license.
7 | */
8 |
9 | 'use strict';
10 |
11 | module.exports = function(grunt) {
12 |
13 | // Project configuration.
14 | grunt.initConfig({
15 | jshint: {
16 | all: [
17 | 'Gruntfile.js',
18 | 'tasks/**/*.js',
19 | '<%= nodeunit.tests %>'
20 | ],
21 | options: {
22 | jshintrc: '.jshintrc'
23 | }
24 | },
25 |
26 | // Before generating any new files, remove any previously-created files.
27 | clean: {
28 | tests: ['tmp']
29 | },
30 |
31 | // Unit tests.
32 | nodeunit: {
33 | tests: ['test/*_test.js']
34 | },
35 | validation: { // Grunt w3c validation plugin
36 | options: {
37 | reset: grunt.option('reset') || false,
38 | stoponerror:false,
39 | // remotePath: "http://decodize.com/",
40 | // remoteFiles: ["html/slidemote-universal-remote-control-for-html5-presentations",
41 | // "GAE/linktomob-share-your-links-quickly-and-easily-on-mobile-devices/",
42 | // "html/getting-started-with-yeoman-1-dot-0-beta-on-windows/",
43 | // "html/moving-from-wordpress-to-octopress/",
44 | // "css/site-preloading-methods/",
45 | // "html/sublime-text-2-bidirectional-language-support-plugin/"]
46 | // remoteFiles: "validation-files.json",
47 | relaxerror: ['Bad value X-UA-Compatible for attribute http-equiv on element meta.',
48 | 'Element title must not be empty.']
49 | },
50 | files: {
51 | src: ['test/html/*.html',
52 | '!test/html/index.html',
53 | '!test/html/404.html']
54 | }
55 | }
56 |
57 | });
58 |
59 | // Actually load this plugin's task(s).
60 | grunt.loadTasks('tasks');
61 |
62 | // These plugins provide necessary tasks.
63 | grunt.loadNpmTasks('grunt-contrib-jshint');
64 | grunt.loadNpmTasks('grunt-contrib-clean');
65 | grunt.loadNpmTasks('grunt-contrib-nodeunit');
66 | // grunt.loadNpmTasks('grunt-html-validation');
67 |
68 |
69 | // Whenever the 'test' task is run, first clean the 'tmp' dir, then run this
70 | // plugin's task(s), then test the result.
71 | grunt.registerTask('test', ['clean', 'jshint', 'nodeunit']);
72 |
73 | grunt.registerTask('multiple', ['validation', 'clean', 'nodeunit']);
74 |
75 | // By default, lint and run all tests.
76 | grunt.registerTask('default', ['test']);
77 |
78 | };
79 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 - 2014 praveenvijayan
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # grunt-html-validation [](https://travis-ci.org/praveenvijayan/grunt-html-validation)
2 |
3 | [](https://nodei.co/npm/grunt-html-validation/)
4 |
5 | > W3C html validation grunt plugin. Validate all files in a directory automatically.
6 |
7 | ## Getting Started
8 | This plugin requires Grunt `~0.4.1`
9 |
10 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:
11 |
12 | ```shell
13 | npm install grunt-html-validation --save-dev
14 | ```
15 |
16 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:
17 |
18 | ```js
19 | grunt.loadNpmTasks('grunt-html-validation');
20 | ```
21 |
22 | And add to your task list using `validation`:
23 |
24 | ```js
25 | grunt.registerTask('default', ['validation']);
26 | ```
27 |
28 | ## The "validation" task
29 |
30 | ### Overview
31 | In your project's Gruntfile, add a section named `validation` to the data object passed into `grunt.initConfig()`.
32 |
33 | ```js
34 | validation: {
35 | options: {
36 | reset: grunt.option('reset') || false,
37 | stoponerror: false,
38 | remotePath: 'http://decodize.com/',
39 | remoteFiles: ['html/moving-from-wordpress-to-octopress/',
40 | 'css/site-preloading-methods/'], //or
41 | remoteFiles: 'validation-files.json', // JSON file contains array of page paths.
42 | relaxerror: ['Bad value X-UA-Compatible for attribute http-equiv on element meta.'] //ignores these errors
43 | },
44 | files: {
45 | src: ['<%= yeoman.app %>/*.html',
46 | '!<%= yeoman.app %>/index.html',
47 | '!<%= yeoman.app %>/modules.html',
48 | '!<%= yeoman.app %>/404.html']
49 | }
50 | }
51 | ```
52 |
53 | ### Options
54 |
55 | #### options.reset
56 | Type: `Boolean`
57 | Default value: `'false'`
58 |
59 | Resets all the validated files status. When want to revalidate all the validated files -
60 | `eg: sudo grunt validate --reset=true`
61 |
62 | #### options.proxy
63 | Type: `String`
64 | Default value: `null`
65 |
66 | Setup your proxy when you are behind a corporate proxy and encounters `ETIMEDOUT`.
67 |
68 | ```js
69 | proxy: 'http://proxy:8080'
70 | ```
71 |
72 | #### options.serverUrl
73 | Type: `String`
74 | Default value: `null`
75 |
76 | Supply a different validator server URL, for instance [if you run a local server](http://validator.w3.org/source/).
77 | Eg: `http://localhost/w3c-validator/check`
78 |
79 | #### options.path
80 | Type: `String`
81 | Default value: `'validation-status.json'`
82 |
83 | Default file for storing validation information.
84 |
85 | #### options.reportpath
86 | Type: `String`
87 | Default value: `validation-report.json`
88 |
89 | Consolidated report in JSON format, if reportpath is `false` it will not generated.
90 |
91 | #### options.stoponerror
92 | Type: `Boolean`
93 | Default value: `false`
94 |
95 | When hit by a validation error, html-validator continue validating next file by default and this process continues until all files in the list completes validation. If 'stoponerror' set to `true`, validator will stop validating next file.
96 |
97 | #### options.maxTry
98 | Type: `Number`
99 | Default value: `3`
100 |
101 | Number of retries when network error occuers. Default case, after 3 reties validator will move to next file.
102 |
103 | #### options.remotePath
104 | Type: `String`
105 | Default value: ``
106 |
107 | #### options.wrapfile
108 | Type: `String`
109 | Default value: ``
110 |
111 | File that will wrap your files inside.
112 |
113 | The file must contain a comment that will be replaced by each file content: **<!-- CONTENT -->**
114 |
115 | Useful to validate partials because w3c validator need <html>, <head>, <body>...
116 |
117 | Note: line reported will be the partial line, if you see a negative number this means that the error is in the *wrapfile*.
118 |
119 | example
120 |
121 | ```html
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | ```
131 |
132 |
133 |
134 | #### options.remoteFiles
135 | Type: `Array`
136 | Default value: ``
137 |
138 | Array of page paths to be validated. When remote files are not present validator will append file names from local folder. `remotePath` is mandatory when this option is specified.
139 |
140 | eg:
141 |
142 | ```js
143 | remoteFiles: ['html/moving-from-wordpress-to-octopress/',
144 | 'css/site-preloading-methods/']
145 | ```
146 |
147 | you can also provide a file that contains an array of pages.
148 |
149 | ```js
150 | remoteFiles: 'validation-files.json'
151 | ```
152 |
153 | ```js
154 | ['html/getting-started-with-yeoman-1-dot-0-beta-on-windows',
155 | 'html/slidemote-universal-remote-control-for-html5-presentations/',
156 | 'html/simple-responsive-image-technique/']
157 | ```
158 |
159 | #### options.relaxerror
160 | Type: `Array`
161 | Default value: ``
162 |
163 | Helps to skip certain w3c errors messages from validation. Give exact error message or a regular expression in an array & validator will ignore those relaxed errors from validation.
164 |
165 | ```js
166 | relaxerror: ['Bad value X-UA-Compatible for attribute http-equiv on element meta.',
167 | 'document type does not allow element "[A-Z]+" here']
168 | ```
169 |
170 | #### options.doctype
171 | Type: `String`
172 | Default value: `false`
173 |
174 | Set `false` for autodetect or chose one of this options:
175 |
176 | - `HTML5`
177 | - `XHTML 1.0 Strict`
178 | - `XHTML 1.0 Transitional`
179 | - `XHTML 1.0 Frameset`
180 | - `HTML 4.01 Strict`
181 | - `HTML 4.01 Transitional`
182 | - `HTML 4.01 Frameset`
183 | - `HTML 4.01 + RDFa 1.1`
184 | - `HTML 3.2`
185 | - `HTML 2.0`
186 | - `ISO/IEC 15445:2000 ("ISO HTML")`
187 | - `XHTML 1.1`
188 | - `XHTML + RDFa`
189 | - `XHTML Basic 1.0`
190 | - `XHTML Basic 1.1`
191 | - `XHTML Mobile Profile 1.2`
192 | - `XHTML-Print 1.0`
193 | - `XHTML 1.1 plus MathML 2.0`
194 | - `XHTML 1.1 plus MathML 2.0 plus SVG 1.1`
195 | - `MathML 2.0`
196 | - `SVG 1.0`
197 | - `SVG 1.1`
198 | - `SVG 1.1 Tiny`
199 | - `SVG 1.1 Basic`
200 | - `SMIL 1.0`
201 | - `SMIL 2.0`
202 |
203 |
204 | #### options.charset
205 | Type: `String`
206 | Default value: `false`
207 |
208 | Set `false` for autodetect or chose one of this options:
209 |
210 | - `utf-8`
211 | - `utf-16`
212 | - `iso-8859-1`
213 | - `iso-8859-2`
214 | - `iso-8859-3`
215 | - `iso-8859-4`
216 | - `iso-8859-5`
217 | - `iso-8859-6-i`
218 | - `iso-8859-7`
219 | - `iso-8859-8`
220 | - `iso-8859-8-i`
221 | - `iso-8859-9`
222 | - `iso-8859-10`
223 | - `iso-8859-11`
224 | - `iso-8859-13`
225 | - `iso-8859-14`
226 | - `iso-8859-15`
227 | - `iso-8859-16`
228 | - `us-ascii`
229 | - `euc-jp`
230 | - `shift_jis`
231 | - `iso-2022-jp`
232 | - `euc-kr`
233 | - `gb2312`
234 | - `gb18030`
235 | - `big5`
236 | - `big5-HKSCS`
237 | - `tis-620`
238 | - `koi8-r`
239 | - `koi8-u`
240 | - `iso-ir-111`
241 | - `macintosh`
242 | - `windows-1250`
243 | - `windows-1251`
244 | - `windows-1252`
245 | - `windows-1253`
246 | - `windows-1254`
247 | - `windows-1255`
248 | - `windows-1256`
249 | - `windows-1257`
250 |
251 | #### options.failHard
252 | Type: `boolean`
253 | Default value: `false`
254 |
255 | If true, the task will fail at the end of its run if there were any validation errors that were not ignored via `options.relaxerror`.
256 |
257 | ## Contributing
258 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).
259 |
260 | Report issues [here](https://github.com/praveenvijayan/grunt-html-validation/issues)
261 |
262 | ## Release History
263 | * 2014-05-27 v0.1.18 Version bump, Fixes #54
264 | * 2014-05-15 v0.1.17 Fixes #50, #52
265 | * 2014-04-23 v0.1.16 Fixes
266 | * 2014-04-03 v0.1.15 Updated dependencies (jshnt, nodeunit, request), gitignore, code cleanup etc..
267 | * 2014-03-23 v0.1.14 Updated with wrapfile & server url options.
268 | * 2013-12-26 v0.1.13 Fixed running multiple tasks fail due to validation failure.
269 | * 2013-12-17 v0.1.11 Option to set proxy, w3cjs updated to 0.1.22, added fail hard and some bug fixes
270 | * 2013-11-22 v0.1.9 Fix some bugs
271 | * 2013-11-22 v0.1.8 Added options for specify doctype and charset
272 | * 2013-11-22 v0.1.7 Added support for RegExp in relaxed validation
273 | * 2013-08-31 v0.1.6 Added relaxed validation, w3cjs updated from 0.1.9 to 0.1.10.
274 | * 2013-08-31 v0.1.5 Added remote validation support. Max network error retry count.
275 | * 2013-08-19 v0.1.4 Fixed issues. Added 'stoponerror' option, validation report added.
276 | * 2013-08-05 v0.1.2 Fixed issues.
277 | * 2013-04-20 v0.1.0 Initial release.
278 |
279 |
280 | [](https://bitdeli.com/free "Bitdeli Badge")
281 |
282 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grunt-html-validation",
3 | "description": "W3C html validation grunt plugin",
4 | "version": "0.1.18",
5 | "homepage": "https://github.com/praveenvijayan/grunt-html-validation",
6 | "author": "praveenvijayan (http://decodize.com)",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/praveenvijayan/grunt-html-validation.git"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/praveenvijayan/grunt-html-validation/issues"
13 | },
14 | "licenses": [
15 | {
16 | "type": "MIT",
17 | "url": "https://github.com/praveenvijayan/grunt-html-validation/blob/master/LICENSE-MIT"
18 | }
19 | ],
20 | "engines": {
21 | "node": ">= 0.8.0"
22 | },
23 | "scripts": {
24 | "test": "grunt test"
25 | },
26 | "peerDependencies": {
27 | "grunt": "~0.4.1"
28 | },
29 | "dependencies": {
30 | "chalk": "^0.5.1",
31 | "colors": "~0.6.2",
32 | "request": "~2.40.0",
33 | "w3cjs": "~0.1.25"
34 | },
35 | "devDependencies": {
36 | "grunt": "~0.4.5",
37 | "grunt-contrib-clean": "~0.6.0",
38 | "grunt-contrib-jshint": "~0.10.0",
39 | "grunt-contrib-nodeunit": "~0.4.1"
40 | },
41 | "keywords": [
42 | "gruntplugin"
43 | ],
44 | "files": [
45 | "tasks",
46 | "LICENSE-MIT"
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/tasks/html_validation.js:
--------------------------------------------------------------------------------
1 | /*
2 | * grunt-html-validation
3 | * https://github.com/praveenvijayan/grunt-html-validation
4 | *
5 | * Copyright (c) 2013 - 2014 Praveen Vijayan
6 | * Licensed under the MIT license.
7 | */
8 |
9 | 'use strict';
10 |
11 | module.exports = function (grunt) {
12 |
13 | var w3cjs = require('w3cjs');
14 | var colors = require('colors');
15 | var chalk = require('chalk');
16 | var rval = require('./lib/remoteval');
17 |
18 | colors.setTheme({
19 | silly: 'rainbow',
20 | input: 'grey',
21 | verbose: 'cyan',
22 | prompt: 'grey',
23 | info: 'green',
24 | data: 'grey',
25 | help: 'cyan',
26 | warn: 'yellow',
27 | debug: 'blue',
28 | error: 'red',
29 | blue: 'blue'
30 | });
31 |
32 | var counter = 0,
33 | msg = {
34 | error: 'Something went wrong',
35 | ok: 'Validation successful..',
36 | start: 'Validation started for.. '.info,
37 | networkError: 'Network error re-validating..'.error,
38 | validFile: 'Validated skipping..',
39 | nofile: ':- No file is specified in the path!',
40 | nextfile: 'Skipping to next file..'.verbose,
41 | eof: 'End of File..'.verbose,
42 | fileNotFound: 'File not found..'.error,
43 | remotePathError: 'Remote path '.error + '(options->remotePath) '.grey +
44 | 'is mandatory when remote files '.error +
45 | '(options-> remoteFiles) '.grey + 'are specified!'.error
46 | },
47 | len,
48 | reportArry = [],
49 | retryCount = 0,
50 | reportFilename = '';
51 |
52 | grunt.registerMultiTask('validation', 'HTML W3C validation.', function () {
53 | // Merge task-specific and/or target-specific options with these defaults.
54 | var options = this.options({
55 | path: 'validation-status.json',
56 | reportpath: 'validation-report.json',
57 | reset: false,
58 | proxy: null,
59 | stoponerror: false,
60 | failHard: false,
61 | remotePath: false,
62 | maxTry: 3,
63 | relaxerror: [],
64 | doctype: false, // Defaults false for autodetect
65 | charset: false // Defaults false for autodetect
66 | });
67 |
68 | var done = this.async(),
69 | files = grunt.file.expand(this.filesSrc),
70 | flen = files.length,
71 | readSettings = {},
72 | isRelaxError = false;
73 |
74 | isRelaxError = options.relaxerror.length && options.relaxerror.length !== '';
75 |
76 | var makeFileList = function (files) {
77 | return files.map(function (file) {
78 | return options.remotePath + file;
79 | });
80 | };
81 |
82 | //Reset current validation status and start from scratch.
83 | if (options.reset) {
84 | grunt.file.write(options.path, '{}');
85 | }
86 |
87 | if (!flen) {
88 | var nomsg = this.data.src;
89 | console.log(nomsg + msg.nofile.error);
90 | }
91 |
92 | var addToReport = function (fname, status) {
93 | var relaxedReport = [];
94 |
95 | for (var i = 0; i < status.length; i++) {
96 | if (!checkRelaxError(status[i].message)) {
97 | relaxedReport.push(status[i]);
98 | }
99 | }
100 |
101 | var report = {};
102 | report.filename = fname;
103 | report.error = relaxedReport;
104 | reportArry.push(report);
105 | };
106 |
107 | var wrapfile,
108 | wrapfile_line_start = 0;
109 | var validate = function (files) {
110 | if (files.length) {
111 | // fix: Fatal error: Unable to read 'undefined' file (Error code: ENOENT).
112 | if (!files[counter]) {
113 | done();
114 | return;
115 | }
116 |
117 |
118 | if (grunt.file.exists(options.path)) {
119 | readSettings = grunt.file.readJSON(options.path);
120 | }
121 | var currFileStat = readSettings[files[counter]] || false;
122 |
123 | if (currFileStat) {
124 | console.log(msg.validFile.green + files[counter]);
125 | reportFilename = options.remoteFiles ? dummyFile[counter] : files[counter];
126 | addToReport(reportFilename, false);
127 | counter++;
128 | validate(files);
129 | done();
130 | return;
131 | }
132 |
133 | if (files[counter] !== undefined) {
134 |
135 | var filename = options.remoteFiles ? dummyFile[counter] : files[counter];
136 |
137 | console.log(msg.start + filename);
138 | }
139 |
140 | var w3cjs_options = {
141 | //file: files[counter], // file can either be a local file or a remote file
142 | // file: 'http://localhost:9001/010_gul006_business_landing_o2_v11.html',
143 | output: 'json', // Defaults to 'json', other option includes html
144 | doctype: options.doctype, // Defaults false for autodetect
145 | charset: options.charset, // Defaults false for autodetect
146 | proxy: options.proxy, // Proxy to pass to the w3c library
147 | callback: function (res) {
148 |
149 | flen = files.length;
150 |
151 | if (!res.messages) {
152 | ++retryCount;
153 | var netErrorMsg = msg.networkError + ' ' + retryCount.toString().error + ' ';
154 | if (retryCount === options.maxTry) {
155 | counter++;
156 | if (counter !== flen) {
157 | netErrorMsg += msg.nextfile;
158 | } else {
159 | netErrorMsg += msg.eof;
160 | }
161 | retryCount = 0;
162 | }
163 |
164 | console.log(netErrorMsg);
165 | validate(files);
166 | return;
167 | }
168 |
169 | len = res.messages.length;
170 |
171 | var setGreen = function () {
172 | readSettings[files[counter]] = true;
173 | grunt.log.ok(msg.ok.green);
174 |
175 | reportFilename = options.remoteFiles ? dummyFile[counter] : files[counter];
176 | addToReport(reportFilename, false);
177 | };
178 |
179 | if (len) {
180 | var errorCount = 0,
181 | prop;
182 |
183 | for (prop in res.messages) {
184 | res.messages[prop].unwrapLine = res.messages[prop].lastLine - wrapfile_line_start;
185 | }
186 |
187 | for (prop in res.messages) {
188 | var chkRelaxError;
189 | if (isRelaxError) {
190 | chkRelaxError = checkRelaxError(res.messages[prop].message);
191 | }
192 |
193 | if (!chkRelaxError) {
194 | errorCount = errorCount + 1;
195 |
196 | var lineNumber = ' Line no: ' + JSON.stringify(options.wrapfile ? res.messages[prop].unwrapLine : res.messages[prop].lastLine);
197 | if (typeof(prompt) !== 'undefined') {
198 | lineNumber = lineNumber.prompt;
199 | }
200 |
201 | console.log(errorCount + '=> '.warn + JSON.stringify(res.messages[prop].message).help + lineNumber );
202 | }
203 |
204 | }
205 |
206 | if (errorCount !== 0) {
207 | console.log('No of errors: '.error + errorCount);
208 | }
209 |
210 | readSettings[files[counter]] = false;
211 | reportFilename = options.remoteFiles ? dummyFile[counter] : files[counter];
212 | addToReport(reportFilename, res.messages);
213 |
214 | if (options.stoponerror) {
215 | done();
216 | return;
217 | }
218 |
219 | if (isRelaxError && errorCount === 0) {
220 | setGreen();
221 | }
222 |
223 | } else {
224 |
225 | setGreen();
226 |
227 | }
228 |
229 | grunt.file.write(options.path, JSON.stringify(readSettings));
230 | // depending on the output type, res will either be a json object or a html string
231 | counter++;
232 |
233 | if (counter === flen) {
234 | if (options.reportpath) {
235 | grunt.file.write(options.reportpath, JSON.stringify(reportArry));
236 | console.log('Validation report generated: '.green + options.reportpath);
237 | }
238 | if (options.failHard) {
239 | var validationErrCount = reportArry.reduce(function (sum, report) {
240 | return sum + report.error.length;
241 | }, 0);
242 | if (validationErrCount > 0) {
243 | grunt.fail.warn(validationErrCount + ' total unignored HTML validation error' + grunt.util.pluralize(validationErrCount, '/s') + '.');
244 | }
245 | }
246 | done();
247 | if (!options.remoteFiles) {
248 | return;
249 | }
250 | }
251 |
252 | if (options.remoteFiles) {
253 | if (counter === flen) {
254 | return;
255 | }
256 |
257 | rval(dummyFile[counter], function () {
258 | validate(files);
259 | });
260 |
261 | } else {
262 | validate(files);
263 | }
264 | }
265 | };
266 |
267 | if (options.wrapfile) {
268 | if (!wrapfile) {
269 | wrapfile = grunt.file.read(options.wrapfile);
270 | wrapfile_line_start = wrapfile.substring(0, wrapfile.indexOf('')).split('\n').length - 1;
271 | }
272 |
273 | w3cjs_options.input = wrapfile.replace('', grunt.file.read(files[counter]));
274 | } else {
275 | w3cjs_options.file = files[counter];
276 | }
277 |
278 | // override default server
279 | if (options.serverUrl) {
280 | w3cjs.setW3cCheckUrl(options.serverUrl);
281 | }
282 |
283 | w3cjs.validate(w3cjs_options);
284 | }
285 | };
286 |
287 | function checkRelaxError(error) {
288 | for (var i = 0, l = options.relaxerror.length; i < l; i++) {
289 | var re = new RegExp(options.relaxerror[i], 'g');
290 | if (re.test(error)) {
291 | return true;
292 | }
293 | }
294 | }
295 |
296 | /* Remote validation
297 | * Note on Remote validation.
298 | * W3Cjs supports remote file validation but due to some reasons it is not working as expected.
299 | * Local file validation is working perfectly. To overcome this remote page is fetch using 'request'
300 | * npm module and write page content in '_tempvlidation.html' file and validates as local file.
301 | */
302 |
303 | if (!options.remotePath && options.remoteFiles) {
304 | console.log(msg.remotePathError);
305 | return;
306 | }
307 |
308 | if (options.remotePath && options.remotePath !== '') {
309 | files = makeFileList(files);
310 | }
311 |
312 | if (options.remoteFiles) {
313 |
314 | if (typeof options.remoteFiles === 'object' && options.remoteFiles.length && options.remoteFiles[0] !== '') {
315 | files = options.remoteFiles;
316 |
317 | } else {
318 | files = grunt.file.readJSON(options.remoteFiles);
319 | }
320 |
321 | files = makeFileList(files);
322 |
323 | var dummyFile = files;
324 |
325 | files = [];
326 |
327 | for (var i = 0; i < dummyFile.length; i++) {
328 | files.push('_tempvlidation.html');
329 | }
330 |
331 | rval(dummyFile[counter], function () {
332 | validate(files);
333 | });
334 |
335 | return;
336 | }
337 |
338 | if (!options.remoteFiles) {
339 | validate(files);
340 | }
341 |
342 | });
343 |
344 | };
345 |
--------------------------------------------------------------------------------
/tasks/lib/remoteval.js:
--------------------------------------------------------------------------------
1 | /*
2 | * grunt-html-validation remote validation helper
3 | * https://github.com/praveen/grunt-html-validation
4 | *
5 | * Copyright (c) 2013 - 2014 Praveen Vijayan
6 | * Licensed under the MIT license.
7 | */
8 |
9 | /* global fileNotFound */
10 |
11 | 'use strict';
12 |
13 | module.exports = function remoteval (file, cb) {
14 |
15 | var request = require('request');
16 | var grunt = require('grunt');
17 |
18 | request(file, function (error, response, body) {
19 | if (response.statusCode === 404) {
20 | console.log(fileNotFound);
21 | }
22 |
23 | if (!error && response.statusCode === 200) {
24 | grunt.file.write('_tempvlidation.html', body);
25 | return cb(true);
26 | }
27 | });
28 |
29 | };
30 |
--------------------------------------------------------------------------------
/test/html/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/test/html/about.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | New file
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/html/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | New file
18 | strong
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/html_validation_test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.validation = function(test){
4 | test.expect(1);
5 | test.ok(true, 'initialize testing');
6 | test.done();
7 | };
8 |
--------------------------------------------------------------------------------