import { Linter, type ESLint } from 'eslint' import formatter from 'eslint-formatter-stylish' import { getEslintConfig, getLocalModuleEslintConfig, getPluginRules } from './Rules' // Module-level singletons — the Linter and configs are shared across all Lint instances. // Uses configType: 'eslintrc' (legacy mode) because ESLint v9's flat config verify() path // re-validates the config (including 7000+ no-restricted-properties entries) on every call, // causing significant performance overhead. In legacy mode, defineRules() loads rules once // and verify() is lightweight — matching ESLint v8 behavior. // // TODO: configType: 'eslintrc' is deprecated and will be removed in ESLint v10. // When that happens, replace the 7000+ no-restricted-properties entries with a single // custom ESLint rule that internalizes the Glide API blocklist. This would make flat // config verify() fast by reducing the rule count, eliminating the need for legacy mode. let sharedLinter: Linter | undefined let dependencyConfig: Linter.LegacyConfig | undefined let localModuleConfig: Linter.LegacyConfig | undefined function getSharedLinter(): Linter { if (!sharedLinter) { sharedLinter = new Linter({ configType: 'eslintrc' }) sharedLinter.defineRules(getPluginRules()) dependencyConfig = getEslintConfig() localModuleConfig = getLocalModuleEslintConfig() } return sharedLinter } function outputLintingResults(messages: Linter.LintMessage[]): string | undefined { const stats = messages.reduce( (result, message) => { if (message.fatal) { result.fatalErrorCount++ } if (message.severity === 1) { result.warningCount++ if (message.fix) { result.fixableWarningCount++ } } if (message.severity === 2) { result.errorCount++ if (message.fix) { result.fixableErrorCount++ } } return result }, { errorCount: 0, fatalErrorCount: 0, warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0, } ) const results: ESLint.LintResult = { filePath: '', messages, suppressedMessages: [], usedDeprecatedRules: [], ...stats, } const problems = results.errorCount + results.warningCount + results.fatalErrorCount if (problems <= 0) { return } return (formatter as (results: ESLint.LintResult[]) => string)([results]) } /** * Lint for 3rd party dependencies — Rhino compatibility + Glide API restrictions. */ export class Lint { check(fileContent: string): string | undefined { const linter = getSharedLinter() const messages = linter.verify(fileContent, dependencyConfig!) return outputLintingResults(messages) } } /** * Lint for local modules — Rhino compatibility rules only (no Glide restrictions). */ export class LocalModuleLint { check(fileContent: string): string | undefined { const linter = getSharedLinter() const messages = linter.verify(fileContent, localModuleConfig!) return outputLintingResults(messages) } }