1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-10-24 17:48:50 +02:00
phorge-arcanist/src/lint/linter/ArcanistJSHintLinter.php

162 lines
4.5 KiB
PHP
Raw Normal View History

<?php
/**
* Uses "JSHint" to detect errors and potential problems in JavaScript code.
* To use this linter, you must install jshint through NPM (Node Package
* Manager). You can configure different JSHint options on a per-file basis.
*
* If you have NodeJS installed you should be able to install jshint with
* ##npm install jshint -g## (don't forget the -g flag or NPM will install
* the package locally). If your system is unusual, you can manually specify
* the location of jshint and its dependencies by configuring these keys in
* your .arcconfig:
*
* lint.jshint.prefix
* lint.jshint.bin
*
* If you want to configure custom options for your project, create a JSON
* file with these options and add the path to the file to your .arcconfig
* by configuring this key:
*
* lint.jshint.config
*
* Example JSON file (config.json):
*
* {
* "predef": [ // Custom globals
* "myGlobalVariable",
* "anotherGlobalVariable"
* ],
*
* "es5": true, // Allow ES5 syntax
* "strict": true // Require strict mode
* }
*
* For more options see http://www.jshint.com/options/.
*
* @group linter
*/
final class ArcanistJSHintLinter extends ArcanistLinter {
const JSHINT_ERROR = 1;
public function getLinterName() {
return 'JSHint';
}
public function getLintSeverityMap() {
return array(
self::JSHINT_ERROR => ArcanistLintSeverity::SEVERITY_ERROR
);
}
public function getLintNameMap() {
return array(
self::JSHINT_ERROR => "JSHint Error"
);
}
public function getJSHintOptions() {
$working_copy = $this->getEngine()->getWorkingCopy();
$options = '--reporter '.dirname(realpath(__FILE__)).'/reporter.js';
$config = $working_copy->getConfig('lint.jshint.config');
if ($config !== null) {
$config = Filesystem::resolvePath(
$config,
$working_copy->getProjectRoot());
if (!Filesystem::pathExists($config)) {
throw new ArcanistUsageException(
"Unable to find custom options file defined by ".
"'lint.jshint.config'. Make sure that the path is correct.");
}
$options .= ' --config '.$config;
}
return $options;
}
private function getJSHintPath() {
$working_copy = $this->getEngine()->getWorkingCopy();
$prefix = $working_copy->getConfig('lint.jshint.prefix');
$bin = $working_copy->getConfig('lint.jshint.bin');
if ($bin === null) {
$bin = "jshint";
}
if ($prefix !== null) {
$bin = $prefix."/".$bin;
if (!Filesystem::pathExists($bin)) {
throw new ArcanistUsageException(
"Unable to find JSHint binary in a specified directory. Make sure ".
"that 'lint.jshint.prefix' and 'lint.jshint.bin' keys are set ".
"correctly. If you'd rather use a copy of JSHint installed ".
"globally, you can just remove these keys from your .arcconfig");
}
return $bin;
}
// Look for globally installed JSHint
$cmd = (phutil_is_windows()) ? 'where %s' : 'which %s';
list($err) = exec_manual($cmd, $bin);
if ($err) {
throw new ArcanistUsageException(
"JSHint does not appear to be installed on this system. Install it ".
"(e.g., with 'npm install jshint -g') or configure ".
"'lint.jshint.prefix' in your .arcconfig to point to the directory ".
"where it resides.");
}
return $bin;
}
public function willLintPaths(array $paths) {
$jshint_bin = $this->getJSHintPath();
$jshint_options = $this->getJSHintOptions();
$futures = array();
foreach ($paths as $path) {
$filepath = $this->getEngine()->getFilePathOnDisk($path);
$futures[$path] = new ExecFuture(
"%s %s %C",
$jshint_bin,
$filepath,
$jshint_options);
}
foreach (Futures($futures)->limit(8) as $path => $future) {
$this->results[$path] = $future->resolve();
}
}
public function lintPath($path) {
Fix ArcanistSingleLintEngine for deleted paths Summary: Currently, ArcanisSingleLintEngine lints deleted paths and directories. These are sometimes appropriate, but SingleLintEngine is a less-sophisticated linter and should have more safe defaults. Also fix an error where JSHint reported useless messages on failure. Test Plan: Reproduced the problem: $ git show commit d71efe2b13770c8861bcd3415c15503fc377339f Author: epriestley <git@epriestley.com> Date: Fri Oct 19 12:22:50 2012 -0700 WIP diff --git a/test.js b/test.js deleted file mode 100644 index 8bd6648..0000000 --- a/test.js +++ /dev/null @@ -1 +0,0 @@ -asdf $ arc set-config --local lint.engine.single.linter ArcanistJSHintLinter Set key 'lint.engine.single.linter' = "ArcanistJSHintLinter" in local config (was null). $ arc lint --engine ArcanistSingleLintEngine --rev HEAD^ Usage Exception: JSHint returned output we can't parse. Check that your JSHint installation. Output: Applied the error message fix: $ arc lint --engine ArcanistSingleLintEngine --rev HEAD^ Usage Exception: JSHint returned unparseable output. stdout: stderr: node.js:181 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: ENOENT, No such file or directory '/INSECURE/repos/git-working-copy/test.js' at Object.statSync (fs.js:400:18) at _collect (/usr/lib/node_modules/jshint/lib/hint.js:77:12) at /usr/lib/node_modules/jshint/lib/hint.js:93:13 at Array.forEach (native) at Object.hint (/usr/lib/node_modules/jshint/lib/hint.js:92:17) at Object.interpret (/usr/lib/node_modules/jshint/lib/cli.js:137:21) at Object.<anonymous> (/usr/lib/node_modules/jshint/bin/hint:2:25) at Module._compile (module.js:420:26) at Object..js (module.js:426:10) at Module.load (module.js:336:31) Applied the remove paths fix: $ arc lint --engine ArcanistSingleLintEngine --rev HEAD^ Usage Exception: No paths are lintable. Reviewers: magazovski, btrahan, vrana Reviewed By: btrahan CC: aran, Korvin, vissi Differential Revision: https://secure.phabricator.com/D3735
2012-10-20 15:12:24 +02:00
list($rc, $stdout, $stderr) = $this->results[$path];
if ($rc === 0) {
return;
}
$errors = json_decode($stdout);
if (!is_array($errors)) {
// Something went wrong and we can't decode the output. Exit abnormally.
throw new ArcanistUsageException(
Fix ArcanistSingleLintEngine for deleted paths Summary: Currently, ArcanisSingleLintEngine lints deleted paths and directories. These are sometimes appropriate, but SingleLintEngine is a less-sophisticated linter and should have more safe defaults. Also fix an error where JSHint reported useless messages on failure. Test Plan: Reproduced the problem: $ git show commit d71efe2b13770c8861bcd3415c15503fc377339f Author: epriestley <git@epriestley.com> Date: Fri Oct 19 12:22:50 2012 -0700 WIP diff --git a/test.js b/test.js deleted file mode 100644 index 8bd6648..0000000 --- a/test.js +++ /dev/null @@ -1 +0,0 @@ -asdf $ arc set-config --local lint.engine.single.linter ArcanistJSHintLinter Set key 'lint.engine.single.linter' = "ArcanistJSHintLinter" in local config (was null). $ arc lint --engine ArcanistSingleLintEngine --rev HEAD^ Usage Exception: JSHint returned output we can't parse. Check that your JSHint installation. Output: Applied the error message fix: $ arc lint --engine ArcanistSingleLintEngine --rev HEAD^ Usage Exception: JSHint returned unparseable output. stdout: stderr: node.js:181 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: ENOENT, No such file or directory '/INSECURE/repos/git-working-copy/test.js' at Object.statSync (fs.js:400:18) at _collect (/usr/lib/node_modules/jshint/lib/hint.js:77:12) at /usr/lib/node_modules/jshint/lib/hint.js:93:13 at Array.forEach (native) at Object.hint (/usr/lib/node_modules/jshint/lib/hint.js:92:17) at Object.interpret (/usr/lib/node_modules/jshint/lib/cli.js:137:21) at Object.<anonymous> (/usr/lib/node_modules/jshint/bin/hint:2:25) at Module._compile (module.js:420:26) at Object..js (module.js:426:10) at Module.load (module.js:336:31) Applied the remove paths fix: $ arc lint --engine ArcanistSingleLintEngine --rev HEAD^ Usage Exception: No paths are lintable. Reviewers: magazovski, btrahan, vrana Reviewed By: btrahan CC: aran, Korvin, vissi Differential Revision: https://secure.phabricator.com/D3735
2012-10-20 15:12:24 +02:00
"JSHint returned unparseable output.\n".
"stdout:\n\n{$stdout}".
"stderr:\n\n{$stderr}");
}
foreach ($errors as $err) {
$this->raiseLintAtLine(
$err->line,
$err->col,
self::JSHINT_ERROR,
$err->reason);
}
}
}