Skip to content

Commit 36492ce

Browse files
mydeaLms24
andauthored
ref: Avoid optional chaining & add eslint rule (#6777)
As this is transpiled to a rather verbose form. Co-authored-by: Lukas Stracke <[email protected]>
1 parent 0122a9f commit 36492ce

File tree

27 files changed

+162
-54
lines changed

27 files changed

+162
-54
lines changed

packages/browser/src/client.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ export class BrowserClient extends BaseClient<BrowserClientOptions> {
9696
const breadcrumbIntegration = this.getIntegrationById(BREADCRUMB_INTEGRATION_ID) as Breadcrumbs | undefined;
9797
// We check for definedness of `addSentryBreadcrumb` in case users provided their own integration with id
9898
// "Breadcrumbs" that does not have this function.
99-
breadcrumbIntegration?.addSentryBreadcrumb?.(event);
99+
if (breadcrumbIntegration && breadcrumbIntegration.addSentryBreadcrumb) {
100+
breadcrumbIntegration.addSentryBreadcrumb(event);
101+
}
100102

101103
super.sendEvent(event, hint);
102104
}

packages/eslint-config-sdk/src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,9 @@ module.exports = {
143143
},
144144
],
145145

146-
// We want to prevent async await usage in our files to prevent uncessary bundle size. Turned off in tests.
146+
// We want to prevent async await & optional chaining usage in our files to prevent uncessary bundle size. Turned off in tests.
147147
'@sentry-internal/sdk/no-async-await': 'error',
148+
'@sentry-internal/sdk/no-optional-chaining': 'error',
148149

