src/lint/linter/LintTrapLinter.php (126 lines of code) (raw):
<?php
/**
* Uses LintTrap to detect errors and potential problems in JavaScript code.
*/
final class LintTrapLinter extends ArcanistExternalLinter {
private $lintignore;
private $lintrc;
private $maxLineLength;
public function getInfoName() {
return 'LintTrap';
}
public function getInfoURI() {
return 'https://github.com/uber/lint-trap';
}
public function getInfoDescription() {
return pht('Use `lint-trap` to detect issues with JavaScript source files.');
}
public function getLinterName() {
return 'LintTrap';
}
public function getLinterConfigurationName() {
return 'lint-trap';
}
protected function getDefaultMessageSeverity($code) {
if (preg_match('/^warning/', $code)) {
return ArcanistLintSeverity::SEVERITY_WARNING;
} else {
return ArcanistLintSeverity::SEVERITY_ERROR;
}
}
public function getDefaultBinary() {
return getcwd().'/node_modules/.bin/lint-trap';
}
public function getVersion() {
list($stdout, $stderr) = execx(
'%C --version',
$this->getExecutableCommand());
$matches = array();
$regex = '/^lint-trap v(?P<version>\d+\.\d+\.\d+)$/';
if (preg_match($regex, $stderr, $matches)) {
return $matches['version'];
} else {
return false;
}
}
public function getInstallInstructions() {
return pht('Install lint-trap using `npm install lint-trap`.');
}
public function shouldExpectCommandErrors() {
return true;
}
public function supportsReadDataFromStdin() {
return true;
}
protected function getMandatoryFlags() {
$options = array(getcwd(), '--reporter=json');
return $options;
}
public function getLinterConfigurationOptions() {
$options = array(
'lint-trap.lintignore' => array(
'type' => 'optional string',
'help' => pht('Pass in a custom .lintignore file path.'),
),
'lint-trap.lintrc' => array(
'type' => 'optional string',
'help' => pht('Custom .lintrc configuration file.'),
),
'text.max-line-length' => array(
'type' => 'optional int',
'help' => pht(
'Adjust the maximum line length before a warning is raised. By '.
'default, a warning is raised on lines exceeding 80 characters.'),
),
);
return $options + parent::getLinterConfigurationOptions();
}
public function setMaxLineLength($new_length) {
$this->maxLineLength = $new_length;
return $this;
}
public function setLinterConfigurationValue($key, $value) {
switch ($key) {
case 'lint-trap.lintignore':
$this->lintignore = $value;
return;
case 'lint-trap.lintrc':
$this->lintrc = $value;
return;
case 'text.max-line-length':
$this->setMaxLineLength($value);
return;
}
return parent::setLinterConfigurationValue($key, $value);
}
protected function getDefaultFlags() {
$options = array();
if ($this->maxLineLength) {
$options[] = '--line-length='.$this->maxLineLength;
}
return $options;
}
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
$json = json_decode($stdout, true);
$files = idx($json, 'files');
if (!is_array($files)) {
// Something went wrong and we can't decode the output. Exit abnormally.
throw new ArcanistUsageException(
"lint-trap returned unparseable output.\n".
"stdout:\n\n{$stdout}".
"stderr:\n\n{$stderr}");
}
$messages = array();
foreach ($files as $f) {
$errors = idx($f, 'errors');
foreach ($errors as $err) {
$message = new ArcanistLintMessage();
$message->setPath(idx($f, 'file'));
$message->setLine(idx($err, 'line'));
$message->setChar(idx($err, 'column'));
$message->setCode(idx($err, 'rule'));
$message->setName(idx($err, 'linter').'.'.idx($err, 'rule'));
$message->setDescription(idx($err, 'message'));
$message->setSeverity($this->getLintMessageSeverity(idx($err, 'type')));
$messages[] = $message;
}
}
return $messages;
}
}