mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
chore(lint): enforce zero warnings and cleanup syntax restrictions (#22902)
This commit is contained in:
+7
-10
@@ -35,6 +35,12 @@ const commonRestrictedSyntaxRules = [
|
|||||||
message:
|
message:
|
||||||
'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
|
'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(
|
export default tseslint.config(
|
||||||
@@ -133,16 +139,7 @@ export default tseslint.config(
|
|||||||
'no-cond-assign': 'error',
|
'no-cond-assign': 'error',
|
||||||
'no-debugger': 'error',
|
'no-debugger': 'error',
|
||||||
'no-duplicate-case': 'error',
|
'no-duplicate-case': 'error',
|
||||||
'no-restricted-syntax': [
|
'no-restricted-syntax': ['error', ...commonRestrictedSyntaxRules],
|
||||||
'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-unsafe-finally': 'error',
|
'no-unsafe-finally': 'error',
|
||||||
'no-unused-expressions': 'off', // Disable base rule
|
'no-unused-expressions': 'off', // Disable base rule
|
||||||
'@typescript-eslint/no-unused-expressions': [
|
'@typescript-eslint/no-unused-expressions': [
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@
|
|||||||
"test:integration:sandbox:none": "cross-env GEMINI_SANDBOX=false vitest run --root ./integration-tests",
|
"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: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",
|
"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:fix": "eslint . --fix --ext .ts,.tsx && eslint integration-tests --fix && eslint scripts --fix && npm run format",
|
||||||
"lint:ci": "npm run lint:all",
|
"lint:ci": "npm run lint:all",
|
||||||
"lint:all": "node scripts/lint.js",
|
"lint:all": "node scripts/lint.js",
|
||||||
|
|||||||
@@ -1137,6 +1137,7 @@ describe('runNonInteractive', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
processStderrSpy.mock.calls.some(
|
processStderrSpy.mock.calls.some(
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
(call) => typeof call[0] === 'string' && call[0].includes('Cancelling'),
|
(call) => typeof call[0] === 'string' && call[0].includes('Cancelling'),
|
||||||
),
|
),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export async function toMatchSvgSnapshot(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toHaveOnlyValidCharacters(this: Assertion, buffer: TextBuffer) {
|
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;
|
const { isNot } = this as any;
|
||||||
let pass = true;
|
let pass = true;
|
||||||
const invalidLines: Array<{ line: number; content: string }> = [];
|
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({
|
expect.extend({
|
||||||
toHaveOnlyValidCharacters,
|
toHaveOnlyValidCharacters,
|
||||||
toMatchSvgSnapshot,
|
toMatchSvgSnapshot,
|
||||||
|
|||||||
@@ -37,14 +37,12 @@ export const createMockCommandContext = (
|
|||||||
},
|
},
|
||||||
services: {
|
services: {
|
||||||
agentContext: null,
|
agentContext: null,
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
merged: defaultMergedSettings,
|
merged: defaultMergedSettings,
|
||||||
setValue: vi.fn(),
|
setValue: vi.fn(),
|
||||||
forScope: vi.fn().mockReturnValue({ settings: {} }),
|
forScope: vi.fn().mockReturnValue({ settings: {} }),
|
||||||
} as unknown as LoadedSettings,
|
} as unknown as LoadedSettings,
|
||||||
git: undefined as GitService | undefined,
|
git: undefined as GitService | undefined,
|
||||||
|
|
||||||
logger: {
|
logger: {
|
||||||
log: vi.fn(),
|
log: vi.fn(),
|
||||||
logMessage: vi.fn(),
|
logMessage: vi.fn(),
|
||||||
@@ -53,7 +51,6 @@ export const createMockCommandContext = (
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} as any, // Cast because Logger is a class.
|
} as any, // Cast because Logger is a class.
|
||||||
},
|
},
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
addItem: vi.fn(),
|
addItem: vi.fn(),
|
||||||
clear: vi.fn(),
|
clear: vi.fn(),
|
||||||
@@ -72,7 +69,6 @@ export const createMockCommandContext = (
|
|||||||
} as any,
|
} as any,
|
||||||
session: {
|
session: {
|
||||||
sessionShellAllowlist: new Set<string>(),
|
sessionShellAllowlist: new Set<string>(),
|
||||||
|
|
||||||
stats: {
|
stats: {
|
||||||
sessionStartTime: new Date(),
|
sessionStartTime: new Date(),
|
||||||
lastPromptTokenCount: 0,
|
lastPromptTokenCount: 0,
|
||||||
@@ -98,7 +94,6 @@ export const createMockCommandContext = (
|
|||||||
for (const key in source) {
|
for (const key in source) {
|
||||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||||
const sourceValue = source[key];
|
const sourceValue = source[key];
|
||||||
|
|
||||||
const targetValue = output[key];
|
const targetValue = output[key];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -109,7 +104,6 @@ export const createMockCommandContext = (
|
|||||||
output[key] = merge(targetValue, sourceValue);
|
output[key] = merge(targetValue, sourceValue);
|
||||||
} else {
|
} else {
|
||||||
// If not, we do a direct assignment. This preserves Date objects and others.
|
// If not, we do a direct assignment. This preserves Date objects and others.
|
||||||
|
|
||||||
output[key] = sourceValue;
|
output[key] = sourceValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -778,7 +778,6 @@ export async function renderHook<Result, Props>(
|
|||||||
generateSvg: () => string;
|
generateSvg: () => string;
|
||||||
}> {
|
}> {
|
||||||
const result = { current: undefined as unknown as Result };
|
const result = { current: undefined as unknown as Result };
|
||||||
|
|
||||||
let currentProps = options?.initialProps as Props;
|
let currentProps = options?.initialProps as Props;
|
||||||
|
|
||||||
function TestComponent({
|
function TestComponent({
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export const createMockSettings = (
|
|||||||
workspace,
|
workspace,
|
||||||
isTrusted,
|
isTrusted,
|
||||||
errors,
|
errors,
|
||||||
|
|
||||||
merged: mergedOverride,
|
merged: mergedOverride,
|
||||||
...settingsOverrides
|
...settingsOverrides
|
||||||
} = overrides;
|
} = overrides;
|
||||||
@@ -61,7 +60,6 @@ export const createMockSettings = (
|
|||||||
settings: settingsOverrides,
|
settings: settingsOverrides,
|
||||||
originalSettings: settingsOverrides,
|
originalSettings: settingsOverrides,
|
||||||
},
|
},
|
||||||
|
|
||||||
(workspace as any) || { path: '', settings: {}, originalSettings: {} },
|
(workspace as any) || { path: '', settings: {}, originalSettings: {} },
|
||||||
isTrusted ?? true,
|
isTrusted ?? true,
|
||||||
errors || [],
|
errors || [],
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ describe('IdeIntegrationNudge', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.mocked(debugLogger.warn).mockImplementation((...args) => {
|
vi.mocked(debugLogger.warn).mockImplementation((...args) => {
|
||||||
if (
|
if (
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
typeof args[0] === 'string' &&
|
typeof args[0] === 'string' &&
|
||||||
/was not wrapped in act/.test(args[0])
|
/was not wrapped in act/.test(args[0])
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ describe('AuthInProgress', () => {
|
|||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
vi.mocked(debugLogger.error).mockImplementation((...args) => {
|
vi.mocked(debugLogger.error).mockImplementation((...args) => {
|
||||||
if (
|
if (
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
typeof args[0] === 'string' &&
|
typeof args[0] === 'string' &&
|
||||||
args[0].includes('was not wrapped in act')
|
args[0].includes('was not wrapped in act')
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -505,7 +505,9 @@ export const useSlashCommandProcessor = (
|
|||||||
const props = result.props as Record<string, unknown>;
|
const props = result.props as Record<string, unknown>;
|
||||||
if (
|
if (
|
||||||
!props ||
|
!props ||
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
typeof props['name'] !== 'string' ||
|
typeof props['name'] !== 'string' ||
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
typeof props['displayName'] !== 'string' ||
|
typeof props['displayName'] !== 'string' ||
|
||||||
!props['definition']
|
!props['definition']
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -514,6 +514,7 @@ describe('textUtils', () => {
|
|||||||
const b = sanitized.b as { c: string; d: Array<string | object> };
|
const b = sanitized.b as { c: string; d: Array<string | object> };
|
||||||
expect(b.c).toBe('\\u001b[32mgreen\\u001b[0m');
|
expect(b.c).toBe('\\u001b[32mgreen\\u001b[0m');
|
||||||
expect(b.d[0]).toBe('\\u001b[33myellow\\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) {
|
if (typeof b.d[1] === 'object' && b.d[1] !== null) {
|
||||||
const e = b.d[1] as { e: string };
|
const e = b.d[1] as { e: string };
|
||||||
expect(e.e).toBe('\\u001b[34mblue\\u001b[0m');
|
expect(e.e).toBe('\\u001b[34mblue\\u001b[0m');
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ describe('signal and TTY handling', () => {
|
|||||||
|
|
||||||
const sigtermHandlers = processOnHandlers.get('SIGTERM') || [];
|
const sigtermHandlers = processOnHandlers.get('SIGTERM') || [];
|
||||||
expect(sigtermHandlers.length).toBeGreaterThan(0);
|
expect(sigtermHandlers.length).toBeGreaterThan(0);
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
expect(typeof sigtermHandlers[0]).toBe('function');
|
expect(typeof sigtermHandlers[0]).toBe('function');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ describe('listSessions', () => {
|
|||||||
// Get all the session log calls (skip the header)
|
// Get all the session log calls (skip the header)
|
||||||
const sessionCalls = mocks.writeToStdout.mock.calls.filter(
|
const sessionCalls = mocks.writeToStdout.mock.calls.filter(
|
||||||
(call): call is [string] =>
|
(call): call is [string] =>
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
typeof call[0] === 'string' &&
|
typeof call[0] === 'string' &&
|
||||||
call[0].includes('[session-') &&
|
call[0].includes('[session-') &&
|
||||||
!call[0].includes('Available sessions'),
|
!call[0].includes('Available sessions'),
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ export class FolderTrustDiscoveryService {
|
|||||||
for (const event of Object.values(hooksConfig)) {
|
for (const event of Object.values(hooksConfig)) {
|
||||||
if (!Array.isArray(event)) continue;
|
if (!Array.isArray(event)) continue;
|
||||||
for (const hook of event) {
|
for (const hook of event) {
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
if (this.isRecord(hook) && typeof hook['command'] === 'string') {
|
if (this.isRecord(hook) && typeof hook['command'] === 'string') {
|
||||||
hooks.add(hook['command']);
|
hooks.add(hook['command']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,7 +136,9 @@ describe('Telemetry Sanitization', () => {
|
|||||||
const attributes = event.toOpenTelemetryAttributes(config);
|
const attributes = event.toOpenTelemetryAttributes(config);
|
||||||
|
|
||||||
// Should be JSON stringified
|
// Should be JSON stringified
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
expect(typeof attributes['hook_input']).toBe('string');
|
expect(typeof attributes['hook_input']).toBe('string');
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
expect(typeof attributes['hook_output']).toBe('string');
|
expect(typeof attributes['hook_output']).toBe('string');
|
||||||
|
|
||||||
const parsedInput = JSON.parse(attributes['hook_input'] as string);
|
const parsedInput = JSON.parse(attributes['hook_input'] as string);
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ export class MockMessageBus {
|
|||||||
if (!this.subscriptions.has(type)) {
|
if (!this.subscriptions.has(type)) {
|
||||||
this.subscriptions.set(type, new Set());
|
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);
|
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) => {
|
<T extends Message>(type: T['type'], listener: (message: T) => void) => {
|
||||||
const listeners = this.subscriptions.get(type);
|
const listeners = this.subscriptions.get(type);
|
||||||
if (listeners) {
|
if (listeners) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
||||||
listeners.delete(listener as (message: Message) => void);
|
listeners.delete(listener as (message: Message) => void);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -103,7 +101,6 @@ export class MockMessageBus {
|
|||||||
* Create a mock MessageBus for testing
|
* Create a mock MessageBus for testing
|
||||||
*/
|
*/
|
||||||
export function createMockMessageBus(): MessageBus {
|
export function createMockMessageBus(): MessageBus {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
||||||
return new MockMessageBus() as unknown as MessageBus;
|
return new MockMessageBus() as unknown as MessageBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +110,5 @@ export function createMockMessageBus(): MessageBus {
|
|||||||
export function getMockMessageBusInstance(
|
export function getMockMessageBusInstance(
|
||||||
messageBus: MessageBus,
|
messageBus: MessageBus,
|
||||||
): MockMessageBus {
|
): MockMessageBus {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
||||||
return messageBus as unknown as MockMessageBus;
|
return messageBus as unknown as MockMessageBus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export function createMockWorkspaceContext(
|
|||||||
): WorkspaceContext {
|
): WorkspaceContext {
|
||||||
const allDirs = [rootDir, ...additionalDirs];
|
const allDirs = [rootDir, ...additionalDirs];
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
||||||
const mockWorkspaceContext = {
|
const mockWorkspaceContext = {
|
||||||
addDirectory: vi.fn(),
|
addDirectory: vi.fn(),
|
||||||
getDirectories: vi.fn().mockReturnValue(allDirs),
|
getDirectories: vi.fn().mockReturnValue(allDirs),
|
||||||
|
|||||||
@@ -674,6 +674,7 @@ describe('ReadFileTool', () => {
|
|||||||
const parts = result.llmContent as Array<Record<string, unknown>>;
|
const parts = result.llmContent as Array<Record<string, unknown>>;
|
||||||
const jitTextPart = parts.find(
|
const jitTextPart = parts.find(
|
||||||
(p) =>
|
(p) =>
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
typeof p['text'] === 'string' && p['text'].includes('Auth rules'),
|
typeof p['text'] === 'string' && p['text'].includes('Auth rules'),
|
||||||
);
|
);
|
||||||
expect(jitTextPart).toBeDefined();
|
expect(jitTextPart).toBeDefined();
|
||||||
|
|||||||
Reference in New Issue
Block a user