mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-24 21:10:43 -07:00
chore(lint): enforce zero warnings and cleanup syntax restrictions (#22902)
This commit is contained in:
@@ -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': [
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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 || [],
|
||||
|
||||
@@ -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])
|
||||
) {
|
||||
|
||||
@@ -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')
|
||||
) {
|
||||
|
||||
@@ -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']
|
||||
) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user