Skip to content

Commit e2612bc

Browse files
authored
feat: update rule no-browser-globals-during-ssr to be more open (#138)
1 parent a05f7df commit e2612bc

File tree

2 files changed

+165
-40
lines changed

2 files changed

+165
-40
lines changed

lib/rule-helpers.js

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const { isGlobalIdentifier } = require('./util/scope');
1414
*/
1515
function reachableDuringSSRPartial() {
1616
let moduleInfo;
17-
let insideLWC = false;
1817
let reachableFunctionThatWeAreIn = null;
1918
let reachableMethodThatWeAreIn = null;
2019
let skippedBlockThatWeAreIn = null;
@@ -24,22 +23,16 @@ function reachableDuringSSRPartial() {
2423
Program: (node) => {
2524
moduleInfo = analyze(node);
2625
},
27-
ClassDeclaration: (node) => {
28-
if (node === moduleInfo.lwcClassDeclaration) {
29-
insideLWC = true;
30-
}
31-
},
32-
'ClassDeclaration:exit': (node) => {
33-
if (node === moduleInfo.lwcClassDeclaration) {
34-
insideLWC = false;
35-
}
36-
},
3726
FunctionDeclaration: (node) => {
3827
if (
3928
!reachableFunctionThatWeAreIn &&
4029
node.id &&
4130
node.id.type === 'Identifier' &&
42-
moduleInfo.moduleScopedFunctionsReachableDuringSSR.has(node.id && node.id.name)
31+
((moduleInfo.isLWC &&
32+
moduleInfo.moduleScopedFunctionsReachableDuringSSR.has(
33+
node.id && node.id.name,
34+
)) ||
35+
!moduleInfo.isLWC)
4336
) {
4437
reachableFunctionThatWeAreIn = node;
4538
}
@@ -54,7 +47,9 @@ function reachableDuringSSRPartial() {
5447
!reachableFunctionThatWeAreIn &&
5548
node.parent.type === 'VariableDeclarator' &&
5649
node.parent.id.type === 'Identifier' &&
57-
moduleInfo.moduleScopedFunctionsReachableDuringSSR.has(node.parent.id.name)
50+
((moduleInfo.isLWC &&
51+
moduleInfo.moduleScopedFunctionsReachableDuringSSR.has(node.parent.id.name)) ||
52+
!moduleInfo.isLWC)
5853
) {
5954
reachableFunctionThatWeAreIn = node;
6055
}
@@ -69,7 +64,9 @@ function reachableDuringSSRPartial() {
6964
!reachableFunctionThatWeAreIn &&
7065
node.parent.type === 'VariableDeclarator' &&
7166
node.parent.id.type === 'Identifier' &&
72-
moduleInfo.moduleScopedFunctionsReachableDuringSSR.has(node.parent.id.name)
67+
((moduleInfo.isLWC &&
68+
moduleInfo.moduleScopedFunctionsReachableDuringSSR.has(node.parent.id.name)) ||
69+
!moduleInfo.isLWC)
7370
) {
7471
reachableFunctionThatWeAreIn = node;
7572
}
@@ -81,9 +78,9 @@ function reachableDuringSSRPartial() {
8178
},
8279
MethodDefinition: (node) => {
8380
if (
84-
insideLWC &&
8581
node.key.type === 'Identifier' &&
86-
moduleInfo.methodsReachableDuringSSR.has(node.key.name)
82+
((moduleInfo.isLWC && moduleInfo.methodsReachableDuringSSR.has(node.key.name)) ||
83+
!moduleInfo.isLWC)
8784
) {
8885
reachableMethodThatWeAreIn = node;
8986
}
@@ -115,7 +112,7 @@ function reachableDuringSSRPartial() {
115112

116113
return {
117114
withinLWCVisitors,
118-
isInsideReachableMethod: () => insideLWC && !!reachableMethodThatWeAreIn,
115+
isInsideReachableMethod: () => !!reachableMethodThatWeAreIn,
119116
isInsideReachableFunction: () => !!reachableFunctionThatWeAreIn,
120117
isInsideSkippedBlock: () => !!skippedBlockThatWeAreIn || !!skippedConditionThatWeAreIn,
121118
};
@@ -193,22 +190,6 @@ module.exports.noReferenceDuringSSR = function noReferenceDuringSSR(
193190
property: node.parent.property.name,
194191
},
195192
});
196-
} else if (
197-
node.parent.type !== 'MemberExpression' &&
198-
node.object.type === 'Identifier' &&
199-
forbiddenGlobalNames.has(node.object.name) &&
200-
isGlobalIdentifier(node.object, context.getScope())
201-
) {
202-
// Prevents expressions like:
203-
// document.addEventListener('click', () => { ... });
204-
// document?.addEventListener('click', () => { ... });
205-
context.report({
206-
messageId: messageIds.at(0),
207-
node,
208-
data: {
209-
identifier: node.object.name,
210-
},
211-
});
212193
} else if (
213194
node.parent.type === 'CallExpression' &&
214195
node.parent.optional !== true &&
@@ -233,6 +214,25 @@ module.exports.noReferenceDuringSSR = function noReferenceDuringSSR(
233214
: node.object.name,
234215
},
235216
});
217+
} else if (
218+
node.parent.type !== 'MemberExpression' &&
219+
node.object.type === 'Identifier' &&
220+
forbiddenGlobalNames.has(node.object.name) &&
221+
isGlobalIdentifier(node.object, context.getScope())
222+
) {
223+
// Prevents expressions like:
224+
// window.addEventListener('click', () => { ... });
225+
// window?.addEventListener('click', () => { ... });
226+
// document.addEventListener('click', () => { ... });
227+
// document?.addEventListener('click', () => { ... });
228+
context.report({
229+
messageId: messageIds.at(0),
230+
node,
231+
data: {
232+
identifier:
233+
node.object.name === 'window' ? node.property.name : node.object.name,
234+
},
235+
});
236236
}
237237
},
238238
Identifier: (node) => {
@@ -244,6 +244,7 @@ module.exports.noReferenceDuringSSR = function noReferenceDuringSSR(
244244
) {
245245
return;
246246
}
247+
247248
if (
248249
noReferenceParentQualifiers.has(node.parent.type) &&
249250
forbiddenGlobalNames.has(node.name) &&
@@ -255,6 +256,43 @@ module.exports.noReferenceDuringSSR = function noReferenceDuringSSR(
255256

256257
// Allows expressions like:
257258
// doSomethingWith(globalThis)
259+
context.report({
260+
messageId: messageIds.at(0),
261+
node,
262+
data: {
263+
identifier: node.name,
264+
},
265+
});
266+
} else if (
267+
node.parent.type === 'BinaryExpression' &&
268+
node.parent.operator === 'instanceof' &&
269+
node.parent.right === node &&
270+
forbiddenGlobalNames.has(node.name) &&
271+
isGlobalIdentifier(node, context.getScope())
272+
) {
273+
// Prevents expressions like:
274+
// if (value instanceof Element) { ... }
275+
// value instanceof Element ? doX() : doY();
276+
context.report({
277+
messageId: messageIds.at(0),
278+
node,
279+
data: {
280+
identifier: node.name,
281+
},
282+
});
283+
}
284+
},
285+
ExpressionStatement: (node) => {
286+
if (
287+
node.parent.type === 'Program' &&
288+
node.expression.type === 'CallExpression' &&
289+
node.expression.callee.type === 'Identifier' &&
290+
forbiddenGlobalNames.has(node.expression.callee.name) &&
291+
isGlobalIdentifier(node, context.getScope())
292+
) {
293+
// Prevents global expressions like:
294+
// addEventListener('resize', () => {...});
295+
258296
context.report({
259297
messageId: messageIds.at(0),
260298
node,

test/lib/rules/no-restricted-browser-globals-during-ssr.js

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ tester.run('no-browser-globals-during-ssr', rule, {
454454
errors: [
455455
{
456456
messageId: 'prohibitedBrowserAPIUsage',
457-
data: { identifier: 'window' },
457+
data: { identifier: 'foo' },
458458
},
459459
],
460460
},
@@ -475,7 +475,7 @@ tester.run('no-browser-globals-during-ssr', rule, {
475475
errors: [
476476
{
477477
messageId: 'prohibitedBrowserAPIUsage',
478-
data: { identifier: 'window' },
478+
data: { identifier: 'bar' },
479479
},
480480
],
481481
},
@@ -534,7 +534,7 @@ tester.run('no-browser-globals-during-ssr', rule, {
534534
errors: [
535535
{
536536
messageId: 'prohibitedBrowserAPIUsage',
537-
data: { identifier: 'window' },
537+
data: { identifier: 'x' },
538538
},
539539
],
540540
},
@@ -553,7 +553,7 @@ tester.run('no-browser-globals-during-ssr', rule, {
553553
errors: [
554554
{
555555
messageId: 'prohibitedBrowserAPIUsage',
556-
data: { identifier: 'window' },
556+
data: { identifier: 'x' },
557557
},
558558
],
559559
},
@@ -572,7 +572,7 @@ tester.run('no-browser-globals-during-ssr', rule, {
572572
errors: [
573573
{
574574
messageId: 'prohibitedBrowserAPIUsage',
575-
data: { identifier: 'window' },
575+
data: { identifier: 'x' },
576576
},
577577
],
578578
},
@@ -590,7 +590,7 @@ tester.run('no-browser-globals-during-ssr', rule, {
590590
errors: [
591591
{
592592
messageId: 'prohibitedBrowserAPIUsage',
593-
data: { identifier: 'window' },
593+
data: { identifier: 'x' },
594594
},
595595
],
596596
},
@@ -605,7 +605,7 @@ tester.run('no-browser-globals-during-ssr', rule, {
605605
errors: [
606606
{
607607
messageId: 'prohibitedBrowserAPIUsage',
608-
data: { identifier: 'window' },
608+
data: { identifier: 'x' },
609609
},
610610
],
611611
},
@@ -824,5 +824,92 @@ tester.run('no-browser-globals-during-ssr', rule, {
824824
},
825825
],
826826
},
827+
{
828+
code: `
829+
function utility() {
830+
window.fuzzAround();
831+
}
832+
833+
export default class Foo {
834+
bar() {
835+
utility();
836+
}
837+
}
838+
`,
839+
errors: [
840+
{
841+
messageId: 'prohibitedBrowserAPIUsage',
842+
data: { identifier: 'fuzzAround' },
843+
},
844+
],
845+
},
846+
{
847+
code: `addEventListener('resize', () => {});`,
848+
errors: [
849+
{
850+
messageId: 'prohibitedBrowserAPIUsage',
851+
data: { identifier: 'addEventListener' },
852+
},
853+
],
854+
},
855+
{
856+
code: `
857+
function utility() {
858+
console.log(window.x);
859+
}
860+
`,
861+
errors: [
862+
{
863+
messageId: 'prohibitedBrowserAPIUsage',
864+
data: { identifier: 'x' },
865+
},
866+
],
867+
},
868+
{
869+
code: `
870+
function getAttributes(element) {
871+
if (element instanceof Element) {
872+
return element.getAttributeNames();
873+
}
874+
return [];
875+
}
876+
`,
877+
errors: [
878+
{
879+
messageId: 'prohibitedBrowserAPIUsage',
880+
data: { identifier: 'Element' },
881+
},
882+
],
883+
},
884+
{
885+
code: `
886+
function getAttributes(element) {
887+
return element instanceof Element ? element.getAttributeNames() : [];
888+
}
889+
`,
890+
errors: [
891+
{
892+
messageId: 'prohibitedBrowserAPIUsage',
893+
data: { identifier: 'Element' },
894+
},
895+
],
896+
},
897+
{
898+
code: `
899+
function getJson(response) {
900+
if (response instanceof Response) {
901+
return response.json();
902+
}
903+
return Promise.resolve();
904+
}
905+
`,
906+
options: [{ 'restricted-globals': { Response: true } }],
907+
errors: [
908+
{
909+
messageId: 'prohibitedBrowserAPIUsage',
910+
data: { identifier: 'Response' },
911+
},
912+
],
913+
},
827914
],
828915
});

0 commit comments

Comments
 (0)