import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; import { ESLint } from 'eslint'; import js from '@eslint/js'; import ts from 'typescript-eslint'; import type { LinterAdapter, AdapterResult, LinterDiagnostic } from './adapter'; import { getEslintConfigPath } from '../config'; const PROJECT_CONFIG_FILES = [ '.eslintrc.js', '.eslintrc.json', '.eslintrc.yaml', '.eslintrc.yml', '.eslintrc', 'eslint.config.js', 'eslint.config.mjs', ]; function findProjectConfig(dir: string): string | null { for (const name of PROJECT_CONFIG_FILES) { const p = path.join(dir, name); if (fs.existsSync(p)) { return p; } } return null; } function resolveEslintConfig(workingDir: string): { configFile?: string; overrideConfig?: any[] } { const globalPath = getEslintConfigPath(); if (globalPath && globalPath.trim() !== '') { return { configFile: globalPath }; } const projectConfig = findProjectConfig(workingDir); if (projectConfig) { return { configFile: projectConfig }; } return { overrideConfig: ESLintAdapter.getDefaultConfig() }; } export class ESLintAdapter implements LinterAdapter { id = 'eslint'; supportedLanguages = ['javascript', 'typescript']; private static defaultConfig: any[] | null = null; public static getDefaultConfig(): any[] { if (!ESLintAdapter.defaultConfig) { ESLintAdapter.defaultConfig = [ js.configs.recommended, ...ts.configs.recommended, ]; } return ESLintAdapter.defaultConfig; } isAvailable(): boolean { return true; } async check(document: vscode.TextDocument, workingDir: string): Promise { try { const resolved = resolveEslintConfig(workingDir); const engine = new ESLint({ cwd: workingDir, ...resolved, }); const ext = document.languageId === 'typescript' ? 'ts' : 'js'; const isVirtual = document.uri.scheme === 'untitled'; const results = await engine.lintText(document.getText(), { filePath: isVirtual ? `untitled.${ext}` : document.fileName, }); const diagnostics: LinterDiagnostic[] = []; for (const result of results) { for (const msg of result.messages) { if (msg.ruleId === null) { continue; } diagnostics.push({ severity: msg.severity === 2 ? 'error' : 'warning', ruleId: `eslint:${msg.ruleId}`, message: msg.message, range: new vscode.Range( msg.line - 1, msg.column - 1, (msg.endLine ?? msg.line) - 1, (msg.endColumn ?? msg.column) - 1 ), suggestion: msg.fix?.text, }); } } return { diagnostics, status: 'ok' }; } catch (error) { return { diagnostics: [], status: 'execution-failed', errorMessage: error instanceof Error ? error.message : String(error), }; } } }