~ubuntu-branches/ubuntu/wily/phabricator/wily

« back to all changes in this revision

Viewing changes to src/lint/linter/ArcanistJSHintLinter.php

  • Committer: Package Import Robot
  • Author(s): Richard Sellam
  • Date: 2014-11-01 23:20:06 UTC
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: package-import@ubuntu.com-20141101232006-mvlnp0cil67tsboe
Tags: upstream-0~git20141101/arcanist
Import upstream version 0~git20141101, component arcanist

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
/**
 
4
 * Uses JSHint to detect errors and potential problems in JavaScript code.
 
5
 */
 
6
final class ArcanistJSHintLinter extends ArcanistExternalLinter {
 
7
 
 
8
  private $jshintignore;
 
9
  private $jshintrc;
 
10
 
 
11
  public function getInfoName() {
 
12
    return 'JSHint';
 
13
  }
 
14
 
 
15
  public function getInfoURI() {
 
16
    return 'http://www.jshint.com';
 
17
  }
 
18
 
 
19
  public function getInfoDescription() {
 
20
    return pht('Use `jshint` to detect issues with JavaScript source files.');
 
21
  }
 
22
 
 
23
  public function getLinterName() {
 
24
    return 'JSHint';
 
25
  }
 
26
 
 
27
  public function getLinterConfigurationName() {
 
28
    return 'jshint';
 
29
  }
 
30
 
 
31
  protected function getDefaultMessageSeverity($code) {
 
32
    if (preg_match('/^W/', $code)) {
 
33
      return ArcanistLintSeverity::SEVERITY_WARNING;
 
34
    } else if (preg_match('/^E043$/', $code)) {
 
35
      // TODO: If JSHint encounters a large number of errors, it will quit
 
36
      // prematurely and add an additional "Too Many Errors" error. Ideally, we
 
37
      // should be able to pass some sort of `--force` option to `jshint`.
 
38
      //
 
39
      // See https://github.com/jshint/jshint/issues/180
 
40
      return ArcanistLintSeverity::SEVERITY_DISABLED;
 
41
    } else {
 
42
      return ArcanistLintSeverity::SEVERITY_ERROR;
 
43
    }
 
44
  }
 
45
 
 
46
  public function getDefaultBinary() {
 
47
    $prefix = $this->getDeprecatedConfiguration('lint.jshint.prefix');
 
48
    $bin = $this->getDeprecatedConfiguration('lint.jshint.bin', 'jshint');
 
49
 
 
50
    if ($prefix) {
 
51
      return $prefix.'/'.$bin;
 
52
    } else {
 
53
      return $bin;
 
54
    }
 
55
  }
 
56
 
 
57
  public function getVersion() {
 
58
    // NOTE: `jshint --version` emits version information on stderr, not stdout.
 
59
    list($stdout, $stderr) = execx(
 
60
      '%C --version',
 
61
      $this->getExecutableCommand());
 
62
 
 
63
    $matches = array();
 
64
    $regex = '/^jshint v(?P<version>\d+\.\d+\.\d+)$/';
 
65
    if (preg_match($regex, $stderr, $matches)) {
 
66
      return $matches['version'];
 
67
    } else {
 
68
      return false;
 
69
    }
 
70
  }
 
71
 
 
72
  public function getInstallInstructions() {
 
73
    return pht('Install JSHint using `npm install -g jshint`.');
 
74
  }
 
75
 
 
76
  public function shouldExpectCommandErrors() {
 
77
    return true;
 
78
  }
 
79
 
 
80
  public function supportsReadDataFromStdin() {
 
81
    return true;
 
82
  }
 
83
 
 
84
  public function getReadDataFromStdinFilename() {
 
85
    return '-';
 
86
  }
 
87
 
 
88
  protected function getMandatoryFlags() {
 
89
    $options = array();
 
90
 
 
91
    $options[] = '--reporter='.dirname(realpath(__FILE__)).'/reporter.js';
 
92
 
 
93
    if ($this->jshintrc) {
 
94
      $options[] = '--config='.$this->jshintrc;
 
95
    }
 
96
 
 
97
    if ($this->jshintignore) {
 
98
      $options[] = '--exclude-path='.$this->jshintignore;
 
99
    }
 
100
 
 
101
    return $options;
 
102
  }
 
103
 
 
104
  public function getLinterConfigurationOptions() {
 
105
    $options = array(
 
106
      'jshint.jshintignore' => array(
 
107
        'type' => 'optional string',
 
108
        'help' => pht('Pass in a custom jshintignore file path.'),
 
109
      ),
 
110
      'jshint.jshintrc' => array(
 
111
        'type' => 'optional string',
 
112
        'help' => pht('Custom configuration file.'),
 
113
      ),
 
114
    );
 
115
 
 
116
    return $options + parent::getLinterConfigurationOptions();
 
117
  }
 
118
 
 
119
  public function setLinterConfigurationValue($key, $value) {
 
120
    switch ($key) {
 
121
      case 'jshint.jshintignore':
 
122
        $this->jshintignore = $value;
 
123
        return;
 
124
 
 
125
      case 'jshint.jshintrc':
 
126
        $this->jshintrc = $value;
 
127
        return;
 
128
    }
 
129
 
 
130
    return parent::setLinterConfigurationValue($key, $value);
 
131
  }
 
132
 
 
133
  protected function getDefaultFlags() {
 
134
    $options = $this->getDeprecatedConfiguration(
 
135
      'lint.jshint.options',
 
136
      array());
 
137
 
 
138
    $config = $this->getDeprecatedConfiguration('lint.jshint.config');
 
139
    if ($config) {
 
140
      $options[] = '--config='.$config;
 
141
    }
 
142
 
 
143
    return $options;
 
144
  }
 
145
 
 
146
  protected function parseLinterOutput($path, $err, $stdout, $stderr) {
 
147
    $errors = json_decode($stdout, true);
 
148
 
 
149
    if (!is_array($errors)) {
 
150
      // Something went wrong and we can't decode the output. Exit abnormally.
 
151
      throw new ArcanistUsageException(
 
152
        "JSHint returned unparseable output.\n".
 
153
        "stdout:\n\n{$stdout}".
 
154
        "stderr:\n\n{$stderr}");
 
155
    }
 
156
 
 
157
    $messages = array();
 
158
    foreach ($errors as $err) {
 
159
      $message = new ArcanistLintMessage();
 
160
      $message->setPath($path);
 
161
      $message->setLine(idx($err, 'line'));
 
162
      $message->setChar(idx($err, 'col'));
 
163
      $message->setCode(idx($err, 'code'));
 
164
      $message->setName('JSHint'.idx($err, 'code'));
 
165
      $message->setDescription(idx($err, 'reason'));
 
166
      $message->setSeverity($this->getLintMessageSeverity(idx($err, 'code')));
 
167
 
 
168
      $messages[] = $message;
 
169
    }
 
170
 
 
171
    return $messages;
 
172
  }
 
173
 
 
174
  protected function getLintCodeFromLinterConfigurationKey($code) {
 
175
    if (!preg_match('/^(E|W)\d+$/', $code)) {
 
176
      throw new Exception(
 
177
        pht(
 
178
          'Unrecognized lint message code "%s". Expected a valid JSHint '.
 
179
          'lint code like "%s" or "%s".',
 
180
          $code,
 
181
          'E033',
 
182
          'W093'));
 
183
    }
 
184
 
 
185
    return $code;
 
186
  }
 
187
 
 
188
}