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,14 +88,102 @@ 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 ) {
8894 grunt . log . writeln ( 'Problems in "' + source + '":' )
89- grunt . log . write ( errors )
95+ grunt . log . write ( messages )
9096 }
9197 ++ failed
9298 }
99+ return addReport ( source , messages )
100+ } )
101+ } )
102+ }
103+
104+ function writeReport ( ) {
105+ function writeReport ( ) {
106+ return writeFile ( report , JSON . stringify ( reports ) )
107+ }
108+
109+ if ( report ) {
110+ const directory = path . dirname ( report )
111+ grunt . verbose . writeln ( 'Writing report to "' + chalk . cyan ( report ) + '".' )
112+ if ( directory ) {
113+ return ensureDirectory ( directory ) . then ( writeReport )
114+ }
115+ return writeReport ( )
116+ }
117+ }
118+
119+ function addReport ( source , messages ) {
120+ if ( messages ) {
121+ return readFile ( source ) . then ( function ( content ) {
122+ reportFile ( source , content , messages )
123+ } )
124+ }
125+ }
126+
127+ function reportFile ( name , content , messages ) {
128+ const contentLines = content . split ( / \r ? \n / )
129+ messages . split ( / \r ? \n / ) . forEach ( function ( line ) {
130+ const message = parseMessage ( line )
131+ const place = contentLines [ message . lastLine - 1 ] || ''
132+ message . extract = place . substr ( message . firstColumn - 1 )
133+ message . hiliteLength = message . hiliteStart = 0
134+ message . file = name
135+ reports . push ( message )
136+ } )
137+ }
138+
139+ function parseMessage ( message ) {
140+ const parsed = / ^ l i n e ( \d + ) c o l u m n ( \d + ) - ( \w + ) : / . exec ( message )
141+ var column
142+ if ( parsed ) {
143+ column = parseInt ( parsed [ 2 ] )
144+ return parsed && {
145+ type : parsed [ 3 ] . toLowerCase ( ) ,
146+ firstColumn : column ,
147+ lastColumn : column ,
148+ lastLine : parseInt ( parsed [ 1 ] ) ,
149+ message : message . substr ( parsed [ 0 ] . length + 1 )
150+ }
151+ }
152+ return { }
153+ }
154+
155+ function ensureDirectory ( name ) {
156+ return new Promise ( function ( resolve , reject ) {
157+ mkdirp ( name , function ( error ) {
158+ if ( error ) {
159+ reject ( error )
160+ } else {
161+ resolve ( )
162+ }
163+ } )
164+ } )
165+ }
166+
167+ function writeFile ( name , content ) {
168+ return new Promise ( function ( resolve , reject ) {
169+ fs . writeFile ( name , content , function ( error ) {
170+ if ( error ) {
171+ reject ( error )
172+ } else {
173+ resolve ( )
174+ }
175+ } )
176+ } )
177+ }
178+
179+ function readFile ( name ) {
180+ return new Promise ( function ( resolve , reject ) {
181+ fs . readFile ( name , 'utf-8' , function ( error , content ) {
182+ if ( error ) {
183+ reject ( error )
184+ } else {
185+ resolve ( content )
186+ }
93187 } )
94188 } )
95189 }
0 commit comments