1111const fs = require ( 'fs' )
1212const chalk = require ( 'chalk' )
1313const libtidy = require ( 'libtidy' )
14+ const mkdirp = require ( 'mkdirp' )
15+ const path = require ( 'path' )
1416
1517module . exports = function ( grunt ) {
1618 grunt . registerMultiTask ( 'tidy-html5' , 'Checks and fixes HTML files using tidy-html5.' , function ( ) {
1719 const done = this . async ( )
1820 const options = this . options ( {
1921 force : false ,
2022 quiet : false ,
23+ report : '' ,
2124 ignoreMissing : false ,
2225 tidyOptions : { }
2326 } )
2427 const force = options . force
2528 const quiet = options . quiet
29+ const report = options . report
2630 const ignoreMissing = options . ignoreMissing
2731 const tidyOptions = options . tidyOptions
2832 const files = this . files
2933 const warn = force ? grunt . log . warn : grunt . fail . warn
34+ const reports = [ ]
3035 let processed = 0
3136 let failed = 0
3237
@@ -48,22 +53,23 @@ module.exports = function (grunt) {
4853
4954 files . reduce ( function ( previous , file ) {
5055 return previous . then ( function ( ) {
51- return file . src . reduce ( process , Promise . resolve ( ) )
56+ return file . src . reduce ( processFile , Promise . resolve ( ) )
5257 } )
5358 } , Promise . resolve ( ) )
5459 . then ( function ( ) {
5560 const ok = failed ? force ? grunt . log . warn : grunt . fail . warn
5661 : grunt . log . ok
5762 ok ( processed + ' ' + grunt . util . pluralize ( processed ,
5863 'file/files' ) + ' processed, ' + failed + ' failed.' )
64+ return writeReport ( )
5965 } , function ( error ) {
6066 grunt . verbose . error ( error . stack )
6167 grunt . log . error ( error )
6268 warn ( 'Processing HTML files failed.' )
6369 } )
6470 . then ( done )
6571
66- function process ( previous , source ) {
72+ function processFile ( previous , source ) {
6773 return previous . then ( function ( ) {
6874 const document = libtidy . TidyDoc ( )
6975 document . options = tidyOptions
@@ -82,13 +88,101 @@ module.exports = function (grunt) {
8288 return document . parseBuffer ( buffer )
8389 } )
8490 . then ( function ( result ) {
85- const errors = result . errlog
86- if ( errors . length ) {
91+ const messages = result . errlog
92+ if ( messages . length ) {
8793 if ( ! quiet ) {
88- grunt . log . write ( errors )
94+ grunt . log . write ( messages )
8995 }
9096 ++ failed
9197 }
98+ return addReport ( source , messages )
99+ } )
100+ } )
101+ }
102+
103+ function writeReport ( ) {
104+ function writeReport ( ) {
105+ return writeFile ( report , JSON . stringify ( reports ) )
106+ }
107+
108+ if ( report ) {
109+ const directory = path . dirname ( report )
110+ grunt . verbose . writeln ( 'Writing report to "' + chalk . cyan ( report ) + '".' )
111+ if ( directory ) {
112+ return ensureDirectory ( directory ) . then ( writeReport )
113+ }
114+ return writeReport ( )
115+ }
116+ }
117+
118+ function addReport ( source , messages ) {
119+ if ( messages ) {
120+ return readFile ( source ) . then ( function ( content ) {
121+ reportFile ( source , content , messages )
122+ } )
123+ }
124+ }
125+
126+ function reportFile ( name , content , messages ) {
127+ const contentLines = content . split ( / \r ? \n / )
128+ messages . split ( / \r ? \n / ) . forEach ( function ( line ) {
129+ const message = parseMessage ( line )
130+ const place = contentLines [ message . lastLine - 1 ] || ''
131+ message . extract = place . substr ( message . firstColumn - 1 )
132+ message . hiliteLength = message . hiliteStart = 0
133+ message . file = name
134+ reports . push ( message )
135+ } )
136+ }
137+
138+ function parseMessage ( message ) {
139+ const parsed = / ^ l i n e ( \d + ) c o l u m n ( \d + ) - ( \w + ) : / . exec ( message )
140+ var column
141+ if ( parsed ) {
142+ column = parseInt ( parsed [ 2 ] )
143+ return parsed && {
144+ type : parsed [ 3 ] . toLowerCase ( ) ,
145+ firstColumn : column ,
146+ lastColumn : column ,
147+ lastLine : parseInt ( parsed [ 1 ] ) ,
148+ message : message . substr ( parsed [ 0 ] . length + 1 )
149+ }
150+ }
151+ return { }
152+ }
153+
154+ function ensureDirectory ( name ) {
155+ return new Promise ( function ( resolve , reject ) {
156+ mkdirp ( name , function ( error ) {
157+ if ( error ) {
158+ reject ( error )
159+ } else {
160+ resolve ( )
161+ }
162+ } )
163+ } )
164+ }
165+
166+ function writeFile ( name , content ) {
167+ return new Promise ( function ( resolve , reject ) {
168+ fs . writeFile ( name , content , function ( error ) {
169+ if ( error ) {
170+ reject ( error )
171+ } else {
172+ resolve ( )
173+ }
174+ } )
175+ } )
176+ }
177+
178+ function readFile ( name ) {
179+ return new Promise ( function ( resolve , reject ) {
180+ fs . readFile ( name , 'utf-8' , function ( error , content ) {
181+ if ( error ) {
182+ reject ( error )
183+ } else {
184+ resolve ( content )
185+ }
92186 } )
93187 } )
94188 }
0 commit comments