44
55namespace CssLint ;
66
7- use Generator ;
87use RuntimeException ;
98use Throwable ;
9+ use CssLint \Formatter \FormatterInterface ;
10+ use CssLint \Formatter \FormatterFactory ;
11+ use Generator ;
1012
1113/**
1214 * @phpstan-import-type Errors from \CssLint\Linter
@@ -21,6 +23,10 @@ class Cli
2123
2224 private const RETURN_CODE_SUCCESS = 0 ;
2325
26+ private ?FormatterFactory $ formatterFactory = null ;
27+
28+ private FormatterInterface $ formatterManager ;
29+
2430 /**
2531 * Entrypoint of the cli, will execute the linter according to the given arguments
2632 * @param string[] $arguments arguments to be parsed (@see $_SERVER['argv'])
@@ -29,6 +35,15 @@ class Cli
2935 public function run (array $ arguments ): int
3036 {
3137 $ cliArgs = $ this ->parseArguments ($ arguments );
38+
39+ try {
40+ $ this ->formatterManager = $ this ->getFormatterFactory ()->create ($ cliArgs ->formatter );
41+ } catch (RuntimeException $ error ) {
42+ // report invalid formatter names via default (plain) formatter
43+ $ this ->getFormatterFactory ()->create (null )->printFatalError (null , $ error );
44+ return self ::RETURN_CODE_ERROR ;
45+ }
46+
3247 if ($ cliArgs ->input === null || $ cliArgs ->input === '' || $ cliArgs ->input === '0 ' ) {
3348 $ this ->printUsage ();
3449 return self ::RETURN_CODE_SUCCESS ;
@@ -41,7 +56,7 @@ public function run(array $arguments): int
4156
4257 return $ this ->lintInput ($ cssLinter , $ cliArgs ->input );
4358 } catch (Throwable $ throwable ) {
44- $ this ->printError ( $ throwable -> getMessage () );
59+ $ this ->formatterManager -> printFatalError ( null , $ throwable );
4560 return self ::RETURN_CODE_ERROR ;
4661 }
4762 }
@@ -51,10 +66,13 @@ public function run(array $arguments): int
5166 */
5267 private function printUsage (): void
5368 {
69+ $ availableFormatters = $ this ->getFormatterFactory ()->getAvailableFormatters ();
70+ $ defaultFormatter = $ availableFormatters [0 ];
71+
5472 $ this ->printLine ('Usage: ' . PHP_EOL .
5573 '------ ' . PHP_EOL .
5674 PHP_EOL .
57- ' ' . self ::SCRIPT_NAME . " [--options='{ }'] input_to_lint " . PHP_EOL .
75+ ' ' . self ::SCRIPT_NAME . " [--options='{ }'] [--formatter=plain|json] input_to_lint " . PHP_EOL .
5876 PHP_EOL .
5977 'Arguments: ' . PHP_EOL .
6078 '---------- ' . PHP_EOL .
@@ -68,6 +86,13 @@ private function printUsage(): void
6886 ' Example: --options= \'{ "constructors": {"o" : false}, "allowedIndentationChars": ["\t"] } \'' .
6987 PHP_EOL .
7088 PHP_EOL .
89+ ' --formatter ' . PHP_EOL .
90+ ' The formatter(s) to be used ' . PHP_EOL .
91+ ' If not specified, the first available formatter will be used. ' . PHP_EOL .
92+ ' Multiple formatters can be specified as a comma-separated list. ' . PHP_EOL .
93+ ' Available formatters: ' . implode (', ' , $ availableFormatters ) . PHP_EOL .
94+ ' Example: --formatter= ' . $ defaultFormatter . PHP_EOL .
95+ PHP_EOL .
7196 ' input_to_lint ' . PHP_EOL .
7297 ' The CSS file path (absolute or relative) ' . PHP_EOL .
7398 ' a glob pattern of file(s) to be linted ' . PHP_EOL .
@@ -100,6 +125,15 @@ private function parseArguments(array $arguments): CliArgs
100125 return new CliArgs ($ arguments );
101126 }
102127
128+ private function getFormatterFactory (): FormatterFactory
129+ {
130+ if ($ this ->formatterFactory === null ) {
131+ $ this ->formatterFactory = new FormatterFactory ();
132+ }
133+
134+ return $ this ->formatterFactory ;
135+ }
136+
103137 /**
104138 * Retrieve the properties from the given options
105139 * @param string $options the options to be parsed
@@ -207,7 +241,7 @@ private function lintGlob(string $glob): int
207241 $ cssLinter = new Linter ();
208242 $ files = glob ($ glob );
209243 if ($ files === [] || $ files === false ) {
210- $ this ->printError ( 'No files found for glob " ' . $ glob . ' " ' );
244+ $ this ->formatterManager -> printFatalError ( $ glob , 'No files found for given glob pattern ' );
211245 return self ::RETURN_CODE_ERROR ;
212246 }
213247
@@ -227,19 +261,17 @@ private function lintGlob(string $glob): int
227261 */
228262 private function lintFile (Linter $ cssLinter , string $ filePath ): int
229263 {
230- $ source = "CSS file \"" . $ filePath . "\"" ;
231- $ this ->printLine ('# Lint ' . $ source . '... ' );
232-
264+ $ source = "CSS file \"{$ filePath }\"" ;
265+ $ this ->formatterManager ->startLinting ($ source );
233266 if (!is_readable ($ filePath )) {
234- $ this ->printError ( ' File " ' . $ filePath . ' " is not readable ' );
267+ $ this ->formatterManager -> printFatalError ( $ source , ' File is not readable ' );
235268 return self ::RETURN_CODE_ERROR ;
236269 }
237270
238271 $ errors = $ cssLinter ->lintFile ($ filePath );
239272 return $ this ->printLinterErrors ($ source , $ errors );
240273 }
241274
242-
243275 /**
244276 * Performs lint on a given string
245277 * @param Linter $cssLinter the instance of the linter
@@ -249,43 +281,29 @@ private function lintFile(Linter $cssLinter, string $filePath): int
249281 private function lintString (Linter $ cssLinter , string $ stringValue ): int
250282 {
251283 $ source = 'CSS string ' ;
252- $ this ->printLine ( ' # Lint ' . $ source . ' ... ' );
284+ $ this ->formatterManager -> startLinting ( $ source );
253285 $ errors = $ cssLinter ->lintString ($ stringValue );
254286 return $ this ->printLinterErrors ($ source , $ errors );
255287 }
256288
257- /**
258- * Display an error message
259- * @param string $error the message to be displayed
260- */
261- private function printError (string $ error ): void
262- {
263- $ this ->printLine ("\033[31m/!\ Error: " . $ error . "\033[0m " . PHP_EOL );
264- }
265-
266289 /**
267290 * Display the errors returned by the linter
268291 * @param Generator<LintError> $errors the generated errors to be displayed
269292 * @return int the return code related to the execution of the linter
270293 */
271294 private function printLinterErrors (string $ source , Generator $ errors ): int
272295 {
273- $ hasErrors = false ;
296+ $ isValid = true ;
274297 foreach ($ errors as $ error ) {
275- if ($ hasErrors === false ) {
276- $ this ->printLine ("\033[31m => " . $ source . " is not valid: \033[0m " . PHP_EOL );
277- $ hasErrors = true ;
298+ if ($ isValid === true ) {
299+ $ isValid = false ;
278300 }
279- $ this ->printLine ( "\033 [31m - " . $ error . "\033 [0m " );
301+ $ this ->formatterManager -> printLintError ( $ source , $ error );
280302 }
281303
282- if ($ hasErrors ) {
283- $ this ->printLine ("" );
284- return self ::RETURN_CODE_ERROR ;
285- }
304+ $ this ->formatterManager ->endLinting ($ source , $ isValid );
286305
287- $ this ->printLine ("\033[32m => " . $ source . " is valid \033[0m " . PHP_EOL );
288- return self ::RETURN_CODE_SUCCESS ;
306+ return $ isValid ? self ::RETURN_CODE_SUCCESS : self ::RETURN_CODE_ERROR ;
289307 }
290308
291309 /**
0 commit comments