'use strict' ; import * as path from 'path' ; import * as fs from 'fs' ; import { workspace as Workspace , window as Window , commands as Commands , languages as Languages , Disposable , ExtensionContext , Uri , StatusBarAlignment , TextDocument , CodeActionContext , Diagnostic , ProviderResult , Command , QuickPickItem , WorkspaceFolder as VWorkspaceFolder , CodeAction , MessageItem , ConfigurationTarget , env as Env , CodeActionKind , WorkspaceConfiguration , ThemeColor } from 'vscode' ; import { LanguageClient , LanguageClientOptions , RequestType , TransportKind , TextDocumentIdentifier , NotificationType , ErrorHandler , ErrorHandlerResult , CloseAction , CloseHandlerResult , State as ClientState , RevealOutputChannelOn , VersionedTextDocumentIdentifier , ExecuteCommandRequest , ExecuteCommandParams , ServerOptions , DocumentFilter , DidCloseTextDocumentNotification , DidOpenTextDocumentNotification , WorkspaceFolder , NotificationType0 } from 'vscode-languageclient/node' ; import { findEslint , convert2RegExp , toOSPath , toPosixPath , Semaphore } from './utils' ; import { TaskProvider } from './tasks' ; namespace Is { const toString = Object . prototype . toString ; export function boolean ( value ) { return value === true || value === false ; } export function string ( value ) { return toString . call ( value ) === '[object String]' ; } export function objectLiteral ( value ) { return value !== null && value !== undefined && ! Array . isArray ( value ) && typeof value === 'object' ; } } interface ValidateItem { language : string ; autoFix ? : boolean ; } namespace ValidateItem { export function is ( item ) { const candidate = item as ValidateItem ; return candidate && Is . string ( candidate . language ) && ( Is . boolean ( candidate . autoFix ) || candidate . autoFix === void 0 ) ; } } interface LegacyDirectoryItem { directory : string ; changeProcessCWD : boolean ; } namespace LegacyDirectoryItem { export function is ( item ) { const candidate = item as LegacyDirectoryItem ; return candidate && Is . string ( candidate . directory ) && Is . boolean ( candidate . changeProcessCWD ) ; } } enum ModeEnum { auto = 'auto' , location = 'location' } namespace ModeEnum { export function is ( value ) { return value === ModeEnum . auto || value === ModeEnum . location ; } } interface ModeItem { mode : ModeEnum ; } namespace ModeItem { export function is ( item ) { const candidate = item as ModeItem ; return candidate && ModeEnum . is ( candidate . mode ) ; } } interface DirectoryItem { directory : string ; '!cwd' ? : boolean ; } namespace DirectoryItem { export function is ( item ) { const candidate = item as DirectoryItem ; return candidate && Is . string ( candidate . directory ) && ( Is . boolean ( candidate [ '!cwd' ] ) || candidate [ '!cwd' ] === undefined ) ; } } interface PatternItem { pattern : string ; '!cwd' ? : boolean ; } namespace PatternItem { export function is ( item ) { const candidate = item as PatternItem ; return candidate && Is . string ( candidate . pattern ) && ( Is . boolean ( candidate [ '!cwd' ] ) || candidate [ '!cwd' ] === undefined ) ; } } type RunValues = 'onType' | 'onSave' ; interface CodeActionSettings { disableRuleComment : { enable : boolean ; location : 'separateLine' | 'sameLine' ; } ; showDocumentation : { enable : boolean ; } ; } enum CodeActionsOnSaveMode { all = 'all' , problems = 'problems' } namespace CodeActionsOnSaveMode { export function from ( value ) { if ( value === undefined || value === null || ! Is . string ( value ) ) { return CodeActionsOnSaveMode . all ; } switch ( value . toLowerCase ( ) ) { case CodeActionsOnSaveMode . problems : return CodeActionsOnSaveMode . problems ; default : return CodeActionsOnSaveMode . all ; } } } namespace CodeActionsOnSaveRules { export function from ( value ) { if ( value === undefined || value === null || ! Array . isArray ( value ) ) { return undefined ; } return value . filter ( item => Is . string ( item ) ) ; } } interface CodeActionsOnSaveSettings { enable : boolean ; mode : CodeActionsOnSaveMode ; rules ? : string [ ] ; } enum Validate { on = 'on' , off = 'off' , probe = 'probe' } enum ESLintSeverity { off = 'off' , warn = 'warn' , error = 'error' } namespace ESLintSeverity { export function from ( value ) { if ( value === undefined || value === null ) { return ESLintSeverity . off ; } switch ( value . toLowerCase ( ) ) { case ESLintSeverity . off : return ESLintSeverity . off ; case ESLintSeverity . warn : return ESLintSeverity . warn ; case ESLintSeverity . error : return ESLintSeverity . error ; default : return ESLintSeverity . off ; } } } enum RuleSeverity { info = 'info' , warn = 'warn' , error = 'error' , off = 'off' , default = 'default' , downgrade = 'downgrade' , upgrade = 'upgrade' } interface RuleCustomization { rule : string ; severity : RuleSeverity ; } interface ConfigurationSettings { validate : Validate ; packageManager : 'npm' | 'yarn' | 'pnpm' ; useESLintClass : boolean ; codeAction : CodeActionSettings ; codeActionOnSave : CodeActionsOnSaveSettings ; format : boolean ; quiet : boolean ; onIgnoredFiles : ESLintSeverity ; options : any | undefined ; rulesCustomizations : RuleCustomization [ ] ; run : RunValues ; nodePath : string | null ; workspaceFolder : WorkspaceFolder | undefined ; workingDirectory : ModeItem | DirectoryItem | undefined ; } interface NoESLintState { global ? : boolean ; workspaces ? : { [ key ] : boolean ; } ; } enum Status { ok = 1 , warn = 2 , error = 3 } interface StatusParams { uri : string ; state : Status ; } namespace StatusNotification { export const type = new NotificationType < StatusParams > ( 'eslint/status' ) ; } interface NoConfigParams { message : string ; document : TextDocumentIdentifier ; } interface NoConfigResult { } namespace NoConfigRequest { export const type = new RequestType < NoConfigParams , NoConfigResult , void > ( 'eslint/noConfig' ) ; } interface NoESLintLibraryParams { source : TextDocumentIdentifier ; } interface NoESLintLibraryResult { } namespace NoESLintLibraryRequest { export const type = new RequestType < NoESLintLibraryParams , NoESLintLibraryResult , void > ( 'eslint/noLibrary' ) ; } interface OpenESLintDocParams { url : string ; } interface OpenESLintDocResult { } namespace OpenESLintDocRequest { export const type = new RequestType < OpenESLintDocParams , OpenESLintDocResult , void > ( 'eslint/openDoc' ) ; } interface ProbeFailedParams { textDocument : TextDocumentIdentifier ; } namespace ProbeFailedRequest { export const type = new RequestType < ProbeFailedParams , void , void > ( 'eslint/probeFailed' ) ; } namespace ShowOutputChannel { export const type = new NotificationType0 ( 'eslint/showOutputChannel' ) ; } const exitCalled = new NotificationType < [ number , string ] > ( 'eslint/exitCalled' ) ; interface WorkspaceFolderItem extends QuickPickItem { folder : VWorkspaceFolder ; } async function pickFolder ( folders , placeHolder ) { if ( folders . length === 1 ) { return Promise . resolve ( folders [ 0 ] ) ; } const selected = await Window . showQuickPick ( folders . map < WorkspaceFolderItem > ( ( folder ) => { return { label : folder . name , description : folder . uri . fsPath , folder : folder } ; } ) , { placeHolder : placeHolder } ) ; if ( selected === undefined ) { return undefined ; } return selected . folder ; } function createDefaultConfiguration ( ) { const folders = Workspace . workspaceFolders ; if ( ! folders ) { void Window . showErrorMessage ( 'An ESLint configuration can only be generated if VS Code is opened on a workspace folder.' ) ; return ; } const noConfigFolders = folders . filter ( folder => { const configFiles = [ '.eslintrc.js' , '.eslintrc.yaml' , '.eslintrc.yml' , '.eslintrc' , '.eslintrc.json' ] ; for ( const configFile of configFiles ) { if ( fs . existsSync ( path . join ( folder . uri . fsPath , configFile ) ) ) { return false ; } } return true ; } ) ; if ( noConfigFolders . length === 0 ) { if ( folders . length === 1 ) { void Window . showInformationMessage ( 'The workspace already contains an ESLint configuration file.' ) ; } else { void Window . showInformationMessage ( 'All workspace folders already contain an ESLint configuration file.' ) ; } return ; } void pickFolder ( noConfigFolders , 'Select a workspace folder to generate a ESLint configuration for' ) . then ( async ( folder ) => { if ( ! folder ) { return ; } const folderRootPath = folder . uri . fsPath ; const terminal = Window . createTerminal ( { name : `ESLint init` , cwd : folderRootPath } ) ; const eslintCommand = await findEslint ( folderRootPath ) ; terminal . sendText ( `${ eslintCommand } --init` ) ; terminal . show ( ) ; } ) ; } let onActivateCommands ; const probeFailed = new Set ( ) ; function computeValidate ( textDocument ) { const config = Workspace . getConfiguration ( 'eslint' , textDocument . uri ) ; if ( ! config . get ( 'enable' , true ) ) { return Validate . off ; } const languageId = textDocument . languageId ; const validate = config . get < ( ValidateItem | string ) [ ] > ( 'validate' ) ; if ( Array . isArray ( validate ) ) { for ( const item of validate ) { if ( Is . string ( item ) && item === languageId ) { return Validate . on ; } else if ( ValidateItem . is ( item ) && item . language === languageId ) { return Validate . on ; } } } const uri = textDocument . uri . toString ( ) ; if ( probeFailed . has ( uri ) ) { return Validate . off ; } const probe = config . get < string [ ] > ( 'probe' ) ; if ( Array . isArray ( probe ) ) { for ( const item of probe ) { if ( item === languageId ) { return Validate . probe ; } } } return Validate . off ; } function parseRulesCustomizations ( rawConfig ) { if ( ! rawConfig || ! Array . isArray ( rawConfig ) ) { return [ ] ; } return rawConfig . map ( rawValue => { if ( typeof rawValue . severity === 'string' && typeof rawValue . rule === 'string' ) { return { severity : rawValue . severity , rule : rawValue . rule , } ; } return undefined ; } ) . filter ( ( value ) : value is RuleCustomization => ! ! value ) ; } let taskProvider ; function isInDebugMode ( ) { const debugStartWith = [ '--debug=' , '--debug-brk=' , '--inspect=' , '--inspect-brk=' ] ; const debugEquals = [ '--debug' , '--debug-brk' , '--inspect' , '--inspect-brk' ] ; let args = ( process as any ) . execArgv ; if ( args ) { return args . some ( ( arg ) => { return debugStartWith . some ( value => arg . startsWith ( value ) ) || debugEquals . some ( value => arg === value ) ; } ) ; } return false ; } export function activate ( context ) { function didOpenTextDocument ( textDocument ) { if ( activated ) { return ; } if ( computeValidate ( textDocument ) !== Validate . off ) { openListener . dispose ( ) ; configurationListener . dispose ( ) ; activated = true ; realActivate ( context ) ; } } function configurationChanged ( ) { if ( activated ) { return ; } for ( const textDocument of Workspace . textDocuments ) { if ( computeValidate ( textDocument ) !== Validate . off ) { openListener . dispose ( ) ; configurationListener . dispose ( ) ; activated = true ; realActivate ( context ) ; return ; } } } let activated = false ; const openListener = Workspace . onDidOpenTextDocument ( didOpenTextDocument ) ; const configurationListener = Workspace . onDidChangeConfiguration ( configurationChanged ) ; const notValidating = ( ) => { const enabled = Workspace . getConfiguration ( 'eslint' , Window . activeTextEditor ?. document ) . get ( 'enable' , true ) ; if ( ! enabled ) { void Window . showInformationMessage ( `ESLint is not running because the deprecated setting 'eslint.enable' is set to false. Remove the setting and use the extension disablement feature.` ) ; } else { void Window . showInformationMessage ( 'ESLint is not running. By default only TypeScript and JavaScript files are validated. If you want to validate other file types please specify them in the \'eslint.probe\' setting.' ) ; } } ; onActivateCommands = [ Commands . registerCommand ( 'eslint.executeAutofix' , notValidating ) , Commands . registerCommand ( 'eslint.showOutputChannel' , notValidating ) , Commands . registerCommand ( 'eslint.migrateSettings' , notValidating ) , Commands . registerCommand ( 'eslint.restart' , notValidating ) ] ; context . subscriptions . push ( Commands . registerCommand ( 'eslint.createConfig' , createDefaultConfiguration ) ) ; taskProvider = new TaskProvider ( ) ; taskProvider . start ( ) ; configurationChanged ( ) ; } interface InspectData < T > { globalValue ? : T ; workspaceValue ? : T ; workspaceFolderValue ? : T ; } interface MigrationElement < T > { changed : boolean ; value : T | undefined ; } interface MigrationData < T > { global : MigrationElement < T > ; workspace : MigrationElement < T > ; workspaceFolder : MigrationElement < T > ; } interface CodeActionsOnSaveMap { 'source.fixAll' ? : boolean ; 'source.fixAll.eslint' ? : boolean ; [ key ] : boolean | undefined ; } type CodeActionsOnSave = CodeActionsOnSaveMap | string [ ] | null ; namespace CodeActionsOnSave { export function isExplicitlyDisabled ( setting ) { if ( setting === undefined || setting === null || Array . isArray ( setting ) ) { return false ; } return setting [ 'source.fixAll.eslint' ] === false ; } export function getSourceFixAll ( setting ) { if ( setting === null ) { return undefined ; } if ( Array . isArray ( setting ) ) { return setting . includes ( 'source.fixAll' ) ? true : undefined ; } else { return setting [ 'source.fixAll' ] ; } } export function getSourceFixAllESLint ( setting ) { if ( setting === null ) { return undefined ; } else if ( Array . isArray ( setting ) ) { return setting . includes ( 'source.fixAll.eslint' ) ? true : undefined ; } else { return setting [ 'source.fixAll.eslint' ] ; } } export function setSourceFixAllESLint ( setting , value ) { if ( setting === null ) { return ; } else if ( Array . isArray ( setting ) ) { const index = setting . indexOf ( 'source.fixAll.eslint' ) ; if ( value === true ) { if ( index === - 1 ) { setting . push ( 'source.fixAll.eslint' ) ; } } else { if ( index >= 0 ) { setting . splice ( index , 1 ) ; } } } else { setting [ 'source.fixAll.eslint' ] = value ; } } } interface LanguageSettings { 'editor.codeActionsOnSave' ? : CodeActionsOnSave ; } namespace MigrationData { export function create < T > ( inspect ) { return inspect === undefined ? { global : { value : undefined , changed : false } , workspace : { value : undefined , changed : false } , workspaceFolder : { value : undefined , changed : false } } : { global : { value : inspect . globalValue , changed : false } , workspace : { value : inspect . workspaceValue , changed : false } , workspaceFolder : { value : inspect . workspaceFolderValue , changed : false } } ; } export function needsUpdate ( data ) { return data . global . changed || data . workspace . changed || data . workspaceFolder . changed ; } } class Migration { private workspaceConfig : WorkspaceConfiguration ; private eslintConfig : WorkspaceConfiguration ; private editorConfig : WorkspaceConfiguration ; private codeActionOnSave : MigrationData < CodeActionsOnSave > ; private languageSpecificSettings : Map < string , MigrationData < CodeActionsOnSave > > ; private autoFixOnSave : MigrationData < boolean > ; private validate : MigrationData < ( ValidateItem | string ) [ ] > ; private workingDirectories : MigrationData < ( string | DirectoryItem ) [ ] > ; private didChangeConfiguration : ( ( ) => void ) | undefined ; constructor ( resource ) { this . workspaceConfig = Workspace . getConfiguration ( undefined , resource ) ; this . eslintConfig = Workspace . getConfiguration ( 'eslint' , resource ) ; this . editorConfig = Workspace . getConfiguration ( 'editor' , resource ) ; this . codeActionOnSave = MigrationData . create ( this . editorConfig . inspect < CodeActionsOnSave > ( 'codeActionsOnSave' ) ) ; this . autoFixOnSave = MigrationData . create ( this . eslintConfig . inspect < boolean > ( 'autoFixOnSave' ) ) ; this . validate = MigrationData . create ( this . eslintConfig . inspect < ( ValidateItem | string ) [ ] > ( 'validate' ) ) ; this . workingDirectories = MigrationData . create ( this . eslintConfig . inspect < ( string | DirectoryItem ) [ ] > ( 'workingDirectories' ) ) ; this . languageSpecificSettings = new Map ( ) ; } public record ( ) { const fixAll = this . recordAutoFixOnSave ( ) ; this . recordValidate ( fixAll ) ; this . recordWorkingDirectories ( ) ; } public captureDidChangeSetting ( func ) { this . didChangeConfiguration = func ; } private recordAutoFixOnSave ( ) { function record ( this , elem , setting ) { if ( CodeActionsOnSave . isExplicitlyDisabled ( setting . value ) ) { return false ; } if ( ! Is . objectLiteral ( setting . value ) && ! Array . isArray ( setting . value ) ) { setting . value = Object . create ( null ) as { } ; } const autoFix = ! ! elem . value ; const sourceFixAll = ! ! CodeActionsOnSave . getSourceFixAll ( setting . value ) ; let result ; if ( autoFix !== sourceFixAll && autoFix && CodeActionsOnSave . getSourceFixAllESLint ( setting . value ) === undefined ) { CodeActionsOnSave . setSourceFixAllESLint ( setting . value , elem . value ) ; setting . changed = true ; result = ! ! CodeActionsOnSave . getSourceFixAllESLint ( setting . value ) ; } else { result = ! ! CodeActionsOnSave . getSourceFixAll ( setting . value ) ; } return result ; } return [ record ( this . autoFixOnSave . global , this . codeActionOnSave . global ) , record ( this . autoFixOnSave . workspace , this . codeActionOnSave . workspace ) , record ( this . autoFixOnSave . workspaceFolder , this . codeActionOnSave . workspaceFolder ) ] ; } private recordValidate ( fixAll ) { function record ( this , elem , settingAccessor , fixAll ) { if ( elem . value === undefined ) { return ; } for ( let i = 0 ; i < elem . value . length ; i ++ ) { const item = elem . value [ i ] ; if ( typeof item === 'string' ) { continue ; } if ( fixAll && item . autoFix === false && typeof item . language === 'string' ) { const setting = settingAccessor ( item . language ) ; if ( ! Is . objectLiteral ( setting . value ) && ! Array . isArray ( setting . value ) ) { setting . value = Object . create ( null ) as { } ; } if ( CodeActionsOnSave . getSourceFixAllESLint ( setting . value ! ) !== false ) { CodeActionsOnSave . setSourceFixAllESLint ( setting . value ! , false ) ; setting . changed = true ; } } } } const languageSpecificSettings = this . languageSpecificSettings ; const workspaceConfig = this . workspaceConfig ; function getCodeActionsOnSave ( language ) { let result = languageSpecificSettings . get ( language ) ; if ( result !== undefined ) { return result ; } const value = workspaceConfig . inspect ( `[${ language }]` ) ; if ( value === undefined ) { return MigrationData . create ( undefined ) ; } const globalValue = value . globalValue ?. [ 'editor.codeActionsOnSave' ] ; const workspaceFolderValue = value . workspaceFolderValue ?. [ 'editor.codeActionsOnSave' ] ; const workspaceValue = value . workspaceValue ?. [ 'editor.codeActionsOnSave' ] ; result = MigrationData . create < CodeActionsOnSave > ( { globalValue , workspaceFolderValue , workspaceValue } ) ; languageSpecificSettings . set ( language , result ) ; return result ; } record ( this . validate . global , ( language ) => getCodeActionsOnSave ( language ) . global , fixAll [ 0 ] ) ; record ( this . validate . workspace , ( language ) => getCodeActionsOnSave ( language ) . workspace , fixAll [ 1 ] ? fixAll [ 1 ] : fixAll [ 0 ] ) ; record ( this . validate . workspaceFolder , ( language ) => getCodeActionsOnSave ( language ) . workspaceFolder , fixAll [ 2 ] ? fixAll [ 2 ] : ( fixAll [ 1 ] ? fixAll [ 1 ] : fixAll [ 0 ] ) ) ; } private recordWorkingDirectories ( ) { function record ( this , elem ) { if ( elem . value === undefined || ! Array . isArray ( elem . value ) ) { return ; } for ( let i = 0 ; i < elem . value . length ; i ++ ) { const item = elem . value [ i ] ; if ( typeof item === 'string' || ModeItem . is ( item ) || PatternItem . is ( item ) ) { continue ; } if ( DirectoryItem . is ( item ) && item [ '!cwd' ] !== undefined ) { continue ; } } } record ( this . workingDirectories . global ) ; record ( this . workingDirectories . workspace ) ; record ( this . workingDirectories . workspaceFolder ) ; } public needsUpdate ( ) { if ( MigrationData . needsUpdate ( this . autoFixOnSave ) || MigrationData . needsUpdate ( this . validate ) || MigrationData . needsUpdate ( this . codeActionOnSave ) || MigrationData . needsUpdate ( this . workingDirectories ) ) { return true ; } for ( const value of this . languageSpecificSettings . values ( ) ) { if ( MigrationData . needsUpdate ( value ) ) { return true ; } } return false ; } public async update ( ) { async function _update < T > ( config , section , newValue , target ) { if ( ! newValue . changed ) { return ; } await config . update ( section , newValue . value , target ) ; } async function _updateLanguageSetting ( config , section , settings , newValue , target ) { if ( ! newValue . changed ) { return ; } if ( settings === undefined ) { settings = Object . create ( null ) as object ; } if ( settings [ 'editor.codeActionsOnSave' ] === undefined ) { settings [ 'editor.codeActionsOnSave' ] = { } ; } settings [ 'editor.codeActionsOnSave' ] = newValue . value ; await config . update ( section , settings , target ) ; } try { await _update ( this . editorConfig , 'codeActionsOnSave' , this . codeActionOnSave . global , ConfigurationTarget . Global ) ; await _update ( this . editorConfig , 'codeActionsOnSave' , this . codeActionOnSave . workspace , ConfigurationTarget . Workspace ) ; await _update ( this . editorConfig , 'codeActionsOnSave' , this . codeActionOnSave . workspaceFolder , ConfigurationTarget . WorkspaceFolder ) ; await _update ( this . eslintConfig , 'autoFixOnSave' , this . autoFixOnSave . global , ConfigurationTarget . Global ) ; await _update ( this . eslintConfig , 'autoFixOnSave' , this . autoFixOnSave . workspace , ConfigurationTarget . Workspace ) ; await _update ( this . eslintConfig , 'autoFixOnSave' , this . autoFixOnSave . workspaceFolder , ConfigurationTarget . WorkspaceFolder ) ; await _update ( this . eslintConfig , 'validate' , this . validate . global , ConfigurationTarget . Global ) ; await _update ( this . eslintConfig , 'validate' , this . validate . workspace , ConfigurationTarget . Workspace ) ; await _update ( this . eslintConfig , 'validate' , this . validate . workspaceFolder , ConfigurationTarget . WorkspaceFolder ) ; await _update ( this . eslintConfig , 'workingDirectories' , this . workingDirectories . global , ConfigurationTarget . Global ) ; await _update ( this . eslintConfig , 'workingDirectories' , this . workingDirectories . workspace , ConfigurationTarget . Workspace ) ; await _update ( this . eslintConfig , 'workingDirectories' , this . workingDirectories . workspaceFolder , ConfigurationTarget . WorkspaceFolder ) ; for ( const language of this . languageSpecificSettings . keys ( ) ) { const value = this . languageSpecificSettings . get ( language ) ! ; if ( MigrationData . needsUpdate ( value ) ) { const section = `[${ language }]` ; const current = this . workspaceConfig . inspect < LanguageSettings > ( section ) ; await _updateLanguageSetting ( this . workspaceConfig , section , current ?. globalValue , value . global , ConfigurationTarget . Global ) ; await _updateLanguageSetting ( this . workspaceConfig , section , current ?. workspaceValue , value . workspace , ConfigurationTarget . Workspace ) ; await _updateLanguageSetting ( this . workspaceConfig , section , current ?. workspaceFolderValue , value . workspaceFolder , ConfigurationTarget . WorkspaceFolder ) ; } } } finally { if ( this . didChangeConfiguration ) { this . didChangeConfiguration ( ) ; this . didChangeConfiguration = undefined ; } } } } function realActivate ( context ) { const statusBarItem = Window . createStatusBarItem ( 'generalStatus' , StatusBarAlignment . Right , 0 ) ; let serverRunning ; const starting = 'ESLint server is starting.' ; const running = 'ESLint server is running.' ; const stopped = 'ESLint server stopped.' ; statusBarItem . name = 'ESLint' ; statusBarItem . text = 'ESLint' ; statusBarItem . command = 'eslint.showOutputChannel' ; const documentStatus = new Map ( ) ; function updateDocumentStatus ( params ) { documentStatus . set ( params . uri , params . state ) ; updateStatusBar ( params . uri ) ; } function updateStatusBar ( uri ) { const status = function ( ) { if ( serverRunning === false ) { return Status . error ; } if ( uri === undefined ) { uri = Window . activeTextEditor ?. document . uri . toString ( ) ; } return ( uri !== undefined ? documentStatus . get ( uri ) : undefined ) ?? Status . ok ; } ( ) ; let icon ; let tooltip ; let text = 'ESLint' ; let backgroundColor ; let foregroundColor ; switch ( status ) { case Status . ok : icon = undefined ; foregroundColor = new ThemeColor ( 'statusBarItem.foreground' ) ; backgroundColor = new ThemeColor ( 'statusBarItem.background' ) ; break ; case Status . warn : icon = '$(alert)' ; foregroundColor = new ThemeColor ( 'statusBarItem.warningForeground' ) ; backgroundColor = new ThemeColor ( 'statusBarItem.warningBackground' ) ; break ; case Status . error : icon = '$(issue-opened)' ; foregroundColor = new ThemeColor ( 'statusBarItem.errorForeground' ) ; backgroundColor = new ThemeColor ( 'statusBarItem.errorBackground' ) ; break ; } statusBarItem . text = icon !== undefined ? `${ icon } ${ text }` : text ; statusBarItem . color = foregroundColor ; statusBarItem . backgroundColor = backgroundColor ; statusBarItem . tooltip = tooltip ? tooltip : serverRunning === undefined ? starting : serverRunning === true ? running : stopped ; const alwaysShow = Workspace . getConfiguration ( 'eslint' ) . get ( 'alwaysShowStatus' , false ) ; if ( alwaysShow || status !== Status . ok ) { statusBarItem . show ( ) ; } else { statusBarItem . hide ( ) ; } } function readCodeActionsOnSaveSetting ( document ) { let result = undefined ; const languageConfig = Workspace . getConfiguration ( undefined , document . uri ) . get < LanguageSettings > ( `[${ document . languageId }]` ) ; function isEnabled ( value ) { if ( value === undefined || value === null ) { return undefined ; } if ( Array . isArray ( value ) ) { const result = value . some ( ( element ) => { return element === 'source.fixAll.eslint' || element === 'source.fixAll' ; } ) ; return result === true ? true : undefined ; } else { return value [ 'source.fixAll.eslint' ] ?? value [ 'source.fixAll' ] ; } } if ( languageConfig !== undefined ) { const codeActionsOnSave = languageConfig ?. [ 'editor.codeActionsOnSave' ] ; if ( codeActionsOnSave !== undefined ) { result = isEnabled ( codeActionsOnSave ) ; } } if ( result === undefined ) { const codeActionsOnSave = Workspace . getConfiguration ( 'editor' , document . uri ) . get < CodeActionsOnSave > ( 'codeActionsOnSave' ) ; if ( codeActionsOnSave !== undefined ) { result = isEnabled ( codeActionsOnSave ) ; } } return result ?? false ; } function migrationFailed ( error ) { client . error ( error . message ?? 'Unknown error' , error ) ; void Window . showErrorMessage ( 'ESLint settings migration failed. Please see the ESLint output channel for further details' , 'Open Channel' ) . then ( ( selected ) => { if ( selected === undefined ) { return ; } client . outputChannel . show ( ) ; } ) ; } async function migrateSettings ( ) { const folders = Workspace . workspaceFolders ; if ( folders === undefined ) { void Window . showErrorMessage ( 'ESLint settings can only be converted if VS Code is opened on a workspace folder.' ) ; return ; } const folder = await pickFolder ( folders , 'Pick a folder to convert its settings' ) ; if ( folder === undefined ) { return ; } const migration = new Migration ( folder . uri ) ; migration . record ( ) ; if ( migration . needsUpdate ( ) ) { try { await migration . update ( ) ; } catch ( error ) { migrationFailed ( error ) ; } } } function sanitize < T , D > ( value , type , def ) { if ( Array . isArray ( value ) ) { return value . filter ( item => typeof item === type ) as unknown as T ; } else if ( typeof value !== type ) { return def ; } return value ; } const serverModule = Uri . joinPath ( context . extensionUri , 'server' , 'out' , 'eslintServer.js' ) . fsPath ; const eslintConfig = Workspace . getConfiguration ( 'eslint' ) ; const debug = sanitize ( eslintConfig . get < boolean > ( 'debug' , false ) ?? false , 'boolean' , false ) ; const runtime = sanitize ( eslintConfig . get < string | null > ( 'runtime' , null ) ?? undefined , 'string' , undefined ) ; const execArgv = sanitize ( eslintConfig . get < string [ ] | null > ( 'execArgv' , null ) ?? undefined , 'string' , undefined ) ; const nodeEnv = sanitize ( eslintConfig . get ( 'nodeEnv' , null ) ?? undefined , 'string' , undefined ) ; let env ; if ( debug ) { env = env || { } ; env . DEBUG = 'eslint:*,-eslint:code-path' ; } if ( nodeEnv !== undefined ) { env = env || { } ; env . NODE_ENV = nodeEnv ; } const debugArgv = [ '--nolazy' , '--inspect=6011' ] ; const serverOptions = { run : { module : serverModule , transport : TransportKind . ipc , runtime , options : { execArgv , cwd : process . cwd ( ) , env } } , debug : { module : serverModule , transport : TransportKind . ipc , runtime , options : { execArgv : execArgv !== undefined ? execArgv . concat ( debugArgv ) : debugArgv , cwd : process . cwd ( ) , env } } } ; let defaultErrorHandler ; let serverCalledProcessExit = false ; const packageJsonFilter = { scheme : 'file' , pattern : '**/package.json' } ; const configFileFilter = { scheme : 'file' , pattern : '**/.eslintr{c.js,c.yaml,c.yml,c,c.json}' } ; const syncedDocuments = new Map < string , TextDocument > ( ) ; let migration ; const migrationSemaphore = new Semaphore < void > ( 1 ) ; let notNow = false ; const supportedQuickFixKinds = new Set ( [ CodeActionKind . Source . value , CodeActionKind . SourceFixAll . value , `${ CodeActionKind . SourceFixAll . value }.eslint` , CodeActionKind . QuickFix . value ] ) ; const clientOptions = { documentSelector : [ { scheme : 'file' } , { scheme : 'untitled' } ] , diagnosticCollectionName : 'eslint' , revealOutputChannelOn : RevealOutputChannelOn . Never , initializationOptions : { } , progressOnInitialization : true , synchronize : { fileEvents : [ Workspace . createFileSystemWatcher ( '**/.eslintr{c.js,c.yaml,c.yml,c,c.json}' ) , Workspace . createFileSystemWatcher ( '**/.eslintignore' ) , Workspace . createFileSystemWatcher ( '**/package.json' ) ] } , initializationFailedHandler : ( error ) => { client . error ( 'Server initialization failed.' , error ) ; client . outputChannel . show ( true ) ; return false ; } , errorHandler : { error : ( error , message , count ) : ErrorHandlerResult => { return defaultErrorHandler . error ( error , message , count ) ; } , closed : ( ) : CloseHandlerResult => { if ( serverCalledProcessExit ) { return { action : CloseAction . DoNotRestart } ; } return defaultErrorHandler . closed ( ) ; } } , middleware : { didOpen : async ( document , next ) => { if ( Languages . match ( packageJsonFilter , document ) || Languages . match ( configFileFilter , document ) || computeValidate ( document ) !== Validate . off ) { const result = next ( document ) ; syncedDocuments . set ( document . uri . toString ( ) , document ) ; return result ; } } , didChange : async ( event , next ) => { if ( syncedDocuments . has ( event . document . uri . toString ( ) ) ) { return next ( event ) ; } } , willSave : async ( event , next ) => { if ( syncedDocuments . has ( event . document . uri . toString ( ) ) ) { return next ( event ) ; } } , willSaveWaitUntil : ( event , next ) => { if ( syncedDocuments . has ( event . document . uri . toString ( ) ) ) { return next ( event ) ; } else { return Promise . resolve ( [ ] ) ; } } , didSave : async ( document , next ) => { if ( syncedDocuments . has ( document . uri . toString ( ) ) ) { return next ( document ) ; } } , didClose : async ( document , next ) => { const uri = document . uri . toString ( ) ; if ( syncedDocuments . has ( uri ) ) { syncedDocuments . delete ( uri ) ; return next ( document ) ; } } , provideCodeActions : ( document , range , context , token , next ) : ProviderResult < ( Command | CodeAction ) [ ] > => { if ( ! syncedDocuments . has ( document . uri . toString ( ) ) ) { return [ ] ; } if ( context . only !== undefined && ! supportedQuickFixKinds . has ( context . only . value ) ) { return [ ] ; } if ( context . only === undefined && ( ! context . diagnostics || context . diagnostics . length === 0 ) ) { return [ ] ; } const eslintDiagnostics = [ ] ; for ( const diagnostic of context . diagnostics ) { if ( diagnostic . source === 'eslint' ) { eslintDiagnostics . push ( diagnostic ) ; } } if ( context . only === undefined && eslintDiagnostics . length === 0 ) { return [ ] ; } const newContext = Object . assign ( { } , context , { diagnostics : eslintDiagnostics } ) ; return next ( document , range , newContext , token ) ; } , workspace : { didChangeWatchedFile : ( event , next ) => { probeFailed . clear ( ) ; return next ( event ) ; } , didChangeConfiguration : async ( sections , next ) => { if ( migration !== undefined && ( sections === undefined || sections . length === 0 ) ) { migration . captureDidChangeSetting ( ( ) => { return next ( sections ) ; } ) ; } else { return next ( sections ) ; } } , configuration : async ( params , _token , _next ) : Promise < any [ ] > => { if ( params . items === undefined ) { return [ ] ; } const result = [ ] ; for ( const item of params . items ) { if ( item . section || ! item . scopeUri ) { result . push ( null ) ; continue ; } const resource = client . protocol2CodeConverter . asUri ( item . scopeUri ) ; const config = Workspace . getConfiguration ( 'eslint' , resource ) ; const workspaceFolder = resource . scheme === 'untitled' ? Workspace . workspaceFolders !== undefined ? Workspace . workspaceFolders [ 0 ] : undefined : Workspace . getWorkspaceFolder ( resource ) ; await migrationSemaphore . lock ( async ( ) => { const globalMigration = Workspace . getConfiguration ( 'eslint' ) . get ( 'migration.2_x' , 'on' ) ; if ( notNow === false && globalMigration === 'on' ) { try { migration = new Migration ( resource ) ; migration . record ( ) ; interface Item extends MessageItem { id : 'yes' | 'no' | 'readme' | 'global' | 'local' ; } if ( migration . needsUpdate ( ) ) { const folder = workspaceFolder ?. name ; const file = path . basename ( resource . fsPath ) ; const selected = await Window . showInformationMessage < Item > ( [ `The ESLint 'autoFixOnSave' setting needs to be migrated to the new 'editor.codeActionsOnSave' setting` , folder !== undefined ? `for the workspace folder: ${ folder }.` : `for the file: ${ file }.` , `For compatibility reasons the 'autoFixOnSave' remains and needs to be removed manually.` , `Do you want to migrate the setting?` ] . join ( ' ' ) , { modal : true } , { id : 'yes' , title : 'Yes' } , { id : 'global' , title : 'Never migrate Settings' } , { id : 'readme' , title : 'Open Readme' } , { id : 'no' , title : 'Not now' , isCloseAffordance : true } ) ; if ( selected !== undefined ) { if ( selected . id === 'yes' ) { try { await migration . update ( ) ; } catch ( error ) { migrationFailed ( error ) ; } } else if ( selected . id === 'no' ) { notNow = true ; } else if ( selected . id === 'global' ) { await config . update ( 'migration.2_x' , 'off' , ConfigurationTarget . Global ) ; } else if ( selected . id === 'readme' ) { notNow = true ; void Env . openExternal ( Uri . parse ( 'https://github.com/microsoft/vscode-eslint#settings-migration' ) ) ; } } } } finally { migration = undefined ; } } } ) ; const settings = { validate : Validate . off , packageManager : config . get ( 'packageManager' , 'npm' ) , useESLintClass : config . get ( 'useESLintClass' , false ) , codeActionOnSave : { enable : false , mode : CodeActionsOnSaveMode . all } , format : false , quiet : config . get ( 'quiet' , false ) , onIgnoredFiles : ESLintSeverity . from ( config . get < string > ( 'onIgnoredFiles' , ESLintSeverity . off ) ) , options : config . get ( 'options' , { } ) , rulesCustomizations : parseRulesCustomizations ( config . get ( 'rules.customizations' ) ) , run : config . get ( 'run' , 'onType' ) , nodePath : config . get < string | undefined > ( 'nodePath' , undefined ) ?? null , workingDirectory : undefined , workspaceFolder : undefined , codeAction : { disableRuleComment : config . get ( 'codeAction.disableRuleComment' , { enable : true , location : 'separateLine' as 'separateLine' } ) , showDocumentation : config . get ( 'codeAction.showDocumentation' , { enable : true } ) } } ; const document = syncedDocuments . get ( item . scopeUri ) ; if ( document === undefined ) { result . push ( settings ) ; continue ; } if ( config . get ( 'enabled' , true ) ) { settings . validate = computeValidate ( document ) ; } if ( settings . validate !== Validate . off ) { settings . format = ! ! config . get ( 'format.enable' , false ) ; settings . codeActionOnSave . enable = readCodeActionsOnSaveSetting ( document ) ; settings . codeActionOnSave . mode = CodeActionsOnSaveMode . from ( config . get ( 'codeActionsOnSave.mode' , CodeActionsOnSaveMode . all ) ) ; settings . codeActionOnSave . rules = CodeActionsOnSaveRules . from ( config . get ( 'codeActionsOnSave.rules' , null ) ) ; } if ( workspaceFolder !== undefined ) { settings . workspaceFolder = { name : workspaceFolder . name , uri : client . code2ProtocolConverter . asUri ( workspaceFolder . uri ) } ; } const workingDirectories = config . get < ( string | LegacyDirectoryItem | DirectoryItem | PatternItem | ModeItem ) [ ] | undefined > ( 'workingDirectories' , undefined ) ; if ( Array . isArray ( workingDirectories ) ) { let workingDirectory = undefined ; const workspaceFolderPath = workspaceFolder && workspaceFolder . uri . scheme === 'file' ? workspaceFolder . uri . fsPath : undefined ; for ( const entry of workingDirectories ) { let directory ; let pattern ; let noCWD = false ; if ( Is . string ( entry ) ) { directory = entry ; } else if ( LegacyDirectoryItem . is ( entry ) ) { directory = entry . directory ; noCWD = ! entry . changeProcessCWD ; } else if ( DirectoryItem . is ( entry ) ) { directory = entry . directory ; if ( entry [ '!cwd' ] !== undefined ) { noCWD = entry [ '!cwd' ] ; } } else if ( PatternItem . is ( entry ) ) { pattern = entry . pattern ; if ( entry [ '!cwd' ] !== undefined ) { noCWD = entry [ '!cwd' ] ; } } else if ( ModeItem . is ( entry ) ) { workingDirectory = entry ; continue ; } let itemValue ; if ( directory !== undefined || pattern !== undefined ) { const filePath = document . uri . scheme === 'file' ? document . uri . fsPath : undefined ; if ( filePath !== undefined ) { if ( directory !== undefined ) { directory = toOSPath ( directory ) ; if ( ! path . isAbsolute ( directory ) && workspaceFolderPath !== undefined ) { directory = path . join ( workspaceFolderPath , directory ) ; } if ( directory . charAt ( directory . length - 1 ) !== path . sep ) { directory = directory + path . sep ; } if ( filePath . startsWith ( directory ) ) { itemValue = directory ; } } else if ( pattern !== undefined && pattern . length > 0 ) { if ( ! path . posix . isAbsolute ( pattern ) && workspaceFolderPath !== undefined ) { pattern = path . posix . join ( toPosixPath ( workspaceFolderPath ) , pattern ) ; } if ( pattern . charAt ( pattern . length - 1 ) !== path . posix . sep ) { pattern = pattern + path . posix . sep ; } const regExp = convert2RegExp ( pattern ) ; if ( regExp !== undefined ) { const match = regExp . exec ( filePath ) ; if ( match !== null && match . length > 0 ) { itemValue = match [ 0 ] ; } } } } } if ( itemValue !== undefined ) { if ( workingDirectory === undefined || ModeItem . is ( workingDirectory ) ) { workingDirectory = { directory : itemValue , '!cwd' : noCWD } ; } else { if ( workingDirectory . directory . length < itemValue . length ) { workingDirectory . directory = itemValue ; workingDirectory [ '!cwd' ] = noCWD ; } } } } settings . workingDirectory = workingDirectory ; } result . push ( settings ) ; } return result ; } } } } ; let client ; try { client = new LanguageClient ( 'ESLint' , serverOptions , clientOptions ) ; } catch ( err ) { void Window . showErrorMessage ( `The ESLint extension couldn't be started. See the ESLint output channel for details.` ) ; return ; } Workspace . onDidChangeConfiguration ( ( ) => { probeFailed . clear ( ) ; for ( const textDocument of syncedDocuments . values ( ) ) { if ( computeValidate ( textDocument ) === Validate . off ) { try { const provider = client . getFeature ( DidCloseTextDocumentNotification . method ) . getProvider ( textDocument ) ; provider ?. send ( textDocument ) ; } catch ( err ) { } } } for ( const textDocument of Workspace . textDocuments ) { if ( ! syncedDocuments . has ( textDocument . uri . toString ( ) ) && computeValidate ( textDocument ) !== Validate . off ) { try { const provider = client . getFeature ( DidOpenTextDocumentNotification . method ) . getProvider ( textDocument ) ; provider ?. send ( textDocument ) ; } catch ( err ) { } } } } ) ; defaultErrorHandler = client . createDefaultErrorHandler ( ) ; client . onDidChangeState ( ( event ) => { if ( event . newState === ClientState . Starting ) { client . info ( 'ESLint server is starting' ) ; serverRunning = undefined ; } else if ( event . newState === ClientState . Running ) { client . info ( running ) ; serverRunning = true ; } else { client . info ( stopped ) ; serverRunning = false ; } updateStatusBar ( undefined ) ; } ) ; const readyHandler = ( ) => { client . onNotification ( ShowOutputChannel . type , ( ) => { client . outputChannel . show ( ) ; } ) ; client . onNotification ( StatusNotification . type , ( params ) => { updateDocumentStatus ( params ) ; } ) ; client . onNotification ( exitCalled , ( params ) => { serverCalledProcessExit = true ; client . error ( `Server process exited with code ${ params [ 0 ] }. This usually indicates a misconfigured ESLint setup.` , params [ 1 ] ) ; void Window . showErrorMessage ( `ESLint server shut down itself. See 'ESLint' output channel for details.` , { title : 'Open Output' , id : 1 } ) . then ( ( value ) => { if ( value !== undefined && value . id === 1 ) { client . outputChannel . show ( ) ; } } ) ; } ) ; client . onRequest ( NoConfigRequest . type , ( params ) => { const document = Uri . parse ( params . document . uri ) ; const workspaceFolder = Workspace . getWorkspaceFolder ( document ) ; const fileLocation = document . fsPath ; if ( workspaceFolder ) { client . warn ( [ '' , `No ESLint configuration (e.g .eslintrc) found for file: ${ fileLocation }` , `File will not be validated. Consider running 'eslint --init' in the workspace folder ${ workspaceFolder . name }` , `Alternatively you can disable ESLint by executing the 'Disable ESLint' command.` ] . join ( '\n' ) ) ; } else { client . warn ( [ '' , `No ESLint configuration (e.g .eslintrc) found for file: ${ fileLocation }` , `File will not be validated. Alternatively you can disable ESLint by executing the 'Disable ESLint' command.` ] . join ( '\n' ) ) ; } updateDocumentStatus ( { uri : params . document . uri , state : Status . error } ) ; return { } ; } ) ; client . onRequest ( NoESLintLibraryRequest . type , ( params ) => { const key = 'noESLintMessageShown' ; const state = context . globalState . get < NoESLintState > ( key , { } ) ; const uri = Uri . parse ( params . source . uri ) ; const workspaceFolder = Workspace . getWorkspaceFolder ( uri ) ; const packageManager = Workspace . getConfiguration ( 'eslint' , uri ) . get ( 'packageManager' , 'npm' ) ; const localInstall = { npm : 'npm install eslint' , pnpm : 'pnpm install eslint' , yarn : 'yarn add eslint' , } ; const globalInstall = { npm : 'npm install -g eslint' , pnpm : 'pnpm install -g eslint' , yarn : 'yarn global add eslint' } ; const isPackageManagerNpm = packageManager === 'npm' ; interface ButtonItem extends MessageItem { id : number ; } const outputItem = { title : 'Go to output' , id : 1 } ; if ( workspaceFolder ) { client . info ( [ '' , `Failed to load the ESLint library for the document ${ uri . fsPath }` , '' , `To use ESLint please install eslint by running ${ localInstall [ packageManager ] } in the workspace folder ${ workspaceFolder . name }` , `or globally using '${ globalInstall [ packageManager ] }'. You need to reopen the workspace after installing eslint.` , '' , isPackageManagerNpm ? 'If you are using yarn or pnpm instead of npm set the setting `eslint.packageManager` to either `yarn` or `pnpm`' : null , `Alternatively you can disable ESLint for the workspace folder ${ workspaceFolder . name } by executing the 'Disable ESLint' command.` ] . filter ( ( str => ( str !== null ) ) ) . join ( '\n' ) ) ; if ( state . workspaces === undefined ) { state . workspaces = { } ; } if ( ! state . workspaces [ workspaceFolder . uri . toString ( ) ] ) { state . workspaces [ workspaceFolder . uri . toString ( ) ] = true ; void context . globalState . update ( key , state ) ; void Window . showInformationMessage ( `Failed to load the ESLint library for the document ${ uri . fsPath }. See the output for more information.` , outputItem ) . then ( ( item ) => { if ( item && item . id === 1 ) { client . outputChannel . show ( true ) ; } } ) ; } } else { client . info ( [ `Failed to load the ESLint library for the document ${ uri . fsPath }` , `To use ESLint for single JavaScript file install eslint globally using '${ globalInstall [ packageManager ] }'.` , isPackageManagerNpm ? 'If you are using yarn or pnpm instead of npm set the setting `eslint.packageManager` to either `yarn` or `pnpm`' : null , 'You need to reopen VS Code after installing eslint.' , ] . filter ( ( str => ( str !== null ) ) ) . join ( '\n' ) ) ; if ( ! state . global ) { state . global = true ; void context . globalState . update ( key , state ) ; void Window . showInformationMessage ( `Failed to load the ESLint library for the document ${ uri . fsPath }. See the output for more information.` , outputItem ) . then ( ( item ) => { if ( item && item . id === 1 ) { client . outputChannel . show ( true ) ; } } ) ; } } return { } ; } ) ; client . onRequest ( OpenESLintDocRequest . type , async ( params ) => { await Commands . executeCommand ( 'vscode.open' , Uri . parse ( params . url ) ) ; return { } ; } ) ; client . onRequest ( ProbeFailedRequest . type , ( params ) => { probeFailed . add ( params . textDocument . uri ) ; const closeFeature = client . getFeature ( DidCloseTextDocumentNotification . method ) ; for ( const document of Workspace . textDocuments ) { if ( document . uri . toString ( ) === params . textDocument . uri ) { closeFeature . getProvider ( document ) ?. send ( document ) ; } } } ) ; } ; client . onReady ( ) . then ( readyHandler ) . catch ( ( error ) => client . error ( `On ready failed` , error ) ) ; if ( onActivateCommands ) { onActivateCommands . forEach ( command => command . dispose ( ) ) ; onActivateCommands = undefined ; } context . subscriptions . push ( client . start ( ) , Window . onDidChangeActiveTextEditor ( ( ) => { updateStatusBar ( undefined ) ; } ) , Workspace . onDidCloseTextDocument ( ( document ) => { const uri = document . uri . toString ( ) ; documentStatus . delete ( uri ) ; updateStatusBar ( undefined ) ; } ) , Commands . registerCommand ( 'eslint.executeAutofix' , async ( ) => { const textEditor = Window . activeTextEditor ; if ( ! textEditor ) { return ; } const textDocument = { uri : textEditor . document . uri . toString ( ) , version : textEditor . document . version } ; const params = { command : 'eslint.applyAllFixes' , arguments : [ textDocument ] } ; await client . onReady ( ) ; client . sendRequest ( ExecuteCommandRequest . type , params ) . then ( undefined , ( ) => { void Window . showErrorMessage ( 'Failed to apply ESLint fixes to the document. Please consider opening an issue with steps to reproduce.' ) ; } ) ; } ) , Commands . registerCommand ( 'eslint.showOutputChannel' , async ( ) => { client . outputChannel . show ( ) ; } ) , Commands . registerCommand ( 'eslint.migrateSettings' , ( ) => { void migrateSettings ( ) ; } ) , Commands . registerCommand ( 'eslint.restart' , async ( ) => { await client . stop ( ) ; const start = ( ) => { client . start ( ) ; client . onReady ( ) . then ( readyHandler ) . catch ( ( error ) => client . error ( `On ready failed` , error ) ) ; } ; if ( isInDebugMode ( ) ) { setTimeout ( start , 1000 ) ; } else { start ( ) ; } } ) ) ; } export function deactivate ( ) { if ( onActivateCommands ) { onActivateCommands . forEach ( command => command . dispose ( ) ) ; } if ( taskProvider ) { taskProvider . dispose ( ) ; } }