@@ -4,6 +4,7 @@ import type {
44 ExpressionStatement ,
55 Identifier ,
66 ImportSpecifier ,
7+ MemberExpression ,
78 Node ,
89 Program ,
910 SpreadElement ,
@@ -113,6 +114,38 @@ const create = Components.detect(
113114 } ) ;
114115 }
115116
117+ function getIsSafeWindowCheck ( node : Rule . NodeParentExtension ) {
118+
119+ // check if the window usage is behind a typeof window === 'undefined' check
120+ const conditionalExpressionNode = node . parent ?. parent ;
121+ const isWindowCheck =
122+ conditionalExpressionNode ?. type === "ConditionalExpression" &&
123+ conditionalExpressionNode . test ?. type === "BinaryExpression" &&
124+ conditionalExpressionNode . test . left ?. type === "UnaryExpression" &&
125+ conditionalExpressionNode . test . left . operator === "typeof" &&
126+ conditionalExpressionNode . test . left . argument ?. type === "Identifier" &&
127+ conditionalExpressionNode . test . left . argument ?. name === "window" &&
128+ conditionalExpressionNode . test . right ?. type === "Literal" &&
129+ conditionalExpressionNode . test . right . value === "undefined" ;
130+
131+ // checks to see if it's `typeof window !== 'undefined'` or `typeof window === 'undefined'`
132+ const isNegatedWindowCheck =
133+ isWindowCheck &&
134+ conditionalExpressionNode . test ?. type === "BinaryExpression" &&
135+ conditionalExpressionNode . test . operator === "!==" ;
136+
137+ // checks to see if window is being accessed safely behind a window check
138+ const isSafelyBehindWindowCheck =
139+ ( isWindowCheck &&
140+ ! isNegatedWindowCheck &&
141+ conditionalExpressionNode . alternate === node ?. parent ) ||
142+ ( isNegatedWindowCheck &&
143+ conditionalExpressionNode . consequent === node ?. parent ) ;
144+
145+ return isSafelyBehindWindowCheck
146+
147+ }
148+
116149 const reactImports : Record < string | "namespace" , string | string [ ] > = {
117150 namespace : [ ] ,
118151 } ;
@@ -209,39 +242,15 @@ const create = Components.detect(
209242 const name = node . object . name ;
210243 const scopeType = context . getScope ( ) . type ;
211244
212- // check if the window usage is behind a typeof window === 'undefined' check
213- const conditionalExpressionNode = node . parent ?. parent ;
214- const isWindowCheck =
215- conditionalExpressionNode ?. type === "ConditionalExpression" &&
216- conditionalExpressionNode . test ?. type === "BinaryExpression" &&
217- conditionalExpressionNode . test . left ?. type === "UnaryExpression" &&
218- conditionalExpressionNode . test . left . operator === "typeof" &&
219- conditionalExpressionNode . test . left . argument ?. type === "Identifier" &&
220- conditionalExpressionNode . test . left . argument ?. name === "window" &&
221- conditionalExpressionNode . test . right ?. type === "Literal" &&
222- conditionalExpressionNode . test . right . value === "undefined" ;
223-
224- // checks to see if it's `typeof window !== 'undefined'` or `typeof window === 'undefined'`
225- const isNegatedWindowCheck =
226- isWindowCheck &&
227- conditionalExpressionNode . test ?. type === "BinaryExpression" &&
228- conditionalExpressionNode . test . operator === "!==" ;
229-
230- // checks to see if window is being accessed safely behind a window check
231- const isSafelyBehindWindowCheck =
232- ( isWindowCheck &&
233- ! isNegatedWindowCheck &&
234- conditionalExpressionNode . alternate === node ?. parent ) ||
235- ( isNegatedWindowCheck &&
236- conditionalExpressionNode . consequent === node ?. parent ) ;
245+ const isSafelyBehindWindowCheck = getIsSafeWindowCheck ( node ) ;
246+
237247
238248 if (
239249 undeclaredReferences . has ( name ) &&
240250 browserOnlyGlobals . has ( name ) &&
241251 ( scopeType === "module" || ! ! util . getParentComponent ( node ) ) &&
242252 ! isSafelyBehindWindowCheck
243253 ) {
244- // console.log(name, node.object)
245254 instances . push ( name ) ;
246255 reportMissingDirective ( "addUseClientBrowserAPI" , node . object ) ;
247256 }
0 commit comments