UNPKG

typescript-assistant

Version:

Combines and integrates professional Typescript tools into your project

164 lines (151 loc) 4.2 kB
import { Bus, EventType } from '../bus'; import { Git } from '../git'; import { Logger } from '../logger'; import { TaskRunner } from '../taskrunner'; import { absolutePath, isTypescriptFile } from '../util'; import { ChildProcess, fork } from 'child_process'; export interface Linter { start(trigger: EventType): void; stop(): void; lintOnce(fix: boolean, files?: string[]): Promise<boolean>; } /** * The messages that are sent to the linter-process */ export interface LinterCommand { fix: boolean; filesToLint: string[]; } export interface LinterResponse { violation?: { fileName: string; message: string; line: number; column: number; hasFix: boolean; }; finished?: { success: boolean; }; error?: { message: string; }; } export let createLinter = (dependencies: { taskRunner: TaskRunner, logger: Logger, bus: Bus, git: Git }): Linter => { let { logger, bus, git } = dependencies; let logError = (err: any) => logger.error('linter', `error: ${err}`); let lintProcess: ChildProcess | undefined; let running = false; let rescheduled = false; let fix = false; let errors = 0; let fixable = 0; let startLint = async (files?: string[]) => { rescheduled = false; running = true; bus.report({ tool: 'lint', status: 'busy' }); if (!files) { files = (await git.findChangedFiles()).filter(isTypescriptFile); } logger.log('linter', `Linting ${files.length} files...`); errors = 0; fixable = 0; let command: LinterCommand = { fix: fix, filesToLint: files }; lintProcess!.send(command); }; let lint = (files?: string[]) => { if (rescheduled) { return; } else if (running) { rescheduled = true; } else { startLint(files).catch(logError); } }; let startProcess = () => { lintProcess = fork(`${__dirname}/linter-process`, [], { execArgv: process.execArgv.filter(arg => !arg.includes('inspect')) }); lintProcess.on('close', (code: number) => { if (code !== 0 && code !== null) { logger.log('linter', `linting process exited with code ${code}`); } }); lintProcess.on('message', (response: LinterResponse) => { if (response.violation) { let { fileName, line, column, message, hasFix } = response.violation; errors++; if (hasFix) { fixable++; } logger.log('linter', `${absolutePath(fileName)}:${line}:${column} ${message}`); } if (response.finished) { running = false; logger.log('linter', response.finished.success ? 'All files are ok' : `${errors} Linting problems found, ${fixable} ${fix ? 'fixed' : 'fixable'}`); bus.signal(response.finished.success ? 'lint-linted' : 'lint-errored'); bus.report({ tool: 'lint', status: 'ready', errors: errors, fixable: fixable }); if (rescheduled) { startLint().catch(logError); } } if (response.error) { logger.error('linter', response.error.message); } }); }; let lintCallback = () => lint(); return { start: (trigger: EventType) => { startProcess(); bus.register(trigger, lintCallback); }, stop: () => { bus.unregister(lintCallback); lintProcess!.kill(); lintProcess = undefined; }, lintOnce: (fixOnce: boolean, files?: string[]) => { fix = fixOnce; let isRunning = lintProcess !== undefined; if (!isRunning) { startProcess(); } return new Promise((resolve) => { let ready = () => { bus.unregister(linted); bus.unregister(errored); fix = false; if (!isRunning) { lintProcess!.kill(); lintProcess = undefined; } }; let linted = () => { ready(); resolve(true); }; let errored = () => { ready(); resolve(false); }; bus.register('lint-linted', linted); bus.register('lint-errored', errored); lint(files); }); } }; };