mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-12-23 05:50:54 +01:00
Add a JSON linter.
Summary: Provide bindings for [[https://github.com/zaach/jsonlint | JSONLint]], which is a useful tool for linting and validating JSON. Theoretically, this could be done with pure PHP, however it would not be trivial (`json_decode`, for example, does not provide any context as to JSON validation errors). Test Plan: Wrote and executed unit tests. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: epriestley, Korvin Differential Revision: https://secure.phabricator.com/D8988
This commit is contained in:
parent
85000be96f
commit
ab5c1562c0
36 changed files with 259 additions and 0 deletions
|
@ -84,6 +84,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistInstallCertificateWorkflow' => 'workflow/ArcanistInstallCertificateWorkflow.php',
|
||||
'ArcanistJSHintLinter' => 'lint/linter/ArcanistJSHintLinter.php',
|
||||
'ArcanistJSHintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSHintLinterTestCase.php',
|
||||
'ArcanistJSONLintLinter' => 'lint/linter/ArcanistJSONLintLinter.php',
|
||||
'ArcanistJSONLintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLintLinterTestCase.php',
|
||||
'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php',
|
||||
'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php',
|
||||
'ArcanistLintConsoleRenderer' => 'lint/renderer/ArcanistLintConsoleRenderer.php',
|
||||
|
@ -245,6 +247,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistInstallCertificateWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistJSHintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistJSHintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
|
||||
'ArcanistJSONLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistJSONLintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
|
||||
'ArcanistLandWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistLiberateWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistLintConsoleRenderer' => 'ArcanistLintRenderer',
|
||||
|
|
83
src/lint/linter/ArcanistJSONLintLinter.php
Normal file
83
src/lint/linter/ArcanistJSONLintLinter.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* A linter for JSON files.
|
||||
*/
|
||||
final class ArcanistJSONLintLinter extends ArcanistExternalLinter {
|
||||
public function getLinterName() {
|
||||
return 'JSON';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'jsonlint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'jsonlint';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
// NOTE: `jsonlint --version` returns a non-zero exit status.
|
||||
list($err, $stdout) = exec_manual(
|
||||
'%C --version',
|
||||
$this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/^(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
|
||||
$version = $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install jsonlint using `npm install -g jsonlint`.');
|
||||
}
|
||||
|
||||
public function shouldExpectCommandErrors() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function supportsReadDataFromStdin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
return array(
|
||||
'--compact',
|
||||
);
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stderr, false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$matches = null;
|
||||
$match = preg_match(
|
||||
'/^(?:(?<path>.+): )?' .
|
||||
'line (?<line>\d+), col (?<column>\d+), ' .
|
||||
'(?<description>.*)$/',
|
||||
$line,
|
||||
$matches);
|
||||
|
||||
if ($match) {
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches['line']);
|
||||
$message->setChar($matches['column']);
|
||||
$message->setCode($this->getLinterName());
|
||||
$message->setDescription(ucfirst($matches['description']));
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
if ($err && !$messages) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
}
|
12
src/lint/linter/__tests__/ArcanistJSONLintLinterTestCase.php
Normal file
12
src/lint/linter/__tests__/ArcanistJSONLintLinterTestCase.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistJSONLintLinterTestCase
|
||||
extends ArcanistArcanistLinterTestCase {
|
||||
|
||||
public function testJSONLintLinter() {
|
||||
$this->executeTestsInDirectory(
|
||||
dirname(__FILE__).'/jsonlint/',
|
||||
new ArcanistJSONLintLinter());
|
||||
}
|
||||
|
||||
}
|
59
src/lint/linter/__tests__/jsonlint/1.lint-test
Normal file
59
src/lint/linter/__tests__/jsonlint/1.lint-test
Normal file
|
@ -0,0 +1,59 @@
|
|||
[
|
||||
"JSON Test Pattern pass1",
|
||||
{"object with 1 member":["array with 1 element"]},
|
||||
{},
|
||||
[],
|
||||
-42,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
{
|
||||
"integer": 1234567890,
|
||||
"real": -9876.543210,
|
||||
"e": 0.123456789e-12,
|
||||
"E": 1.234567890E+34,
|
||||
"": 23456789012E66,
|
||||
"zero": 0,
|
||||
"one": 1,
|
||||
"space": " ",
|
||||
"quote": "\"",
|
||||
"backslash": "\\",
|
||||
"controls": "\b\f\n\r\t",
|
||||
"slash": "/ & \/",
|
||||
"alpha": "abcdefghijklmnopqrstuvwyz",
|
||||
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
|
||||
"digit": "0123456789",
|
||||
"0123456789": "digit",
|
||||
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
|
||||
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
|
||||
"true": true,
|
||||
"false": false,
|
||||
"null": null,
|
||||
"array":[ ],
|
||||
"object":{ },
|
||||
"address": "50 St. James Street",
|
||||
"url": "http://www.JSON.org/",
|
||||
"comment": "// /* <!-- --",
|
||||
"# -- --> */": " ",
|
||||
" s p a c e d " :[1,2 , 3
|
||||
|
||||
,
|
||||
|
||||
4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
|
||||
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
|
||||
"quotes": "" \u0022 %22 0x22 034 "",
|
||||
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
|
||||
: "A key can be any string"
|
||||
},
|
||||
0.5 ,98.6
|
||||
,
|
||||
99.44
|
||||
,
|
||||
|
||||
1066,
|
||||
1e1,
|
||||
0.1e1,
|
||||
1e-1,
|
||||
1e00,2e+00,2e-00
|
||||
,"rosebud"]
|
||||
~~~~~~~~~~
|
3
src/lint/linter/__tests__/jsonlint/10.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/10.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Extra value after close": true} "misplaced quoted value"
|
||||
~~~~~~~~~~
|
||||
error:1:33
|
3
src/lint/linter/__tests__/jsonlint/11.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/11.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Illegal expression": 1 + 2}
|
||||
~~~~~~~~~~
|
||||
error:1:24
|
3
src/lint/linter/__tests__/jsonlint/12.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/12.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Illegal invocation": alert()}
|
||||
~~~~~~~~~~
|
||||
error:1:22
|
3
src/lint/linter/__tests__/jsonlint/13.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/13.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Numbers cannot have leading zeroes": 013}
|
||||
~~~~~~~~~~
|
||||
error:1:38
|
3
src/lint/linter/__tests__/jsonlint/14.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/14.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Numbers cannot be hex": 0x14}
|
||||
~~~~~~~~~~
|
||||
error:1:25
|
3
src/lint/linter/__tests__/jsonlint/15.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/15.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["Illegal backslash escape: \x15"]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/16.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/16.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
[\naked]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/17.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/17.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["Illegal backslash escape: \017"]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/19.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/19.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Missing colon" null}
|
||||
~~~~~~~~~~
|
||||
error:1:16
|
2
src/lint/linter/__tests__/jsonlint/2.lint-test
Normal file
2
src/lint/linter/__tests__/jsonlint/2.lint-test
Normal file
|
@ -0,0 +1,2 @@
|
|||
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
|
||||
~~~~~~~~~~
|
3
src/lint/linter/__tests__/jsonlint/20.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/20.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Double colon":: null}
|
||||
~~~~~~~~~~
|
||||
error:1:16
|
3
src/lint/linter/__tests__/jsonlint/21.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/21.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Comma instead of colon", null}
|
||||
~~~~~~~~~~
|
||||
error:1:25
|
3
src/lint/linter/__tests__/jsonlint/22.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/22.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["Colon instead of comma": false]
|
||||
~~~~~~~~~~
|
||||
error:1:25
|
3
src/lint/linter/__tests__/jsonlint/23.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/23.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["Bad value", truth]
|
||||
~~~~~~~~~~
|
||||
error:1:13
|
3
src/lint/linter/__tests__/jsonlint/24.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/24.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
['single quote']
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/25.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/25.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
[" tab character in string "]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/26.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/26.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["tab\ character\ in\ string\ "]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
4
src/lint/linter/__tests__/jsonlint/27.lint-test
Normal file
4
src/lint/linter/__tests__/jsonlint/27.lint-test
Normal file
|
@ -0,0 +1,4 @@
|
|||
["line
|
||||
break"]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
4
src/lint/linter/__tests__/jsonlint/28.lint-test
Normal file
4
src/lint/linter/__tests__/jsonlint/28.lint-test
Normal file
|
@ -0,0 +1,4 @@
|
|||
["line\
|
||||
break"]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/29.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/29.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
[0e]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
7
src/lint/linter/__tests__/jsonlint/3.lint-test
Normal file
7
src/lint/linter/__tests__/jsonlint/3.lint-test
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"JSON Test Pattern pass3": {
|
||||
"The outermost value": "must be an object or array.",
|
||||
"In this test": "It is an object."
|
||||
}
|
||||
}
|
||||
~~~~~~~~~~
|
3
src/lint/linter/__tests__/jsonlint/30.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/30.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
[0e+]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/31.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/31.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
[0e+-1]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/32.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/32.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Comma instead if closing brace": true,
|
||||
~~~~~~~~~~
|
||||
error:1:40
|
3
src/lint/linter/__tests__/jsonlint/33.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/33.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["mismatch"}
|
||||
~~~~~~~~~~
|
||||
error:1:11
|
3
src/lint/linter/__tests__/jsonlint/34.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/34.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"extra brace": 1}}
|
||||
~~~~~~~~~~
|
||||
error:1:18
|
3
src/lint/linter/__tests__/jsonlint/4.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/4.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["extra comma",]
|
||||
~~~~~~~~~~
|
||||
error:1:15
|
3
src/lint/linter/__tests__/jsonlint/5.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/5.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["double extra comma",,]
|
||||
~~~~~~~~~~
|
||||
error:1:22
|
3
src/lint/linter/__tests__/jsonlint/6.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/6.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
[, "<-- missing value"]
|
||||
~~~~~~~~~~
|
||||
error:1:1
|
3
src/lint/linter/__tests__/jsonlint/7.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/7.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["Comma after the close"],
|
||||
~~~~~~~~~~
|
||||
error:1:25
|
3
src/lint/linter/__tests__/jsonlint/8.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/8.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
["Extra close"]]
|
||||
~~~~~~~~~~
|
||||
error:1:15
|
3
src/lint/linter/__tests__/jsonlint/9.lint-test
Normal file
3
src/lint/linter/__tests__/jsonlint/9.lint-test
Normal file
|
@ -0,0 +1,3 @@
|
|||
{"Extra comma": true,}
|
||||
~~~~~~~~~~
|
||||
error:1:21
|
Loading…
Reference in a new issue