getDeprecatedConfiguration('lint.jshint.prefix'); $bin = $this->getDeprecatedConfiguration('lint.jshint.bin', 'jshint'); if ($prefix) { return $prefix.'/'.$bin; } else { return $bin; } } public function getVersion() { // NOTE: `jshint --version` emits version information on stderr, not stdout. list($stdout, $stderr) = execx( '%C --version', $this->getExecutableCommand()); $matches = array(); $regex = '/^jshint v(?P\d+\.\d+\.\d+)$/'; if (preg_match($regex, $stderr, $matches)) { return $matches['version']; } else { return false; } } public function getInstallInstructions() { return pht('Install JSHint using `npm install -g jshint`.'); } protected function getMandatoryFlags() { $options = array(); $options[] = '--reporter='.dirname(realpath(__FILE__)).'/reporter.js'; if ($this->jshintrc) { $options[] = '--config='.$this->jshintrc; } if ($this->jshintignore) { $options[] = '--exclude-path='.$this->jshintignore; } return $options; } public function getLinterConfigurationOptions() { $options = array( 'jshint.jshintignore' => array( 'type' => 'optional string', 'help' => pht('Pass in a custom jshintignore file path.'), ), 'jshint.jshintrc' => array( 'type' => 'optional string', 'help' => pht('Custom configuration file.'), ), ); return $options + parent::getLinterConfigurationOptions(); } public function setLinterConfigurationValue($key, $value) { switch ($key) { case 'jshint.jshintignore': $this->jshintignore = $value; return; case 'jshint.jshintrc': $this->jshintrc = $value; return; } return parent::setLinterConfigurationValue($key, $value); } protected function getDefaultFlags() { $options = $this->getDeprecatedConfiguration( 'lint.jshint.options', array()); $config = $this->getDeprecatedConfiguration('lint.jshint.config'); if ($config) { $options[] = '--config='.$config; } return $options; } protected function parseLinterOutput($path, $err, $stdout, $stderr) { $errors = null; try { $errors = phutil_json_decode($stdout); } catch (PhutilJSONParserException $ex) { // Something went wrong and we can't decode the output. Exit abnormally. throw new PhutilProxyException( pht('JSHint returned unparseable output.'), $ex); } $messages = array(); foreach ($errors as $err) { $message = new ArcanistLintMessage(); $message->setPath($path); $message->setLine(idx($err, 'line')); $message->setChar(idx($err, 'col')); $message->setCode(idx($err, 'code')); $message->setName('JSHint'.idx($err, 'code')); $message->setDescription(idx($err, 'reason')); $message->setSeverity($this->getLintMessageSeverity(idx($err, 'code'))); $messages[] = $message; } return $messages; } protected function getLintCodeFromLinterConfigurationKey($code) { if (!preg_match('/^(E|W)\d+$/', $code)) { throw new Exception( pht( 'Unrecognized lint message code "%s". Expected a valid JSHint '. 'lint code like "%s" or "%s".', $code, 'E033', 'W093')); } return $code; } }