diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -156,6 +156,8 @@ 'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php', 'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php', 'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php', + 'ArcanistSCSSLintLinter' => 'lint/linter/ArcanistSCSSLintLinter.php', + 'ArcanistSCSSLintLinterTestCase' => 'lint/linter/__tests__/ArcanistSCSSLintLinterTestCase.php', 'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php', 'ArcanistSetConfigWorkflow' => 'workflow/ArcanistSetConfigWorkflow.php', 'ArcanistSettings' => 'configuration/ArcanistSettings.php', @@ -337,6 +339,8 @@ 'ArcanistRevertWorkflow' => 'ArcanistWorkflow', 'ArcanistRubyLinter' => 'ArcanistExternalLinter', 'ArcanistRubyLinterTestCase' => 'ArcanistArcanistLinterTestCase', + 'ArcanistSCSSLintLinter' => 'ArcanistExternalLinter', + 'ArcanistSCSSLintLinterTestCase' => 'ArcanistArcanistLinterTestCase', 'ArcanistScriptAndRegexLinter' => 'ArcanistLinter', 'ArcanistSetConfigWorkflow' => 'ArcanistWorkflow', 'ArcanistShellCompleteWorkflow' => 'ArcanistWorkflow', diff --git a/src/lint/linter/ArcanistSCSSLintLinter.php b/src/lint/linter/ArcanistSCSSLintLinter.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/ArcanistSCSSLintLinter.php @@ -0,0 +1,134 @@ +getExecutableCommand()); + + $matches = array(); + if (preg_match('/^scss-lint\s(?P\d+\.\d+\.\d+)$/', $stdout, + $matches)) { + return $matches['version']; + } else { + return false; + } + } + + public function getInstallInstructions() { + return pht('Install SCSS-Lint using `gem install scss-lint`.'); + } + + public function shouldExpectCommandErrors() { + return true; + } + + public function supportsReadDataFromStdin() { + return false; + } + + protected function getMandatoryFlags() { + $options = array( + '--format=JSON', + ); + + if ($this->config) { + $options[] = '--config='.$this->config; + } + + return $options; + } + + public function getLinterConfigurationOptions() { + $options = array( + 'scss-lint.config' => array( + 'type' => 'optional string', + 'help' => pht('A custom configuration file.'), + ), + ); + + return $options + parent::getLinterConfigurationOptions(); + } + + public function setLinterConfigurationValue($key, $value) { + switch ($key) { + case 'scss-lint.config': + $this->config = $value; + return; + } + + return parent::setLinterConfigurationValue($key, $value); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) { + $results = json_decode($stdout); + $messages = array(); + + var_dump($stdout); + var_dump($path); + var_dump($stderr); + var_dump($err); + + foreach ($results as $path => $offenses) { + foreach ($offenses as $offense) { + $message = new ArcanistLintMessage(); + + $message->setPath($path); + $message->setDescription($offense->reason); + + $message->setLine($offense->line); + $message->setChar($offense->column); + + $message->setSeverity($this->getArcSeverity($offense->severity)); + $message->setName($this->getLinterName()); + $message->setCode($offense->linter); + + $messages[] = $message; + } + } + + return $messages; + } + + private function getArcSeverity($severity) { + $arc_severity = ArcanistLintSeverity::SEVERITY_ADVICE; + + switch ($severity) { + case 'warning': + $arc_severity = ArcanistLintSeverity::SEVERITY_WARNING; + break; + + case 'error': + $arc_severity = ArcanistLintSeverity::SEVERITY_ERROR; + break; + } + + return $arc_severity; + } +} diff --git a/src/lint/linter/__tests__/ArcanistLinterTestCase.php b/src/lint/linter/__tests__/ArcanistLinterTestCase.php --- a/src/lint/linter/__tests__/ArcanistLinterTestCase.php +++ b/src/lint/linter/__tests__/ArcanistLinterTestCase.php @@ -4,11 +4,12 @@ * Facilitates implementation of test cases for @{class:ArcanistLinter}s. */ abstract class ArcanistLinterTestCase extends ArcanistPhutilTestCase { + protected $testFileSuffix = 'lint-test'; public function executeTestsInDirectory($root, ArcanistLinter $linter) { $files = id(new FileFinder($root)) ->withType('f') - ->withSuffix('lint-test') + ->withSuffix($this->testFileSuffix) ->find(); $test_count = 0; @@ -19,7 +20,8 @@ $this->assertTrue( ($test_count > 0), - pht('Expected to find some .lint-test tests in directory %s!', $root)); + pht('Expected to find some .%s tests in directory %s!', + $this->testFileSuffix, $root)); } private function lintFile($file, ArcanistLinter $linter) { diff --git a/src/lint/linter/__tests__/ArcanistSCSSLintLinterTestCase.php b/src/lint/linter/__tests__/ArcanistSCSSLintLinterTestCase.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ArcanistSCSSLintLinterTestCase.php @@ -0,0 +1,14 @@ +executeTestsInDirectory( + dirname(__FILE__).'/scss-lint/', + new ArcanistSCSSLintLinter()); + } + +} diff --git a/src/lint/linter/__tests__/scss-lint/no_errors.lint-test.scss b/src/lint/linter/__tests__/scss-lint/no_errors.lint-test.scss new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/scss-lint/no_errors.lint-test.scss @@ -0,0 +1,4 @@ +.module { + color: #fff; +} +~~~~~~~~~ diff --git a/src/lint/linter/__tests__/scss-lint/warning.lint-test.scss b/src/lint/linter/__tests__/scss-lint/warning.lint-test.scss new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/scss-lint/warning.lint-test.scss @@ -0,0 +1,5 @@ +.module { + border: none; +} +~~~~~~~~~ +warning:2:3