11// Copyright (c) 2020 Gonzalo Müller Bravo.
22// Licensed under the MIT License (MIT), see LICENSE.txt
3- const { buildCreateFunction } = require ( '../utils/create-utils.js' )
4- const { formatReportMessage } = require ( '../utils/report-utils.js' )
5- const { shouldCheck } = require ( '../utils/check-utils.js' )
3+ const { buildCreateFunction} = require ( '../utils/create-utils.js' )
4+ const { formatReportMessage} = require ( '../utils/report-utils.js' )
5+ const { shouldCheck} = require ( '../utils/check-utils.js' )
66
7- const { REGEX_FLAGS_FIELD_DEFINITION , FILES_FIELD_DEFINITION } = require ( './common-fields-definitions.js' )
7+ const { REGEX_FLAGS_FIELD_DEFINITION , FILES_FIELD_DEFINITION } = require ( './common-fields-definitions.js' )
88
99/**
1010 * @param {string } source Text that was checked.
@@ -14,9 +14,9 @@ const { REGEX_FLAGS_FIELD_DEFINITION, FILES_FIELD_DEFINITION } = require('./comm
1414function foundStartLocation ( source , foundAt ) {
1515 return Array . from ( source . substring ( 0 , foundAt ) )
1616 . reduce ( ( result , char ) => char !== '\n'
17- ? { line : result . line , column : result . column + 1 }
18- : { line : result . line + 1 , column : 0 } ,
19- { line : 1 , column : 0 } )
17+ ? { line : result . line , column : result . column + 1 }
18+ : { line : result . line + 1 , column : 0 } ,
19+ { line : 1 , column : 0 } )
2020}
2121
2222/**
@@ -28,8 +28,9 @@ function foundStartLocation(source, foundAt) {
2828 * @param {{ regex: RegExp, id: string | undefined, message: string | undefined } } pattern Information of the pattern to be use to inspect.
2929 * @param {* } replace Replacement function
3030 * @param {Function } report Report function
31+ * @param {*[] } [suggestions] Array of suggestion functions.
3132 */
32- function checkRegex ( source , pattern , replace , report ) {
33+ function checkRegex ( source , pattern , replace , report , suggestions = [ ] ) {
3334 /**
3435 * @param {string } source Text to be checked.
3536 * @param {RegExp } regex Regular Expression use to check.
@@ -48,12 +49,16 @@ function checkRegex(source, pattern, replace, report) {
4849 from => `Invalid regular expression ${ from } found`
4950 )
5051 pattern . regex . test ( '' )
51- let matchDetail = { matchStart : - 1 }
52- while ( matchDetail . matchStart !== matchDetail . nextChar && ( matchDetail = checkRegexInSource ( ) ) ) {
52+ let matchDetail = { matchStart : - 1 }
53+ while ( matchDetail . matchStart !== matchDetail . nextChar && ( matchDetail = checkRegexInSource ( ) ) ) {
5354 report ( {
54- loc : { start : foundStartLocation ( source , matchDetail . matchStart ) } ,
55+ loc : { start : foundStartLocation ( source , matchDetail . matchStart ) } ,
5556 message,
56- fix : replace ( 0 , matchDetail )
57+ fix : replace ( 0 , matchDetail ) ,
58+ suggest : suggestions . map ( s => ( {
59+ desc : s . message ,
60+ fix : s . replacement ( 0 , matchDetail ) ,
61+ } ) ) ,
5762 } )
5863 }
5964}
@@ -68,11 +73,12 @@ function buildReplacementFunction(replacement) {
6873 return replacement
6974 }
7075 }
71- catch ( e ) { }
76+ catch ( e ) {
77+ }
7278 return $ [ 0 ]
7379 }
7480 }
75- catch ( e ) {
81+ catch ( e ) {
7682 return null
7783 }
7884}
@@ -91,7 +97,7 @@ function createReplacement(replacement) {
9197 switch ( typeof replacement ) {
9298 case 'string' :
9399 return ( from , matchDetail ) => fixer => fixer . replaceTextRange ( [ from + matchDetail . matchStart , from + matchDetail . nextChar ] , replacement )
94- case 'object' :{
100+ case 'object' : {
95101 const replacementFunction = buildReplacementFunction ( replacement . function )
96102 if ( typeof replacementFunction === 'function' ) {
97103 return ( from , matchDetail ) =>
@@ -106,14 +112,27 @@ function createReplacement(replacement) {
106112}
107113
108114function checkPatterns ( fileName , source , patterns , report ) {
109- patterns . forEach ( pattern => shouldCheck ( pattern . files , fileName ) &&
110- checkRegex ( source , pattern , createReplacement ( pattern . details . replacement ) , report ) )
115+ patterns . forEach ( pattern => {
116+ if ( ! shouldCheck ( pattern . files , fileName ) ) {
117+ return
118+ }
119+
120+ const replacement = createReplacement ( pattern . details . replacement )
121+ let suggestions = pattern . details . suggestions || [ ]
122+ suggestions = suggestions . map ( ( d ) => ( {
123+ message : d . message ,
124+ replacement : createReplacement ( d . replacement )
125+ } ) )
126+
127+ checkRegex ( source , pattern , replacement , report , suggestions )
128+ } )
111129}
112130
113131module . exports = {
114132 meta : {
115133 type : 'suggestion' ,
116134 fixable : 'code' ,
135+ hasSuggestions : true ,
117136 docs : {
118137 description : 'Invalid regular expressions to be reported' ,
119138 category : 'Stylistic Issues' ,
@@ -148,7 +167,7 @@ module.exports = {
148167 } ,
149168 flags : REGEX_FLAGS_FIELD_DEFINITION ,
150169 replacement : {
151- oneOf :[ {
170+ oneOf : [ {
152171 title : 'Replacement' ,
153172 description : 'Replacement for invalid pattern' ,
154173 type : 'string'
@@ -168,15 +187,53 @@ module.exports = {
168187 maxProperties : 1
169188 } ]
170189 } ,
171- message : {
172- title : 'Invalid message' ,
173- description : 'Message to be shown when Invalid pattern is found' ,
174- type : 'string' ,
175- minLength : 3
190+ suggestions : {
191+ type : 'array' ,
192+ description : 'Array of suggestions to be used when the pattern is found' ,
193+ title : 'Suggestions' ,
194+ items : {
195+ type : 'object' ,
196+ description : 'Suggestion to be used when the pattern is found' ,
197+ properties : {
198+ message : {
199+ title : 'Suggestion message' ,
200+ description : 'Message to be shown when the suggestion is applied' ,
201+ type : 'string' ,
202+ minLength : 3
203+ } ,
204+ replacement : {
205+ oneOf : [ {
206+ title : 'Replacement' ,
207+ description : 'Replacement for invalid pattern' ,
208+ type : 'string'
209+ } , {
210+ title : 'Detailed replacement' ,
211+ description : 'Detailed replacements for invalid patterns' ,
212+ type : 'object' ,
213+ properties : {
214+ function : {
215+ title : 'Replacement function' ,
216+ description : 'Function used to replace the found pattern. It receives the found text and must return the replacement text' ,
217+ type : 'string' ,
218+ minLength : 1
219+ }
220+ } ,
221+ minProperties : 1 ,
222+ maxProperties : 1
223+ } ]
224+ }
225+ } ,
226+ } ,
227+ message : {
228+ title : 'Invalid message' ,
229+ description : 'Message to be shown when Invalid pattern is found' ,
230+ type : 'string' ,
231+ minLength : 3
232+ } ,
233+ files : FILES_FIELD_DEFINITION
176234 } ,
177- files : FILES_FIELD_DEFINITION
178- } ,
179- required : [ 'regex' ]
235+ required : [ 'regex' ]
236+ }
180237 } ]
181238 } ,
182239 minItems : 1
0 commit comments