11'use strict' ;
2- const isShadowed = require ( './utils/is-shadowed.js' ) ;
3- const {
4- referenceIdentifierSelector,
5- callExpressionSelector,
6- } = require ( './selectors/index.js' ) ;
2+ const { ReferenceTracker} = require ( 'eslint-utils' ) ;
73const { replaceReferenceIdentifier} = require ( './fix/index.js' ) ;
84const { fixSpaceAroundKeyword} = require ( './fix/index.js' ) ;
95
@@ -25,102 +21,102 @@ const methods = {
2521 isFinite : false ,
2622} ;
2723
28- const methodsSelector = [
29- callExpressionSelector ( Object . keys ( methods ) ) ,
30- ' > ' ,
31- '.callee' ,
32- ] . join ( '' ) ;
33-
34- const propertiesSelector = referenceIdentifierSelector ( [ 'NaN' , 'Infinity' ] ) ;
35-
3624const isNegative = node => {
3725 const { parent} = node ;
3826 return parent && parent . type === 'UnaryExpression' && parent . operator === '-' && parent . argument === node ;
3927} ;
4028
29+ function * checkMethods ( { sourceCode, tracker} ) {
30+ const traceMap = Object . fromEntries (
31+ Object . keys ( methods ) . map ( name => [ name , { [ ReferenceTracker . CALL ] : true } ] ) ,
32+ ) ;
33+
34+ for ( const { node : callExpression , path : [ name ] } of tracker . iterateGlobalReferences ( traceMap ) ) {
35+ const node = callExpression . callee ;
36+ const isSafe = methods [ name ] ;
37+
38+ const problem = {
39+ node,
40+ messageId : METHOD_ERROR_MESSAGE_ID ,
41+ data : {
42+ name,
43+ } ,
44+ } ;
45+
46+ const fix = fixer => replaceReferenceIdentifier ( node , `Number.${ name } ` , fixer , sourceCode ) ;
47+
48+ if ( isSafe ) {
49+ problem . fix = fix ;
50+ } else {
51+ problem . suggest = [
52+ {
53+ messageId : METHOD_SUGGESTION_MESSAGE_ID ,
54+ data : {
55+ name,
56+ } ,
57+ fix,
58+ } ,
59+ ] ;
60+ }
61+
62+ yield problem ;
63+ }
64+ }
65+
66+ function * checkProperties ( { sourceCode, tracker, checkInfinity} ) {
67+ const properties = checkInfinity ? [ 'NaN' , 'Infinity' ] : [ 'NaN' ] ;
68+ const traceMap = Object . fromEntries (
69+ properties . map ( name => [ name , { [ ReferenceTracker . READ ] : true } ] ) ,
70+ ) ;
71+
72+ for ( const { node, path : [ name ] } of tracker . iterateGlobalReferences ( traceMap ) ) {
73+ const { parent} = node ;
74+
75+ let property = name ;
76+ if ( name === 'Infinity' ) {
77+ property = isNegative ( node ) ? 'NEGATIVE_INFINITY' : 'POSITIVE_INFINITY' ;
78+ }
79+
80+ const problem = {
81+ node,
82+ messageId : PROPERTY_ERROR_MESSAGE_ID ,
83+ data : {
84+ identifier : name ,
85+ property,
86+ } ,
87+ } ;
88+
89+ if ( property === 'NEGATIVE_INFINITY' ) {
90+ problem . node = parent ;
91+ problem . data . identifier = '-Infinity' ;
92+ problem . fix = function * ( fixer ) {
93+ yield fixer . replaceText ( parent , 'Number.NEGATIVE_INFINITY' ) ;
94+ yield * fixSpaceAroundKeyword ( fixer , parent , sourceCode ) ;
95+ } ;
96+ } else {
97+ problem . fix = fixer => replaceReferenceIdentifier ( node , `Number.${ property } ` , fixer , sourceCode ) ;
98+ }
99+
100+ yield problem ;
101+ }
102+ }
103+
41104/** @param {import('eslint').Rule.RuleContext } context */
42105const create = context => {
43- const sourceCode = context . getSourceCode ( ) ;
44- const options = {
106+ const {
107+ checkInfinity,
108+ } = {
45109 checkInfinity : true ,
46110 ...context . options [ 0 ] ,
47111 } ;
48112
49- // Cache `NaN` and `Infinity` in `foo = {NaN, Infinity}`
50- const reported = new WeakSet ( ) ;
51-
52113 return {
53- [ methodsSelector ] ( node ) {
54- if ( isShadowed ( context . getScope ( ) , node ) ) {
55- return ;
56- }
57-
58- const { name} = node ;
59- const isSafe = methods [ name ] ;
60-
61- const problem = {
62- node,
63- messageId : METHOD_ERROR_MESSAGE_ID ,
64- data : {
65- name,
66- } ,
67- } ;
68-
69- const fix = fixer => replaceReferenceIdentifier ( node , `Number.${ name } ` , fixer , sourceCode ) ;
70-
71- if ( isSafe ) {
72- problem . fix = fix ;
73- } else {
74- problem . suggest = [
75- {
76- messageId : METHOD_SUGGESTION_MESSAGE_ID ,
77- data : {
78- name,
79- } ,
80- fix,
81- } ,
82- ] ;
83- }
84-
85- return problem ;
86- } ,
87- [ propertiesSelector ] ( node ) {
88- if ( reported . has ( node ) || isShadowed ( context . getScope ( ) , node ) ) {
89- return ;
90- }
91-
92- const { name, parent} = node ;
93- if ( name === 'Infinity' && ! options . checkInfinity ) {
94- return ;
95- }
96-
97- let property = name ;
98- if ( name === 'Infinity' ) {
99- property = isNegative ( node ) ? 'NEGATIVE_INFINITY' : 'POSITIVE_INFINITY' ;
100- }
101-
102- const problem = {
103- node,
104- messageId : PROPERTY_ERROR_MESSAGE_ID ,
105- data : {
106- identifier : name ,
107- property,
108- } ,
109- } ;
114+ * 'Program:exit' ( ) {
115+ const sourceCode = context . getSourceCode ( ) ;
116+ const tracker = new ReferenceTracker ( context . getScope ( ) ) ;
110117
111- if ( property === 'NEGATIVE_INFINITY' ) {
112- problem . node = parent ;
113- problem . data . identifier = '-Infinity' ;
114- problem . fix = function * ( fixer ) {
115- yield fixer . replaceText ( parent , 'Number.NEGATIVE_INFINITY' ) ;
116- yield * fixSpaceAroundKeyword ( fixer , parent , sourceCode ) ;
117- } ;
118- } else {
119- problem . fix = fixer => replaceReferenceIdentifier ( node , `Number.${ property } ` , fixer , sourceCode ) ;
120- }
121-
122- reported . add ( node ) ;
123- return problem ;
118+ yield * checkMethods ( { sourceCode, tracker} ) ;
119+ yield * checkProperties ( { sourceCode, tracker, checkInfinity} ) ;
124120 } ,
125121 } ;
126122} ;
0 commit comments