Skip to content

Commit ad7b8f2

Browse files
andreiborzaLms24
andauthored
ref(eslint): Set @typescript-eslint/no-unsafe-member-access rule to error (#892)
--------- Co-authored-by: Lukas Stracke <[email protected]>
1 parent 95af834 commit ad7b8f2

File tree

12 files changed

+116
-49
lines changed

12 files changed

+116
-49
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ module.exports = {
5858
'@typescript-eslint/ban-ts-comment': 'error',
5959
'@typescript-eslint/no-unsafe-call': 'error',
6060
'@typescript-eslint/restrict-template-expressions': 'error',
61-
'@typescript-eslint/no-unsafe-member-access': 'warn',
61+
'@typescript-eslint/no-unsafe-member-access': 'error',
6262
'@typescript-eslint/no-unsafe-assignment': 'warn',
6363
'@typescript-eslint/no-unsafe-argument': 'warn',
6464
'@typescript-eslint/no-unsafe-return': 'error',

lib/Helper/Package.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import { satisfies, subset, valid, validRange } from 'semver';
22

33
import { green, red } from './Logging';
44

5+
type PackageDotJson = {
6+
dependencies?: Record<string, string>;
7+
devDependencies?: Record<string, string>;
8+
};
9+
510
export function checkPackageVersion(
6-
appPackage: {
7-
dependencies?: Record<string, string>;
8-
devDependencies?: Record<string, string>;
9-
},
11+
appPackage: PackageDotJson,
1012
packageName: string,
1113
acceptableVersions: string,
1214
canBeLatest: boolean,
@@ -71,10 +73,10 @@ function fulfillsVersionRange(
7173
* @returns `true` if the package is installed, `false` otherwise
7274
*/
7375
export function hasPackageInstalled(
74-
appPackage: Record<string, any>,
76+
appPackage: PackageDotJson,
7577
packageName: string,
7678
): boolean {
77-
const depsVersion = appPackage.dependencies[packageName];
78-
const devDepsVersion = appPackage.devDependencies[packageName];
79+
const depsVersion = appPackage.dependencies?.[packageName];
80+
const devDepsVersion = appPackage.devDependencies?.[packageName];
7981
return !!depsVersion || !!devDepsVersion;
8082
}

lib/Helper/SentryCli.ts

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,42 @@ import * as path from 'node:path';
55
import type { Args } from '../Constants';
66
import { addToGitignore } from './Git';
77
import { green, l, nl, red } from './Logging';
8+
import { Config } from '../Types';
89

910
const SENTRYCLIRC_FILENAME = '.sentryclirc';
1011
const GITIGNORE_FILENAME = '.gitignore';
1112
const PROPERTIES_FILENAME = 'sentry.properties';
1213

1314
export interface SentryCliProps {
14-
[s: string]: string;
15+
'defaults/url': string;
16+
'defaults/org': string | null;
17+
'defaults/project': string | null;
18+
'auth/token': string | null;
19+
'cli/executable'?: string;
1520
}
1621

17-
type SentryCliConfig = Record<string, SentryCliProps>;
22+
type SentryCliConfig = Record<string, Partial<SentryCliProps>>;
23+
type RequireResolve = typeof require.resolve;
1824

1925
export class SentryCli {
20-
// eslint-disable-next-line @typescript-eslint/typedef
21-
private _resolve = require.resolve;
26+
private _resolve: RequireResolve = require.resolve;
2227

2328
public constructor(protected _argv: Args) {}
2429

25-
public setResolveFunction(resolve: (path: string) => string): void {
26-
this._resolve = resolve as any;
30+
public setResolveFunction(resolve: RequireResolve): void {
31+
this._resolve = resolve;
2732
}
2833

29-
public convertAnswersToProperties(answers: Answers): SentryCliProps {
30-
const props: SentryCliProps = {};
31-
props['defaults/url'] = this._argv.url;
32-
props['defaults/org'] = answers.config?.organization?.slug ?? null;
33-
props['defaults/project'] = answers.config?.project?.slug ?? null;
34-
props['auth/token'] = answers.config?.auth?.token ?? null;
34+
public convertAnswersToProperties(
35+
answers: Answers & { config?: Config },
36+
): SentryCliProps {
37+
const props: SentryCliProps = {
38+
'defaults/url': this._argv.url,
39+
'defaults/org': answers.config?.organization?.slug ?? null,
40+
'defaults/project': answers.config?.project?.slug ?? null,
41+
'auth/token': answers.config?.auth?.token ?? null,
42+
};
43+
3544
try {
3645
const cliPath = this._resolve('@sentry/cli/bin/sentry-cli', {
3746
paths: [process.cwd()],
@@ -45,11 +54,26 @@ export class SentryCli {
4554
return props;
4655
}
4756

48-
/** Create the contents of a `sentry.properties` file */
49-
public dumpProperties(props: SentryCliProps): string {
50-
const rv = [];
51-
for (const [key, value] of Object.entries(props)) {
52-
const normalizedKey = key.replace(/\//g, '.');
57+
/**
58+
* Create the contents of a `sentry.properties` file
59+
* @param props the properties to write to the file
60+
* @param format the format of the file, either `rc`
61+
* (.sentryclirc) or `properties` (sentry.properties)
62+
*/
63+
public dumpProperties(
64+
props: Partial<SentryCliProps>,
65+
format: 'rc' | 'properties' = 'properties',
66+
): string {
67+
const propEntries = Object.entries(props) as [
68+
keyof SentryCliProps,
69+
SentryCliProps[keyof SentryCliProps],
70+
][];
71+
const rv: string[] = [];
72+
for (const [key, value] of propEntries) {
73+
const normalizedKey =
74+
format === 'properties'
75+
? key.replace(/\//g, '.')
76+
: key.split('/').at(1) ?? '';
5377
if (value === undefined || value === null) {
5478
// comment that property out since it has no value
5579
rv.push(`#${normalizedKey}=`);
@@ -61,10 +85,10 @@ export class SentryCli {
6185
return rv.join('\n') + '\n';
6286
}
6387

64-
public dumpConfig(config: SentryCliConfig): string {
88+
public dumpConfig(config: Partial<SentryCliConfig>): string {
6589
const dumpedSections: string[] = [];
66-
for (const [sectionName, val] of Object.entries(config)) {
67-
const props = this.dumpProperties(val);
90+
for (const [sectionName, values] of Object.entries(config)) {
91+
const props = values ? this.dumpProperties(values, 'rc') : '';
6892
const section = `[${sectionName}]\n${props}`;
6993
dumpedSections.push(section);
7094
}
@@ -94,7 +118,7 @@ export class SentryCli {
94118
try {
95119
await fs.promises.appendFile(
96120
SENTRYCLIRC_FILENAME,
97-
this.dumpConfig({ auth: { token: authToken } }),
121+
this.dumpConfig({ auth: { 'auth/token': authToken } }),
98122
);
99123
green(`✓ Successfully added the auth token to ${SENTRYCLIRC_FILENAME}`);
100124
} catch {

lib/Helper/Wizard.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ export async function startWizard<M extends IStep>(
5151
const answers = await step.emit(prevAnswer);
5252
return { ...prevAnswer, ...answers };
5353
}, Promise.resolve({}));
54-
} catch (e) {
54+
} catch (e: unknown) {
5555
BottomBar.hide();
5656
nl();
5757
red('Sentry Wizard failed with:');
58-
red(argv.debug ? e : e.message);
58+
red(argv.debug && e instanceof Error ? e.message : String(e));
5959
nl();
6060
red('Protip: Add --debug to see whats going on');
6161
red('OR use --help to see your options');

lib/Helper/__tests__/SentryCli.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import type { Args } from '../../Constants';
55
import { Integration, Platform } from '../../Constants';
66
import { SentryCli } from '../SentryCli';
77

8+
type RequireResolve = typeof require.resolve;
9+
810
const args: Args = {
911
debug: false,
1012
integration: Integration.reactNative,
@@ -32,8 +34,10 @@ const demoAnswers: Answers = {
3234
};
3335

3436
describe('SentryCli', () => {
37+
const resolveFunc = jest
38+
.fn()
39+
.mockReturnValue('node_modules/sentry/cli') as unknown as RequireResolve;
3540
test('convertAnswersToProperties', () => {
36-
const resolveFunc = jest.fn().mockReturnValue('node_modules/sentry/cli');
3741
const sentry = new SentryCli(args);
3842
sentry.setResolveFunction(resolveFunc);
3943
const props = sentry.convertAnswersToProperties(demoAnswers);
@@ -45,7 +49,6 @@ describe('SentryCli', () => {
4549
});
4650

4751
test('dump properties', () => {
48-
const resolveFunc = jest.fn().mockReturnValue('node_modules/sentry/cli');
4952
const sentry = new SentryCli(args);
5053
sentry.setResolveFunction(resolveFunc);
5154
const props = sentry.convertAnswersToProperties(demoAnswers);
@@ -58,8 +61,25 @@ cli.executable=node_modules/sentry/cli
5861
`);
5962
});
6063

64+
test('dump config', () => {
65+
const sentry = new SentryCli(args);
66+
sentry.setResolveFunction(resolveFunc);
67+
const props = {
68+
auth: { 'auth/token': 'myToken' },
69+
};
70+
expect(sentry.dumpConfig(props)).toMatchInlineSnapshot(`
71+
"[auth]
72+
token=myToken
73+
"
74+
`);
75+
});
76+
6177
test('convertAnswersToProperties windows', () => {
62-
const resolveFunc = jest.fn().mockReturnValue('node_modules\\sentry\\cli');
78+
const resolveFunc = jest
79+
.fn()
80+
.mockReturnValue(
81+
'node_modules\\sentry\\cli',
82+
) as unknown as RequireResolve;
6383
const sentry = new SentryCli(args);
6484
sentry.setResolveFunction(resolveFunc);
6585
const props = sentry.convertAnswersToProperties(demoAnswers);
@@ -71,7 +91,11 @@ cli.executable=node_modules/sentry/cli
7191
});
7292

7393
test('dump properties windows', () => {
74-
const resolveFunc = jest.fn().mockReturnValue('node_modules\\sentry\\cli');
94+
const resolveFunc = jest
95+
.fn()
96+
.mockReturnValue(
97+
'node_modules\\sentry\\cli',
98+
) as unknown as RequireResolve;
7599
const sentry = new SentryCli(args);
76100
sentry.setResolveFunction(resolveFunc);
77101
const props = sentry.convertAnswersToProperties(demoAnswers);

lib/Setup.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { Answers } from 'inquirer';
12
import { enableDebugLogs } from '../src/utils/debug';
3+
import { Args } from './Constants';
24

35
import { readEnvironment } from './Helper/Env';
46
import { startWizard } from './Helper/Wizard';
@@ -10,7 +12,7 @@ import * as Step from './Steps';
1012
* Therefor, do not call this function anymore.
1113
* Use `run` from {@link ../src/run.ts} instead.
1214
*/
13-
export async function run(argv: any): Promise<any> {
15+
export async function run(argv: Args): Promise<Answers> {
1416
const args = { ...argv, ...readEnvironment() };
1517

1618
if (argv.debug) {

lib/Steps/Integrations/Electron.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { Args } from '../../Constants';
77
import { dim, green, l, nl, red } from '../../Helper/Logging';
88
import { SentryCli } from '../../Helper/SentryCli';
99
import { BaseIntegration } from './BaseIntegration';
10+
import { Config } from '../../Types';
1011

1112
const MIN_ELECTRON_VERSION_STRING = '23.0.0';
1213
const MIN_ELECTRON_VERSION = parseInt(
@@ -63,8 +64,8 @@ export class Electron extends BaseIntegration {
6364
}
6465

6566
// eslint-disable-next-line @typescript-eslint/require-await
66-
public async emit(answers: Answers): Promise<Answers> {
67-
const dsn = answers.config?.dsn?.public ?? null;
67+
public async emit(answers: Answers & { config?: Config }): Promise<Answers> {
68+
const dsn = answers.config?.dsn?.public ?? 'DSN NOT FOUND';
6869
nl();
6970

7071
const sentryCliProps = this._sentryCli.convertAnswersToProperties(answers);

lib/Steps/PromptForParameters.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import { prompt } from 'inquirer';
44
import { dim } from '../Helper/Logging';
55
import { getCurrentIntegration } from '../Helper/Wizard';
66
import { BaseStep } from './BaseStep';
7-
8-
type Config = {
9-
organization?: { slug?: string };
10-
project?: { slug?: string };
11-
dsn?: { public?: string };
12-
auth?: { token?: string };
13-
};
7+
import { Config } from '../Types';
148

159
export class PromptForParameters extends BaseStep {
1610
public async emit(

lib/Types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type Config = {
2+
organization?: { slug?: string };
3+
project?: { slug?: string };
4+
dsn?: { public?: string };
5+
auth?: { token?: string };
6+
};

src/nuxt/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export async function isNuxtV4(
2222
// major yet. We must read the `compatibilityVersion`
2323
// from the nuxt config.
2424
try {
25+
// Magicast is hard to type correctly so we disable these rules
26+
/* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
2527
const mod = await loadFile(nuxtConfig);
2628
const config =
2729
mod.exports.default.$type === 'function-call'
@@ -31,6 +33,7 @@ export async function isNuxtV4(
3133
if (config && config.future && config.future.compatibilityVersion === 4) {
3234
return true;
3335
}
36+
/* eslint-enable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
3437
} catch {
3538
// If we cannot parse their config, just ask.
3639
return await abortIfCancelled(

0 commit comments

Comments
 (0)