Git - enable ESLint rule for git extensions (#277156)

* Initial commit with all exceptions

* First pass of fixing

* Add ignored files explicitly
This commit is contained in:
Ladislau Szomoru 2025-11-13 15:35:01 +00:00 committed by GitHub
parent 8603e1dd3a
commit 2b52b93770
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 67 additions and 55 deletions

View File

@ -350,12 +350,26 @@ export default tseslint.config(
'local/code-no-in-operator': 'warn',
}
},
// vscode TS: strict no explicit `any`
// Strict no explicit `any`
{
files: [
// Extensions
'extensions/git/src/**/*.ts',
'extensions/git-base/src/**/*.ts',
'extensions/github/src/**/*.ts',
// vscode
'src/**/*.ts',
],
ignores: [
// Extensions
'extensions/git/src/commands.ts',
'extensions/git/src/decorators.ts',
'extensions/git/src/git.ts',
'extensions/git/src/repository.ts',
'extensions/git/src/util.ts',
'extensions/git-base/src/decorators.ts',
'extensions/github/src/util.ts',
// vscode d.ts
'src/vs/amdX.ts',
'src/vs/monaco.d.ts',
'src/vscode-dts/**',

View File

@ -9,13 +9,13 @@ import { ApiRepository, ApiImpl } from './api1';
import { Event, EventEmitter } from 'vscode';
import { CloneManager } from '../cloneManager';
function deprecated(original: any, context: ClassMemberDecoratorContext) {
if (context.kind !== 'method') {
function deprecated(original: unknown, context: ClassMemberDecoratorContext) {
if (typeof original !== 'function' || context.kind !== 'method') {
throw new Error('not supported');
}
const key = context.name.toString();
return function (this: any, ...args: any[]): any {
return function (this: unknown, ...args: unknown[]) {
console.warn(`Git extension API method '${key}' is deprecated.`);
return original.apply(this, args);
};

View File

@ -6,7 +6,7 @@
import * as fs from 'fs';
import { IPCClient } from './ipc/ipcClient';
function fatal(err: any): void {
function fatal(err: unknown): void {
console.error('Missing or invalid credentials.');
console.error(err);
process.exit(1);

View File

@ -132,7 +132,7 @@ class LinkedMap<K, V> implements Map<K, V> {
return item.value;
}
forEach(callbackfn: (value: V, key: K, map: LinkedMap<K, V>) => void, thisArg?: any): void {
forEach(callbackfn: (value: V, key: K, map: LinkedMap<K, V>) => void, thisArg?: unknown): void {
const state = this._state;
let current = this._head;
while (current) {

View File

@ -9,7 +9,7 @@ import { Command, commands, Disposable, MessageOptions, Position, QuickPickItem,
import TelemetryReporter from '@vscode/extension-telemetry';
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
import { ForcePushMode, GitErrorCodes, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote, Branch, Ref } from './api/git';
import { Git, Stash, Worktree } from './git';
import { Git, GitError, Stash, Worktree } from './git';
import { Model } from './model';
import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository';
import { DiffEditorSelectionHunkToolbarContext, LineChange, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges, compareLineChanges } from './staging';
@ -365,8 +365,8 @@ interface ScmCommand {
const Commands: ScmCommand[] = [];
function command(commandId: string, options: ScmCommandOptions = {}): Function {
return (value: any, context: ClassMethodDecoratorContext) => {
if (context.kind !== 'method') {
return (value: unknown, context: ClassMethodDecoratorContext) => {
if (typeof value !== 'function' || context.kind !== 'method') {
throw new Error('not supported');
}
const key = context.name.toString();
@ -3591,10 +3591,8 @@ export class CommandCenter {
}
}
@command('git.createWorktree')
async createWorktree(repository: any): Promise<void> {
repository = this.model.getRepository(repository);
@command('git.createWorktree', { repository: true })
async createWorktree(repository?: Repository): Promise<void> {
if (!repository) {
// Single repository/submodule/worktree
if (this.model.repositories.length === 1) {
@ -3786,9 +3784,9 @@ export class CommandCenter {
this.globalState.update(`${CommandCenter.WORKTREE_ROOT_KEY}:${repository.root}`, worktreeRoot);
}
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.WorktreeAlreadyExists) {
if (err instanceof GitError && err.gitErrorCode === GitErrorCodes.WorktreeAlreadyExists) {
await this.handleWorktreeAlreadyExists(err);
} else if (err.gitErrorCode === GitErrorCodes.WorktreeBranchAlreadyUsed) {
} else if (err instanceof GitError && err.gitErrorCode === GitErrorCodes.WorktreeBranchAlreadyUsed) {
await this.handleWorktreeBranchAlreadyUsed(err);
} else {
throw err;
@ -3798,8 +3796,8 @@ export class CommandCenter {
}
}
private async handleWorktreeBranchAlreadyUsed(err: any): Promise<void> {
const match = err.stderr.match(/fatal: '([^']+)' is already used by worktree at '([^']+)'/);
private async handleWorktreeBranchAlreadyUsed(err: GitError): Promise<void> {
const match = err.stderr?.match(/fatal: '([^']+)' is already used by worktree at '([^']+)'/);
if (!match) {
return;
@ -3810,8 +3808,8 @@ export class CommandCenter {
await this.handleWorktreeConflict(path, message);
}
private async handleWorktreeAlreadyExists(err: any): Promise<void> {
const match = err.stderr.match(/fatal: '([^']+)'/);
private async handleWorktreeAlreadyExists(err: GitError): Promise<void> {
const match = err.stderr?.match(/fatal: '([^']+)'/);
if (!match) {
return;

View File

@ -32,7 +32,7 @@ class GitIgnoreDecorationProvider implements FileDecorationProvider {
private disposables: Disposable[] = [];
constructor(private model: Model) {
const onDidChangeRepository = anyEvent<any>(
const onDidChangeRepository = anyEvent<unknown>(
filterEvent(workspace.onDidSaveTextDocument, e => /\.gitignore$|\.git\/info\/exclude$/.test(e.uri.path)),
model.onDidOpenRepository,
model.onDidCloseRepository

View File

@ -6,8 +6,8 @@
import { done } from './util';
function decorate(decorator: (fn: Function, key: string) => Function): Function {
return function (original: any, context: ClassMethodDecoratorContext) {
if (context.kind === 'method' || context.kind === 'getter' || context.kind === 'setter') {
return function (original: unknown, context: ClassMethodDecoratorContext) {
if (typeof original === 'function' && (context.kind === 'method' || context.kind === 'getter' || context.kind === 'setter')) {
return decorator(original, context.name.toString());
}
throw new Error('not supported');

View File

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IPCClient } from './ipc/ipcClient';
function fatal(err: any): void {
function fatal(err: unknown): void {
console.error(err);
process.exit(1);
}

View File

@ -117,7 +117,7 @@ function findGitDarwin(onValidate: (path: string) => boolean): Promise<IGit> {
}
// must check if XCode is installed
cp.exec('xcode-select -p', (err: any) => {
cp.exec('xcode-select -p', (err) => {
if (err && err.code === 2) {
// git is not installed, and launching /usr/bin/git
// will prompt the user to install it
@ -1975,11 +1975,12 @@ export class Repository {
}
}
private async handleCommitError(commitErr: any): Promise<void> {
if (/not possible because you have unmerged files/.test(commitErr.stderr || '')) {
private async handleCommitError(commitErr: unknown): Promise<void> {
if (commitErr instanceof GitError && /not possible because you have unmerged files/.test(commitErr.stderr || '')) {
commitErr.gitErrorCode = GitErrorCodes.UnmergedChanges;
throw commitErr;
} else if (/Aborting commit due to empty commit message/.test(commitErr.stderr || '')) {
} else if (commitErr instanceof GitError && /Aborting commit due to empty commit message/.test(commitErr.stderr || '')) {
commitErr.gitErrorCode = GitErrorCodes.EmptyCommitMessage;
throw commitErr;
}
@ -2113,8 +2114,8 @@ export class Repository {
const pathsByGroup = groupBy(paths.map(sanitizePath), p => path.dirname(p));
const groups = Object.keys(pathsByGroup).map(k => pathsByGroup[k]);
const limiter = new Limiter(5);
const promises: Promise<any>[] = [];
const limiter = new Limiter<IExecutionResult<string>>(5);
const promises: Promise<IExecutionResult<string>>[] = [];
const args = ['clean', '-f', '-q'];
for (const paths of groups) {

View File

@ -34,7 +34,7 @@ export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider {
};
}
async handle({ commitMessagePath }: GitEditorRequest): Promise<any> {
async handle({ commitMessagePath }: GitEditorRequest): Promise<boolean> {
if (commitMessagePath) {
const uri = Uri.file(commitMessagePath);
const doc = await workspace.openTextDocument(uri);
@ -49,6 +49,8 @@ export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider {
});
});
}
return Promise.resolve(false);
}
getEnv(): { [key: string]: string } {

View File

@ -19,7 +19,7 @@ export class IPCClient {
this.ipcHandlePath = ipcHandlePath;
}
call(request: any): Promise<any> {
call(request: unknown): Promise<unknown> {
const opts: http.RequestOptions = {
socketPath: this.ipcHandlePath,
path: `/${this.handlerName}`,

View File

@ -25,7 +25,7 @@ function getIPCHandlePath(id: string): string {
}
export interface IIPCHandler {
handle(request: any): Promise<any>;
handle(request: unknown): Promise<unknown>;
}
export async function createIPCServer(context?: string): Promise<IPCServer> {

View File

@ -29,9 +29,9 @@ import { GitCommitInputBoxCodeActionsProvider, GitCommitInputBoxDiagnosticsManag
import { GitBlameController } from './blame';
import { CloneManager } from './cloneManager';
const deactivateTasks: { (): Promise<any> }[] = [];
const deactivateTasks: { (): Promise<void> }[] = [];
export async function deactivate(): Promise<any> {
export async function deactivate(): Promise<void> {
for (const task of deactivateTasks) {
await task();
}

View File

@ -227,7 +227,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi
return Promise.resolve();
}
return eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise<any>;
return eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized') as Event<unknown>) as Promise<void>;
}
private remoteSourcePublishers = new Set<RemoteSourcePublisher>();
@ -835,7 +835,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi
commands.executeCommand('setContext', 'operationInProgress', operationInProgress);
};
const operationEvent = anyEvent(repository.onDidRunOperation as Event<any>, repository.onRunOperation as Event<any>);
const operationEvent = anyEvent(repository.onDidRunOperation as Event<unknown>, repository.onRunOperation as Event<unknown>);
const operationListener = operationEvent(() => updateOperationInProgressContext());
updateOperationInProgressContext();
@ -901,11 +901,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi
return pick && pick.repository;
}
getRepository(sourceControl: SourceControl): Repository | undefined;
getRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined;
getRepository(path: string): Repository | undefined;
getRepository(resource: Uri): Repository | undefined;
getRepository(hint: any): Repository | undefined {
getRepository(hint: SourceControl | SourceControlResourceGroup | Uri | string): Repository | undefined {
const liveRepository = this.getOpenRepository(hint);
return liveRepository && liveRepository.repository;
}
@ -932,12 +928,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi
}
}
private getOpenRepository(repository: Repository): OpenRepository | undefined;
private getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined;
private getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined;
private getOpenRepository(path: string): OpenRepository | undefined;
private getOpenRepository(resource: Uri): OpenRepository | undefined;
private getOpenRepository(hint: any): OpenRepository | undefined {
private getOpenRepository(hint: SourceControl | SourceControlResourceGroup | Repository | Uri | string): OpenRepository | undefined {
if (!hint) {
return undefined;
}

View File

@ -200,7 +200,7 @@ export const Operation = {
export interface OperationResult {
operation: Operation;
error: any;
error: unknown;
}
interface IOperationManager {

View File

@ -692,11 +692,7 @@ interface BranchProtectionMatcher {
}
export interface IRepositoryResolver {
getRepository(sourceControl: SourceControl): Repository | undefined;
getRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined;
getRepository(path: string): Repository | undefined;
getRepository(resource: Uri): Repository | undefined;
getRepository(hint: any): Repository | undefined;
getRepository(hint: SourceControl | SourceControlResourceGroup | Uri | string): Repository | undefined;
}
export class Repository implements Disposable {
@ -940,7 +936,9 @@ export class Repository implements Disposable {
: repository.kind === 'worktree' && repository.dotGit.commonPath
? path.dirname(repository.dotGit.commonPath)
: undefined;
const parent = this.repositoryResolver.getRepository(parentRoot)?.sourceControl;
const parent = parentRoot
? this.repositoryResolver.getRepository(parentRoot)?.sourceControl
: undefined;
// Icon
const icon = repository.kind === 'submodule'

View File

@ -10,7 +10,15 @@ import { Octokit } from '@octokit/rest';
import { getRepositoryFromQuery, getRepositoryFromUrl } from './util.js';
import { getBranchLink, getVscodeDevHost } from './links.js';
function asRemoteSource(raw: any): RemoteSource {
type RemoteSourceResponse = {
readonly full_name: string;
readonly description: string | null;
readonly stargazers_count: number;
readonly clone_url: string;
readonly ssh_url: string;
};
function asRemoteSource(raw: RemoteSourceResponse): RemoteSource {
const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol');
return {
name: `$(github) ${raw.full_name}`,