1+ 'use strict' ;
2+
3+ // replace left/right quotation marks with normal quotation marks
4+ function normalizeQuotationMarks ( str ) {
5+ if ( str ) {
6+ str = str . replace ( / [ \u201c \u201d ] / g, '"' ) ;
7+ }
8+ return str ;
9+ }
10+
11+ function lint ( config , files , done ) {
12+ const path = require ( 'path' )
13+ const exec = require ( 'child_process' ) . exec
14+ var chunkify = require ( './chunkify' )
15+ const async = require ( 'async' )
16+ const javadetect = require ( './javadetect' )
17+ const jar = require ( 'vnu-jar' )
18+
19+ const maxChars = 5000
20+
21+ // increase child process buffer to accommodate large amounts of
22+ // validation output. ( default is a paltry 200k. )
23+ // http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
24+ const maxBuffer = 20000 * 1024
25+
26+ // parse and, if needed, normalize error messages from HttpClient to java -jar format
27+ // java -jar: one object containing messages for all files
28+ // { messages: [{ message, type, url, ... }, ...] }
29+ // HttpClient: one object per file, separated by a newline, each object containing messages for only that file
30+ // { messages: [{ message, type, ...}, ...], url }\n{ ... }
31+ function parseErrorMessages ( errors ) {
32+ var parsed = JSON . parse ( config . server ? '[' + errors . trim ( ) . replace ( / \n / g, ',' ) + ']' : errors ) ;
33+ var messages = parsed . messages ;
34+ if ( config . server ) {
35+ // extract "messages" property from each object and set the url of each message
36+ // this results in an array of arrays instead of array of objects, which is then flattened by concatenation
37+ messages = Array . prototype . concat . apply ( [ ] , parsed . map ( function ( file ) {
38+ return file . messages . map ( function ( message ) {
39+ message . url = file . url ;
40+ return message ;
41+ } ) ;
42+ } ) ) ;
43+ }
44+ return messages ;
45+ }
46+
47+ // determine proper jarfile command and arguments
48+ function cmd ( java , chunk ) {
49+ var args = '' ;
50+ if ( config . server ) {
51+ if ( config . server . host ) {
52+ args += ' -Dnu.validator.client.host=' + config . server . host ;
53+ }
54+ if ( config . server . port ) {
55+ args += ' -Dnu.validator.client.port=' + config . server . port ;
56+ }
57+ args += ' -Dnu.validator.client.out=json nu.validator.client.HttpClient' ;
58+ } else {
59+ args += ' --format json' ;
60+ }
61+ if ( config . noLangDetect ) {
62+ args += ' --no-langdetect' ;
63+ }
64+ var invoke = ( config . server ? '-cp' : '-jar' ) + ' "' + jar + '"' + args ;
65+ // command to call java, increasing the default stack size for ia32 versions of the JRE and using the default setting for x64 versions
66+ return 'java ' + ( java . arch === 'ia32' ? '-Xss512k ' : '' ) + invoke + ' ' + chunk ;
67+ }
68+
69+ javadetect ( function ( err , java ) {
70+ if ( err ) {
71+ throw err ;
72+ }
73+
74+ var javaVersion = java . version . split ( '.' ) . map ( Number ) ;
75+ if ( ( javaVersion [ 0 ] !== 1 && javaVersion [ 0 ] < 8 ) || ( javaVersion [ 0 ] === 1 && javaVersion [ 1 ] < 8 ) ) {
76+ throw new Error ( '\nUnsupported Java version used: ' + java . version + '. Java 8 environment or up is required!' ) ;
77+ }
78+
79+ files = files . map ( ( file ) => path . relative ( '.' , file ) )
80+ async . mapSeries ( chunkify ( files , maxChars ) , function ( chunk , cb ) {
81+
82+ exec ( cmd ( java , chunk , config . noLangDetect ) , {
83+ 'maxBuffer' : maxBuffer
84+ } , function ( error , stdout , stderr ) {
85+ if ( error && ( error . code !== 1 || error . killed || error . signal ) ) {
86+ cb ( error ) ;
87+ return ;
88+ }
89+
90+ stderr = config . server ? stdout : stderr ;
91+ var result = [ ] ;
92+ if ( stderr ) {
93+ try {
94+ result = parseErrorMessages ( stderr ) ;
95+ } catch ( err ) {
96+ throw new Error ( err + '\nInvalid input follows below:\n\n' + stderr ) ;
97+ }
98+ result . forEach ( function ( message ) {
99+ if ( message . url ) {
100+ message . file = path . relative ( '.' , message . url . replace ( path . sep !== '\\' ? 'file:' : 'file:/' , '' ) ) ;
101+ }
102+ if ( config . absoluteFilePathsForReporter ) {
103+ message . file = path . resolve ( message . file ) ;
104+ }
105+ } ) ;
106+ if ( config . ignore ) {
107+ var ignore = config . ignore instanceof Array ? config . ignore : [ config . ignore ] ;
108+ result = result . filter ( function ( message ) {
109+ // iterate over the ignore rules and test the message agains each rule.
110+ // A match should return false, which causes every( ) to return false and the message to be filtered out.
111+ return ignore . every ( function ( currentValue ) {
112+ if ( currentValue instanceof RegExp ) {
113+ return ! currentValue . test ( message . message ) ;
114+ }
115+ return normalizeQuotationMarks ( currentValue ) !== normalizeQuotationMarks ( message . message ) ;
116+ } ) ;
117+ } ) ;
118+ }
119+ }
120+ cb ( null , result ) ;
121+ } ) ;
122+ } , function ( error , results ) {
123+ if ( error ) {
124+ done ( error ) ;
125+ return ;
126+ }
127+
128+ var result = [ ] ;
129+ for ( var r = 0 , len = results . length ; r < len ; r ++ ) {
130+ result = result . concat ( results [ r ] ) ;
131+ }
132+
133+ done ( null , result . filter ( function ( item ) {
134+ return config . errorLevels . indexOf ( item . type ) !== - 1 ;
135+ } ) ) ;
136+ } ) ;
137+ } ) ;
138+ }
139+
140+ module . exports = lint
0 commit comments