@@ -18,6 +18,7 @@ function reachableDuringSSRPartial() {
1818 let reachableFunctionThatWeAreIn = null ;
1919 let reachableMethodThatWeAreIn = null ;
2020 let skippedBlockThatWeAreIn = null ;
21+ let skippedConditionThatWeAreIn = null ;
2122
2223 const withinLWCVisitors = {
2324 Program : ( node ) => {
@@ -100,13 +101,23 @@ function reachableDuringSSRPartial() {
100101 skippedBlockThatWeAreIn = null ;
101102 }
102103 } ,
104+ ConditionalExpression : ( node ) => {
105+ if ( isSSREscape ( node ) ) {
106+ skippedConditionThatWeAreIn = node ;
107+ }
108+ } ,
109+ 'ConditionalExpression:exit' : ( node ) => {
110+ if ( skippedConditionThatWeAreIn === node ) {
111+ skippedConditionThatWeAreIn = null ;
112+ }
113+ } ,
103114 } ;
104115
105116 return {
106117 withinLWCVisitors,
107118 isInsideReachableMethod : ( ) => insideLWC && ! ! reachableMethodThatWeAreIn ,
108119 isInsideReachableFunction : ( ) => ! ! reachableFunctionThatWeAreIn ,
109- isInsideSkippedBlock : ( ) => ! ! skippedBlockThatWeAreIn ,
120+ isInsideSkippedBlock : ( ) => ! ! skippedBlockThatWeAreIn || ! ! skippedConditionThatWeAreIn ,
110121 } ;
111122}
112123
@@ -159,18 +170,21 @@ module.exports.noReferenceDuringSSR = function noReferenceDuringSSR(
159170
160171 if (
161172 node . parent . type === 'MemberExpression' &&
162- node . parent . optional !== true &&
163173 node . object . type === 'Identifier' &&
164- node . object . name === 'globalThis' &&
174+ ( ( node . object . name === 'globalThis' && node . parent . optional !== true ) ||
175+ node . object . name === 'window' ) &&
165176 node . property . type === 'Identifier' &&
166177 forbiddenGlobalNames . has ( node . property . name ) &&
167178 isGlobalIdentifier ( node . object , context . getScope ( ) )
168179 ) {
169180 // Prevents expressions like:
170181 // globalThis.document.addEventListener('click', () => { ... });
182+ // const url = window.location.href;
183+ // const url = window.location?.href;
171184
172185 // Allows expressions like:
173186 // globalThis.document?.addEventListener('click', () => { ... });
187+ // const url = globalThis.location?.href;
174188 context . report ( {
175189 messageId : messageIds . at ( 1 ) ,
176190 node,
@@ -182,17 +196,33 @@ module.exports.noReferenceDuringSSR = function noReferenceDuringSSR(
182196 } else if (
183197 node . parent . type !== 'MemberExpression' &&
184198 node . object . type === 'Identifier' &&
185- ( forbiddenGlobalNames . has ( node . object . name ) ||
186- ( node . object . name === 'globalThis' && node . optional !== true ) ) &&
199+ forbiddenGlobalNames . has ( node . object . name ) &&
187200 isGlobalIdentifier ( node . object , context . getScope ( ) )
188201 ) {
189202 // Prevents expressions like:
190- // globalThis.addEventListener('click', () => { ... });
191203 // document.addEventListener('click', () => { ... });
192204 // document?.addEventListener('click', () => { ... });
205+ context . report ( {
206+ messageId : messageIds . at ( 0 ) ,
207+ node,
208+ data : {
209+ identifier : node . object . name ,
210+ } ,
211+ } ) ;
212+ } else if (
213+ node . parent . type === 'CallExpression' &&
214+ node . parent . optional !== true &&
215+ node . object . type === 'Identifier' &&
216+ node . object . name === 'globalThis' &&
217+ node . property . type === 'Identifier' &&
218+ forbiddenGlobalNames . has ( node . property . name ) &&
219+ isGlobalIdentifier ( node . object , context . getScope ( ) )
220+ ) {
221+ // Prevents expressions like:
222+ // globalThis.addEventListener('click', () => { ... });
193223
194224 // Allows expressions like:
195- // globalThis? .addEventListener('click', () => { ... });
225+ // globalThis.addEventListener?. ('click', () => { ... });
196226 context . report ( {
197227 messageId : messageIds . at ( 0 ) ,
198228 node,
@@ -247,37 +277,14 @@ module.exports.noPropertyAccessDuringSSR = function noPropertyAccessDuringSSR(
247277) {
248278 const { withinLWCVisitors, isInsideReachableMethod, isInsideSkippedBlock } =
249279 reachableDuringSSRPartial ( ) ;
250- let expressionStatementWeAreIn = null ;
251- let memberExpressionsInStatement = [ ] ;
252- let callExpressionsInStatement = [ ] ;
253280
254281 return {
255282 ...withinLWCVisitors ,
256- ExpressionStatement : ( node ) => {
257- expressionStatementWeAreIn = node ;
258- callExpressionsInStatement = [ ] ;
259- memberExpressionsInStatement = [ ] ;
260- } ,
261- 'ExpressionStatement:exit' : ( node ) => {
262- if ( expressionStatementWeAreIn === node ) {
263- expressionStatementWeAreIn = null ;
264- callExpressionsInStatement = [ ] ;
265- memberExpressionsInStatement = [ ] ;
266- }
267- } ,
268- AssignmentExpression : ( node ) => {
269- callExpressionsInStatement . push ( node ) ;
270- } ,
271- CallExpression : ( node ) => {
272- callExpressionsInStatement . push ( node ) ;
273- } ,
274283 MemberExpression : ( node ) => {
275284 if ( ! isInsideReachableMethod ( ) || isInsideSkippedBlock ( ) ) {
276285 return ;
277286 }
278287
279- memberExpressionsInStatement . push ( node ) ;
280-
281288 if (
282289 node . object . type === 'ThisExpression' &&
283290 globalAccessQualifiers . has ( node . parent . type ) &&
@@ -289,30 +296,11 @@ module.exports.noPropertyAccessDuringSSR = function noPropertyAccessDuringSSR(
289296 // this.querySelector('button').addEventListener('click', ...);
290297 // this.querySelector?.('button').addEventListener('click', ...);
291298 // this.querySelector?.('button')?.addEventListener('click', ...);
299+ // this.querySelector?.('button')?.addEventListener?.('click', ...);
292300 // this.querySelector?.('button').firstElementChild.id;
301+ // this.querySelector?.('button').firstElementChild?.id;
293302 // this.childNodes.item(0).textContent = 'foo';
294-
295- // Allows all-optional expressions like:
296- // this.dispatchEvent?.(new CustomEvent('myevent'));
297- // this.querySelector?.('button')?.addEventListener?.('click', ...);
298- // this.querySelector?.('button')?.firstElementChild.id;
299- const allCallExpressionsOptional = callExpressionsInStatement . every (
300- ( expression ) => expression . optional ,
301- ) ;
302- const allMemberExpressionsOptional = memberExpressionsInStatement . every (
303- ( expression , index ) => {
304- if ( expression . parent && expression . parent . type === 'CallExpression' ) {
305- // Skip CallExpressions here as they are treated separately
306- return true ;
307- }
308- // Return `true` if the MemberExpression is either `optional` or
309- // the last expression of the chain (which is in revered order).
310- return expression . optional || index === 0 ;
311- } ,
312- ) ;
313- if ( ! allCallExpressionsOptional || ! allMemberExpressionsOptional ) {
314- reporter ( node ) ;
315- }
303+ reporter ( node ) ;
316304 }
317305 } ,
318306 } ;
0 commit comments