11import globals from 'globals' ;
2- import esquery from 'esquery' ;
32import { functionTypes } from './ast/index.js' ;
43
54const MESSAGE_ID_EXTERNALLY_SCOPED_VARIABLE = 'externally-scoped-variable' ;
65const messages = {
76 [ MESSAGE_ID_EXTERNALLY_SCOPED_VARIABLE ] : 'Variable {{name}} not defined in scope of isolated function. Function is isolated because: {{reason}}.' ,
87} ;
98
10- const parsedEsquerySelectors = new Map ( ) ;
11- const parseEsquerySelector = selector => {
12- if ( ! parsedEsquerySelectors . has ( selector ) ) {
13- parsedEsquerySelectors . set ( selector , esquery . parse ( selector ) ) ;
14- }
15-
16- return parsedEsquerySelectors . get ( selector ) ;
17- } ;
18-
199/** @type {{functions: string[], selectors: string[], comments: string[], overrideGlobals?: import('eslint').Linter.Globals} } */
2010const defaultOptions = {
2111 functions : [ 'makeSynchronous' ] ,
@@ -40,14 +30,16 @@ const create = context => {
4030 ...context . languageOptions . globals ,
4131 ...options . overrideGlobals ,
4232 } ;
33+ const checked = new WeakSet ( ) ;
4334
4435 /** @param {import('estree').Node } node */
45- const checkForExternallyScopedVariables = node => {
46- let reason = reasonForBeingIsolatedFunction ( node ) ;
47- if ( ! reason ) {
36+ const checkForExternallyScopedVariables = ( node , reason ) => {
37+ if ( checked . has ( node ) || ! functionTypes . includes ( node . type ) ) {
4838 return ;
4939 }
5040
41+ checked . add ( node ) ;
42+
5143 const nodeScope = sourceCode . getScope ( node ) ;
5244
5345 // `through`: "The array of references which could not be resolved in this scope" https://eslint.org/docs/latest/extend/scope-manager-interface#scope-interface
@@ -136,20 +128,29 @@ const create = context => {
136128 ) {
137129 return `callee of function named ${ JSON . stringify ( node . parent . callee . name ) } ` ;
138130 }
131+ } ;
139132
140- if ( options . selectors . length > 0 ) {
141- const ancestors = sourceCode . getAncestors ( node ) ;
142- const matchedSelector = options . selectors . find ( selector => esquery . matches ( node , parseEsquerySelector ( selector ) , ancestors ) ) ;
143- if ( matchedSelector ) {
144- return `matches selector ${ JSON . stringify ( matchedSelector ) } ` ;
133+ context . onExit (
134+ functionTypes ,
135+ node => {
136+ const reason = reasonForBeingIsolatedFunction ( node ) ;
137+ if ( ! reason ) {
138+ return ;
145139 }
146- }
147- } ;
148140
149- context . on (
150- functionTypes . map ( type => `${ type } :exit` ) ,
151- checkForExternallyScopedVariables ,
141+ return checkForExternallyScopedVariables ( node , reason ) ;
142+ } ,
152143 ) ;
144+
145+ for ( const selector of options . selectors ) {
146+ context . onExit (
147+ selector ,
148+ node => {
149+ const reason = `matches selector ${ JSON . stringify ( selector ) } ` ;
150+ return checkForExternallyScopedVariables ( node , reason ) ;
151+ } ,
152+ ) ;
153+ }
153154} ;
154155
155156/** @type {import('json-schema').JSONSchema7[] } */
0 commit comments