@@ -3,6 +3,11 @@ const {isParenthesized, findVariable} = require('eslint-utils');
33const getDocumentationUrl = require ( './utils/get-documentation-url' ) ;
44const methodSelector = require ( './utils/method-selector' ) ;
55const getVariableIdentifiers = require ( './utils/get-variable-identifiers' ) ;
6+ const renameVariable = require ( './utils/rename-variable' ) ;
7+ const avoidCapture = require ( './utils/avoid-capture' ) ;
8+ const getChildScopesRecursive = require ( './utils/get-child-scopes-recursive' ) ;
9+ const singular = require ( './utils/singular' ) ;
10+ const extendFixRange = require ( './utils/extend-fix-range' ) ;
611
712const ERROR_ZERO_INDEX = 'error-zero-index' ;
813const ERROR_SHIFT = 'error-shift' ;
@@ -94,10 +99,10 @@ const destructuringAssignmentSelector = [
9499// Need add `()` to the `AssignmentExpression`
95100// - `ObjectExpression`: `[{foo}] = array.filter(bar)` fix to `{foo} = array.find(bar)`
96101// - `ObjectPattern`: `[{foo = baz}] = array.filter(bar)`
97- const assignmentNeedParenthesize = ( node , source ) => {
102+ const assignmentNeedParenthesize = ( node , sourceCode ) => {
98103 const isAssign = node . type === 'AssignmentExpression' ;
99104
100- if ( ! isAssign || isParenthesized ( node , source ) ) {
105+ if ( ! isAssign || isParenthesized ( node , sourceCode ) ) {
101106 return false ;
102107 }
103108
@@ -143,36 +148,36 @@ const getDestructuringLeftAndRight = node => {
143148 return { } ;
144149} ;
145150
146- function * fixDestructuring ( node , source , fixer ) {
151+ function * fixDestructuring ( node , sourceCode , fixer ) {
147152 const { left} = getDestructuringLeftAndRight ( node ) ;
148153 const [ element ] = left . elements ;
149154
150- const leftText = source . getText ( element . type === 'AssignmentPattern' ? element . left : element ) ;
155+ const leftText = sourceCode . getText ( element . type === 'AssignmentPattern' ? element . left : element ) ;
151156 yield fixer . replaceText ( left , leftText ) ;
152157
153158 // `AssignmentExpression` always starts with `[` or `(`, so we don't need check ASI
154- if ( assignmentNeedParenthesize ( node , source ) ) {
159+ if ( assignmentNeedParenthesize ( node , sourceCode ) ) {
155160 yield fixer . insertTextBefore ( node , '(' ) ;
156161 yield fixer . insertTextAfter ( node , ')' ) ;
157162 }
158163}
159164
160165const hasDefaultValue = node => getDestructuringLeftAndRight ( node ) . left . elements [ 0 ] . type === 'AssignmentPattern' ;
161166
162- const fixDestructuringDefaultValue = ( node , source , fixer , operator ) => {
167+ const fixDestructuringDefaultValue = ( node , sourceCode , fixer , operator ) => {
163168 const { left, right} = getDestructuringLeftAndRight ( node ) ;
164169 const [ element ] = left . elements ;
165170 const defaultValue = element . right ;
166- let defaultValueText = source . getText ( defaultValue ) ;
171+ let defaultValueText = sourceCode . getText ( defaultValue ) ;
167172
168- if ( isParenthesized ( defaultValue , source ) || hasLowerPrecedence ( defaultValue , operator ) ) {
173+ if ( isParenthesized ( defaultValue , sourceCode ) || hasLowerPrecedence ( defaultValue , operator ) ) {
169174 defaultValueText = `(${ defaultValueText } )` ;
170175 }
171176
172177 return fixer . insertTextAfter ( right , ` ${ operator } ${ defaultValueText } ` ) ;
173178} ;
174179
175- const fixDestructuringAndReplaceFilter = ( source , node ) => {
180+ const fixDestructuringAndReplaceFilter = ( sourceCode , node ) => {
176181 const { property} = getDestructuringLeftAndRight ( node ) . right . callee ;
177182
178183 let suggest ;
@@ -186,14 +191,14 @@ const fixDestructuringAndReplaceFilter = (source, node) => {
186191 messageId,
187192 * fix ( fixer ) {
188193 yield fixer . replaceText ( property , 'find' ) ;
189- yield fixDestructuringDefaultValue ( node , source , fixer , operator ) ;
190- yield * fixDestructuring ( node , source , fixer ) ;
194+ yield fixDestructuringDefaultValue ( node , sourceCode , fixer , operator ) ;
195+ yield * fixDestructuring ( node , sourceCode , fixer ) ;
191196 }
192197 } ) ) ;
193198 } else {
194199 fix = function * ( fixer ) {
195200 yield fixer . replaceText ( property , 'find' ) ;
196- yield * fixDestructuring ( node , source , fixer ) ;
201+ yield * fixDestructuring ( node , sourceCode , fixer ) ;
197202 } ;
198203 }
199204
@@ -221,7 +226,7 @@ const isDestructuringFirstElement = node => {
221226} ;
222227
223228const create = context => {
224- const source = context . getSourceCode ( ) ;
229+ const sourceCode = context . getSourceCode ( ) ;
225230
226231 return {
227232 [ zeroIndexSelector ] ( node ) {
@@ -248,18 +253,19 @@ const create = context => {
248253 context . report ( {
249254 node : node . init . callee . property ,
250255 messageId : ERROR_DESTRUCTURING_DECLARATION ,
251- ...fixDestructuringAndReplaceFilter ( source , node )
256+ ...fixDestructuringAndReplaceFilter ( sourceCode , node )
252257 } ) ;
253258 } ,
254259 [ destructuringAssignmentSelector ] ( node ) {
255260 context . report ( {
256261 node : node . right . callee . property ,
257262 messageId : ERROR_DESTRUCTURING_ASSIGNMENT ,
258- ...fixDestructuringAndReplaceFilter ( source , node )
263+ ...fixDestructuringAndReplaceFilter ( sourceCode , node )
259264 } ) ;
260265 } ,
261266 [ filterVariableSelector ] ( node ) {
262- const variable = findVariable ( context . getScope ( ) , node . id ) ;
267+ const scope = context . getScope ( ) ;
268+ const variable = findVariable ( scope , node . id ) ;
263269 const identifiers = getVariableIdentifiers ( variable ) . filter ( identifier => identifier !== node . id ) ;
264270
265271 if ( identifiers . length === 0 ) {
@@ -288,12 +294,22 @@ const create = context => {
288294 problem . fix = function * ( fixer ) {
289295 yield fixer . replaceText ( node . init . callee . property , 'find' ) ;
290296
297+ const singularName = singular ( node . id . name ) ;
298+ if ( singularName ) {
299+ // Rename variable to be singularized now that it refers to a single item in the array instead of the entire array.
300+ const singularizedName = avoidCapture ( singularName , getChildScopesRecursive ( scope ) , context . parserOptions . ecmaVersion ) ;
301+ yield * renameVariable ( variable , singularizedName , fixer ) ;
302+
303+ // Prevent possible variable conflicts
304+ yield * extendFixRange ( fixer , sourceCode . ast . range ) ;
305+ }
306+
291307 for ( const node of zeroIndexNodes ) {
292308 yield fixer . removeRange ( [ node . object . range [ 1 ] , node . range [ 1 ] ] ) ;
293309 }
294310
295311 for ( const node of destructuringNodes ) {
296- yield * fixDestructuring ( node , source , fixer ) ;
312+ yield * fixDestructuring ( node , sourceCode , fixer ) ;
297313 }
298314 } ;
299315 }
0 commit comments