chore(lint): enforce zero warnings and cleanup syntax restrictions (#22902)

This commit is contained in:
Alisa
2026-03-20 21:01:48 -07:00
committed by GitHub
parent b316fcc44d
commit 1a70fdd364
18 changed files with 21 additions and 27 deletions

View File

@@ -35,6 +35,12 @@ const commonRestrictedSyntaxRules = [
message:
'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
},
{
selector:
'UnaryExpression[operator="typeof"] > MemberExpression[computed=true][property.type="Literal"]',
message:
'Do not use typeof to check object properties. Define a TypeScript interface and a type guard function instead.',
},
];
export default tseslint.config(
@@ -133,16 +139,7 @@ export default tseslint.config(
'no-cond-assign': 'error',
'no-debugger': 'error',
'no-duplicate-case': 'error',
'no-restricted-syntax': [
'error',
...commonRestrictedSyntaxRules,
{
selector:
'UnaryExpression[operator="typeof"] > MemberExpression[computed=true][property.type="Literal"]',
message:
'Do not use typeof to check object properties. Define a TypeScript interface and a type guard function instead.',
},
],
'no-restricted-syntax': ['error', ...commonRestrictedSyntaxRules],
'no-unsafe-finally': 'error',
'no-unused-expressions': 'off', // Disable base rule
'@typescript-eslint/no-unused-expressions': [

View File

@@ -51,7 +51,7 @@
"test:integration:sandbox:none": "cross-env GEMINI_SANDBOX=false vitest run --root ./integration-tests",
"test:integration:sandbox:docker": "cross-env GEMINI_SANDBOX=docker npm run build:sandbox && cross-env GEMINI_SANDBOX=docker vitest run --root ./integration-tests",
"test:integration:sandbox:podman": "cross-env GEMINI_SANDBOX=podman vitest run --root ./integration-tests",
"lint": "eslint . --cache",
"lint": "eslint . --cache --max-warnings 0",
"lint:fix": "eslint . --fix --ext .ts,.tsx && eslint integration-tests --fix && eslint scripts --fix && npm run format",
"lint:ci": "npm run lint:all",
"lint:all": "node scripts/lint.js",

View File

@@ -1137,6 +1137,7 @@ describe('runNonInteractive', () => {
expect(
processStderrSpy.mock.calls.some(
// eslint-disable-next-line no-restricted-syntax
(call) => typeof call[0] === 'string' && call[0].includes('Cancelling'),
),
).toBe(true);

View File

@@ -79,7 +79,7 @@ export async function toMatchSvgSnapshot(
}
function toHaveOnlyValidCharacters(this: Assertion, buffer: TextBuffer) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { isNot } = this as any;
let pass = true;
const invalidLines: Array<{ line: number; content: string }> = [];
@@ -108,7 +108,6 @@ function toHaveOnlyValidCharacters(this: Assertion, buffer: TextBuffer) {
};
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
expect.extend({
toHaveOnlyValidCharacters,
toMatchSvgSnapshot,

View File

@@ -37,14 +37,12 @@ export const createMockCommandContext = (
},
services: {
agentContext: null,
settings: {
merged: defaultMergedSettings,
setValue: vi.fn(),
forScope: vi.fn().mockReturnValue({ settings: {} }),
} as unknown as LoadedSettings,
git: undefined as GitService | undefined,
logger: {
log: vi.fn(),
logMessage: vi.fn(),
@@ -53,7 +51,6 @@ export const createMockCommandContext = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any, // Cast because Logger is a class.
},
ui: {
addItem: vi.fn(),
clear: vi.fn(),
@@ -72,7 +69,6 @@ export const createMockCommandContext = (
} as any,
session: {
sessionShellAllowlist: new Set<string>(),
stats: {
sessionStartTime: new Date(),
lastPromptTokenCount: 0,
@@ -98,7 +94,6 @@ export const createMockCommandContext = (
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
const sourceValue = source[key];
const targetValue = output[key];
if (
@@ -109,7 +104,6 @@ export const createMockCommandContext = (
output[key] = merge(targetValue, sourceValue);
} else {
// If not, we do a direct assignment. This preserves Date objects and others.
output[key] = sourceValue;
}
}

View File

@@ -778,7 +778,6 @@ export async function renderHook<Result, Props>(
generateSvg: () => string;
}> {
const result = { current: undefined as unknown as Result };
let currentProps = options?.initialProps as Props;
function TestComponent({

View File

@@ -46,7 +46,6 @@ export const createMockSettings = (
workspace,
isTrusted,
errors,
merged: mergedOverride,
...settingsOverrides
} = overrides;
@@ -61,7 +60,6 @@ export const createMockSettings = (
settings: settingsOverrides,
originalSettings: settingsOverrides,
},
(workspace as any) || { path: '', settings: {}, originalSettings: {} },
isTrusted ?? true,
errors || [],

View File

@@ -42,6 +42,7 @@ describe('IdeIntegrationNudge', () => {
beforeEach(() => {
vi.mocked(debugLogger.warn).mockImplementation((...args) => {
if (
// eslint-disable-next-line no-restricted-syntax
typeof args[0] === 'string' &&
/was not wrapped in act/.test(args[0])
) {

View File

@@ -42,6 +42,7 @@ describe('AuthInProgress', () => {
vi.useFakeTimers();
vi.mocked(debugLogger.error).mockImplementation((...args) => {
if (
// eslint-disable-next-line no-restricted-syntax
typeof args[0] === 'string' &&
args[0].includes('was not wrapped in act')
) {

View File

@@ -505,7 +505,9 @@ export const useSlashCommandProcessor = (
const props = result.props as Record<string, unknown>;
if (
!props ||
// eslint-disable-next-line no-restricted-syntax
typeof props['name'] !== 'string' ||
// eslint-disable-next-line no-restricted-syntax
typeof props['displayName'] !== 'string' ||
!props['definition']
) {

View File

@@ -514,6 +514,7 @@ describe('textUtils', () => {
const b = sanitized.b as { c: string; d: Array<string | object> };
expect(b.c).toBe('\\u001b[32mgreen\\u001b[0m');
expect(b.d[0]).toBe('\\u001b[33myellow\\u001b[0m');
// eslint-disable-next-line no-restricted-syntax
if (typeof b.d[1] === 'object' && b.d[1] !== null) {
const e = b.d[1] as { e: string };
expect(e.e).toBe('\\u001b[34mblue\\u001b[0m');

View File

@@ -183,6 +183,7 @@ describe('signal and TTY handling', () => {
const sigtermHandlers = processOnHandlers.get('SIGTERM') || [];
expect(sigtermHandlers.length).toBeGreaterThan(0);
// eslint-disable-next-line no-restricted-syntax
expect(typeof sigtermHandlers[0]).toBe('function');
});
});

View File

@@ -214,6 +214,7 @@ describe('listSessions', () => {
// Get all the session log calls (skip the header)
const sessionCalls = mocks.writeToStdout.mock.calls.filter(
(call): call is [string] =>
// eslint-disable-next-line no-restricted-syntax
typeof call[0] === 'string' &&
call[0].includes('[session-') &&
!call[0].includes('Available sessions'),

View File

@@ -163,6 +163,7 @@ export class FolderTrustDiscoveryService {
for (const event of Object.values(hooksConfig)) {
if (!Array.isArray(event)) continue;
for (const hook of event) {
// eslint-disable-next-line no-restricted-syntax
if (this.isRecord(hook) && typeof hook['command'] === 'string') {
hooks.add(hook['command']);
}

View File

@@ -136,7 +136,9 @@ describe('Telemetry Sanitization', () => {
const attributes = event.toOpenTelemetryAttributes(config);
// Should be JSON stringified
// eslint-disable-next-line no-restricted-syntax
expect(typeof attributes['hook_input']).toBe('string');
// eslint-disable-next-line no-restricted-syntax
expect(typeof attributes['hook_output']).toBe('string');
const parsedInput = JSON.parse(attributes['hook_input'] as string);

View File

@@ -62,7 +62,6 @@ export class MockMessageBus {
if (!this.subscriptions.has(type)) {
this.subscriptions.set(type, new Set());
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
this.subscriptions.get(type)!.add(listener as (message: Message) => void);
},
);
@@ -74,7 +73,6 @@ export class MockMessageBus {
<T extends Message>(type: T['type'], listener: (message: T) => void) => {
const listeners = this.subscriptions.get(type);
if (listeners) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
listeners.delete(listener as (message: Message) => void);
}
},
@@ -103,7 +101,6 @@ export class MockMessageBus {
* Create a mock MessageBus for testing
*/
export function createMockMessageBus(): MessageBus {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return new MockMessageBus() as unknown as MessageBus;
}
@@ -113,6 +110,5 @@ export function createMockMessageBus(): MessageBus {
export function getMockMessageBusInstance(
messageBus: MessageBus,
): MockMessageBus {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return messageBus as unknown as MockMessageBus;
}

View File

@@ -19,7 +19,6 @@ export function createMockWorkspaceContext(
): WorkspaceContext {
const allDirs = [rootDir, ...additionalDirs];
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mockWorkspaceContext = {
addDirectory: vi.fn(),
getDirectories: vi.fn().mockReturnValue(allDirs),

View File

@@ -674,6 +674,7 @@ describe('ReadFileTool', () => {
const parts = result.llmContent as Array<Record<string, unknown>>;
const jitTextPart = parts.find(
(p) =>
// eslint-disable-next-line no-restricted-syntax
typeof p['text'] === 'string' && p['text'].includes('Auth rules'),
);
expect(jitTextPart).toBeDefined();