149150
// JSDOC comments are required for classes and methods. As we have a public facing codebase, documentation,
150151
// even if it may seems excessive at times, is important to emphasize. Turned off in tests.
@@ -178,6 +179,7 @@ module.exports = {
178179
'@typescript-eslint/no-non-null-assertion': 'off',
179180
'@typescript-eslint/no-empty-function': 'off',
180181
'@sentry-internal/sdk/no-async-await': 'off',
182+
'@sentry-internal/sdk/no-optional-chaining': 'off',
181183
},
182184
},
183185
{

packages/eslint-plugin-sdk/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
module.exports = {
1212
rules: {
1313
'no-async-await': require('./rules/no-async-await'),
14+
'no-optional-chaining': require('./rules/no-optional-chaining'),
1415
'no-eq-empty': require('./rules/no-eq-empty'),
1516
},
1617
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @fileoverview Rule to disallow using optional chaining - because this is transpiled into verbose code.
3+
* @author Francesco Novy
4+
*
5+
* Based on https://github.com/facebook/lexical/pull/3233
6+
*/
7+
'use strict';
8+
9+
// ------------------------------------------------------------------------------
10+
// Rule Definition
11+
// ------------------------------------------------------------------------------
12+
13+
/** @type {import('eslint').Rule.RuleModule} */
14+
module.exports = {
15+
meta: {
16+
type: 'problem',
17+
docs: {
18+
description: 'disallow usage of optional chaining',
19+
category: 'Best Practices',
20+
recommended: true,
21+
},
22+
messages: {
23+
forbidden: 'Avoid using optional chaining',
24+
},
25+
fixable: null,
26+
schema: [],
27+
},
28+
29+
create(context) {
30+
const sourceCode = context.getSourceCode();
31+
32+
/**
33+
* Checks if the given token is a `?.` token or not.
34+
* @param {Token} token The token to check.
35+
* @returns {boolean} `true` if the token is a `?.` token.
36+
*/
37+
function isQuestionDotToken(token) {
38+
return (
39+
token.value === '?.' &&
40+
(token.type === 'Punctuator' || // espree has been parsed well.
41+
// [email protected] doesn't parse "?." tokens well. Therefore, get the string from the source code and check it.
42+
sourceCode.getText(token) === '?.')
43+
);
44+
}
45+
46+
return {
47+
'CallExpression[optional=true]'(node) {
48+
context.report({
49+
messageId: 'forbidden',
50+
node: sourceCode.getTokenAfter(node.callee, isQuestionDotToken),
51+
});
52+
},
53+
'MemberExpression[optional=true]'(node) {
54+
context.report({
55+
messageId: 'forbidden',
56+
node: sourceCode.getTokenAfter(node.object, isQuestionDotToken),
57+
});
58+
},
59+
};
60+
},
61+
};

packages/nextjs/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
extends: ['../../.eslintrc.js'],
1111
rules: {
1212
'@sentry-internal/sdk/no-async-await': 'off',
13+
'@sentry-internal/sdk/no-optional-chaining': 'off',
1314
},
1415
overrides: [
1516
{

packages/node/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ module.exports = {
55
extends: ['../../.eslintrc.js'],
66
rules: {
77
'@sentry-internal/sdk/no-async-await': 'off',
8+
'@sentry-internal/sdk/no-optional-chaining': 'off',
89
},
910
};

packages/opentelemetry-node/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ module.exports = {
55
extends: ['../../.eslintrc.js'],
66
rules: {
77
'@sentry-internal/sdk/no-async-await': 'off',
8+
'@sentry-internal/sdk/no-optional-chaining': 'off',
89
},
910
};

packages/react/src/reactrouterv6.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ export function wrapUseRoutes(origUseRoutes: UseRoutes): UseRoutes {
226226

227227
// A value with stable identity to either pick `locationArg` if available or `location` if not
228228
const stableLocationParam =
229-
typeof locationArg === 'string' || locationArg?.pathname !== undefined
229+
typeof locationArg === 'string' || (locationArg && locationArg.pathname)
230230
? (locationArg as { pathname: string })
231231
: location;
232232

packages/remix/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
extends: ['../../.eslintrc.js'],
1111
rules: {
1212
'@sentry-internal/sdk/no-async-await': 'off',
13+
'@sentry-internal/sdk/no-optional-chaining': 'off',
1314
},
1415
overrides: [
1516
{

packages/replay/src/coreHandlers/handleGlobalEvent.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ export function handleGlobalEventListener(replay: ReplayContainer): (event: Even
2121

2222
// Unless `captureExceptions` is enabled, we want to ignore errors coming from rrweb
2323
// As there can be a bunch of stuff going wrong in internals there, that we don't want to bubble up to users
24-
if (isRrwebError(event) && !replay.getOptions()._experiments?.captureExceptions) {
24+
if (isRrwebError(event) && !replay.getOptions()._experiments.captureExceptions) {
2525
__DEBUG_BUILD__ && logger.log('[Replay] Ignoring error from rrweb internals', event);
2626
return null;
2727
}
2828

2929
// Only tag transactions with replayId if not waiting for an error
3030
// @ts-ignore private
3131
if (!event.type || replay.recordingMode === 'session') {
32-
event.tags = { ...event.tags, replayId: replay.session?.id };
32+
event.tags = { ...event.tags, replayId: replay.getSessionId() };
3333
}
3434

3535
// Collect traceIds in _context regardless of `recordingMode` - if it's true,
@@ -44,12 +44,10 @@ export function handleGlobalEventListener(replay: ReplayContainer): (event: Even
4444
replay.getContext().errorIds.add(event.event_id as string);
4545
}
4646

47-
const exc = event.exception?.values?.[0];
48-
if (__DEBUG_BUILD__ && replay.getOptions()._experiments?.traceInternals) {
47+
if (__DEBUG_BUILD__ && replay.getOptions()._experiments.traceInternals) {
48+
const exc = getEventExceptionValues(event);
4949
addInternalBreadcrumb({
50-
message: `Tagging event (${event.event_id}) - ${event.message} - ${exc?.type || 'Unknown'}: ${
51-
exc?.value || 'n/a'
52-
}`,
50+
message: `Tagging event (${event.event_id}) - ${event.message} - ${exc.type}: ${exc.value}`,
5351
});
5452
}
5553

@@ -89,3 +87,11 @@ function addInternalBreadcrumb(arg: Parameters<typeof addBreadcrumb>[0]): void {
8987
...rest,
9088
});
9189
}
90+
91+
function getEventExceptionValues(event: Event): { type: string; value: string } {
92+
return {
93+
type: 'Unknown',
94+
value: 'n/a',
95+
...(event.exception && event.exception.values && event.exception.values[0]),
96+
};
97+
}

0 commit comments

Comments
 (0)