Skip to content

Commit 3dbb045

Browse files
committed
feat(output): allows outputs in stdout or file
Signed-off-by: Emilien Escalle <[email protected]>
1 parent cf8daa8 commit 3dbb045

25 files changed

+1130
-495
lines changed

docs/usage.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Result:
2727
Usage:
2828
------
2929
30-
php-css-lint [--options='{ }'] [--formatter=plain|json] input_to_lint
30+
php-css-lint [--options='{ }'] [--formatter=name] [--formatter=name:path] input_to_lint
3131
3232
Arguments:
3333
----------
@@ -41,11 +41,14 @@ Arguments:
4141
Example: --options='{ "constructors": {"o" : false}, "allowedIndentationChars": ["\t"] }'
4242
4343
--formatter
44-
The formatter(s) to be used
45-
If not specified, the first available formatter will be used.
46-
Multiple formatters can be specified as a comma-separated list.
47-
Available formatters: plain, json
48-
Example: --formatter=plain
44+
The formatter(s) to be used. Can be specified multiple times.
45+
Format: --formatter=name (output to stdout) or --formatter=name:path (output to file)
46+
If not specified, the default formatter will output to stdout.
47+
Available formatters: plain, gitlab-ci, github-actions
48+
Examples:
49+
output to stdout: --formatter=plain
50+
output to file: --formatter=plain:report.txt
51+
multiple outputs: --formatter=plain --formatter=gitlab-ci:report.json
4952
5053
input_to_lint
5154
The CSS file path (absolute or relative)
@@ -67,6 +70,15 @@ Examples:
6770
6871
Lint with only tabulation as indentation:
6972
php-css-lint --options='{ "allowedIndentationChars": ["\t"] }' ".test { color: red; }"
73+
74+
Output to a file:
75+
php-css-lint --formatter=plain:output.txt ".test { color: red; }"
76+
77+
Generate GitLab CI report:
78+
php-css-lint --formatter=gitlab-ci:report.json "./path/to/css_file.css"
79+
80+
Multiple outputs (console and file):
81+
php-css-lint --formatter=plain --formatter=gitlab-ci:ci-report.json ".test { color: red; }"
7082
```
7183

7284
### Lint a file

src/CssLint/Cli.php

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
use RuntimeException;
88
use Throwable;
9-
use CssLint\Formatter\FormatterInterface;
10-
use CssLint\Formatter\FormatterFactory;
9+
use CssLint\Output\Formatter\FormatterFactory;
10+
use CssLint\Output\Formatter\FormatterManager;
1111
use Generator;
1212

1313
/**
@@ -25,7 +25,7 @@ class Cli
2525

2626
private ?FormatterFactory $formatterFactory = null;
2727

28-
private FormatterInterface $formatterManager;
28+
private FormatterManager $formatterManager;
2929

3030
/**
3131
* Entrypoint of the cli, will execute the linter according to the given arguments
@@ -37,10 +37,10 @@ public function run(array $arguments): int
3737
$cliArgs = $this->parseArguments($arguments);
3838

3939
try {
40-
$this->formatterManager = $this->getFormatterFactory()->create($cliArgs->formatter);
40+
$this->formatterManager = $this->getFormatterFactory()->create($cliArgs->formatters);
4141
} catch (RuntimeException $error) {
4242
// report invalid formatter names via default (plain) formatter
43-
$this->getFormatterFactory()->create(null)->printFatalError(null, $error);
43+
$this->getFormatterFactory()->create()->printFatalError(null, $error);
4444
return self::RETURN_CODE_ERROR;
4545
}
4646

@@ -69,50 +69,60 @@ private function printUsage(): void
6969
$availableFormatters = $this->getFormatterFactory()->getAvailableFormatters();
7070
$defaultFormatter = $availableFormatters[0];
7171

72-
$this->printLine('Usage:' . PHP_EOL .
73-
'------' . PHP_EOL .
74-
PHP_EOL .
75-
' ' . self::SCRIPT_NAME . " [--options='{ }'] [--formatter=plain|json] input_to_lint" . PHP_EOL .
76-
PHP_EOL .
77-
'Arguments:' . PHP_EOL .
78-
'----------' . PHP_EOL .
79-
PHP_EOL .
80-
' --options' . PHP_EOL .
81-
' Options (optional), must be a json object:' . PHP_EOL .
82-
' * "allowedIndentationChars" => [" "] or ["\t"]: will override the current property' . PHP_EOL .
83-
' * "constructors": { "property" => bool }: will merge with the current property' . PHP_EOL .
84-
' * "standards": { "property" => bool }: will merge with the current property' . PHP_EOL .
85-
' * "nonStandards": { "property" => bool }: will merge with the current property' . PHP_EOL .
86-
' Example: --options=\'{ "constructors": {"o" : false}, "allowedIndentationChars": ["\t"] }\'' .
87-
PHP_EOL .
88-
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 .
96-
' input_to_lint' . PHP_EOL .
97-
' The CSS file path (absolute or relative)' . PHP_EOL .
98-
' a glob pattern of file(s) to be linted' . PHP_EOL .
99-
' or a CSS string to be linted' . PHP_EOL .
100-
' Example:' . PHP_EOL .
101-
' "./path/to/css_file_path_to_lint.css"' . PHP_EOL .
102-
' "./path/to/css_file_path_to_lint/*.css"' . PHP_EOL .
103-
' ".test { color: red; }"' . PHP_EOL .
104-
PHP_EOL .
105-
'Examples:' . PHP_EOL .
106-
'---------' . PHP_EOL .
107-
PHP_EOL .
108-
' Lint a CSS file:' . PHP_EOL .
109-
' ' . self::SCRIPT_NAME . ' "./path/to/css_file_path_to_lint.css"' . PHP_EOL . PHP_EOL .
110-
' Lint a CSS string:' . PHP_EOL .
111-
' ' . self::SCRIPT_NAME . ' ".test { color: red; }"' . PHP_EOL . PHP_EOL .
112-
' Lint with only tabulation as indentation:' . PHP_EOL .
113-
' ' . self::SCRIPT_NAME .
114-
' --options=\'{ "allowedIndentationChars": ["\t"] }\' ".test { color: red; }"' . PHP_EOL .
115-
PHP_EOL . PHP_EOL);
72+
$this->printLine(
73+
'Usage:' . PHP_EOL .
74+
'------' . PHP_EOL .
75+
PHP_EOL .
76+
' ' . self::SCRIPT_NAME . " [--options='{ }'] [--formatter=name] [--formatter=name:path] input_to_lint" . PHP_EOL .
77+
PHP_EOL .
78+
'Arguments:' . PHP_EOL .
79+
'----------' . PHP_EOL .
80+
PHP_EOL .
81+
' --options' . PHP_EOL .
82+
' Options (optional), must be a json object:' . PHP_EOL .
83+
' * "allowedIndentationChars" => [" "] or ["\t"]: will override the current property' . PHP_EOL .
84+
' * "constructors": { "property" => bool }: will merge with the current property' . PHP_EOL .
85+
' * "standards": { "property" => bool }: will merge with the current property' . PHP_EOL .
86+
' * "nonStandards": { "property" => bool }: will merge with the current property' . PHP_EOL .
87+
' Example: --options=\'{ "constructors": {"o" : false}, "allowedIndentationChars": ["\t"] }\'' .
88+
PHP_EOL .
89+
PHP_EOL .
90+
' --formatter' . PHP_EOL .
91+
' The formatter(s) to be used. Can be specified multiple times.' . PHP_EOL .
92+
' Format: --formatter=name (output to stdout) or --formatter=name:path (output to file)' . PHP_EOL .
93+
' If not specified, the default formatter will output to stdout.' . PHP_EOL .
94+
' Available formatters: ' . implode(', ', $availableFormatters) . PHP_EOL .
95+
' Examples:' . PHP_EOL .
96+
' output to stdout: --formatter=' . $defaultFormatter . PHP_EOL .
97+
' output to file: --formatter=' . $defaultFormatter . ':report.txt' . PHP_EOL .
98+
' multiple outputs: --formatter=' . $defaultFormatter . ' --formatter=' . $availableFormatters[1] . ':report.json' . PHP_EOL .
99+
PHP_EOL .
100+
' input_to_lint' . PHP_EOL .
101+
' The CSS file path (absolute or relative)' . PHP_EOL .
102+
' a glob pattern of file(s) to be linted' . PHP_EOL .
103+
' or a CSS string to be linted' . PHP_EOL .
104+
' Example:' . PHP_EOL .
105+
' "./path/to/css_file_path_to_lint.css"' . PHP_EOL .
106+
' "./path/to/css_file_path_to_lint/*.css"' . PHP_EOL .
107+
' ".test { color: red; }"' . PHP_EOL .
108+
PHP_EOL .
109+
'Examples:' . PHP_EOL .
110+
'---------' . PHP_EOL .
111+
PHP_EOL .
112+
' Lint a CSS file:' . PHP_EOL .
113+
' ' . self::SCRIPT_NAME . ' "./path/to/css_file_path_to_lint.css"' . PHP_EOL . PHP_EOL .
114+
' Lint a CSS string:' . PHP_EOL .
115+
' ' . self::SCRIPT_NAME . ' ".test { color: red; }"' . PHP_EOL . PHP_EOL .
116+
' Lint with only tabulation as indentation:' . PHP_EOL .
117+
' ' . self::SCRIPT_NAME .
118+
' --options=\'{ "allowedIndentationChars": ["\t"] }\' ".test { color: red; }"' . PHP_EOL . PHP_EOL .
119+
' Output to a file:' . PHP_EOL .
120+
' ' . self::SCRIPT_NAME . ' --formatter=plain:output.txt ".test { color: red; }"' . PHP_EOL . PHP_EOL .
121+
' Generate GitLab CI report:' . PHP_EOL .
122+
' ' . self::SCRIPT_NAME . ' --formatter=gitlab-ci:report.json "./path/to/css_file.css"' . PHP_EOL . PHP_EOL .
123+
' Multiple outputs (console and file):' . PHP_EOL .
124+
' ' . self::SCRIPT_NAME . ' --formatter=plain --formatter=gitlab-ci:ci-report.json ".test { color: red; }"' . PHP_EOL . PHP_EOL
125+
);
116126
}
117127

118128
/**

src/CssLint/CliArgs.php

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
/**
88
* @package CssLint
99
* @phpstan-type Arguments string[]
10-
* @phpstan-type ParsedArguments array<string, string>
1110
*/
1211
class CliArgs
1312
{
@@ -16,9 +15,11 @@ class CliArgs
1615
public ?string $options = null;
1716

1817
/**
19-
* Output formatter type
18+
* Array of formatter specifications with their output destinations
19+
* Format: ['plain' => null, 'gitlab-ci' => '/path/to/report.json']
20+
* @var array<string, string|null>
2021
*/
21-
public ?string $formatter = null;
22+
public array $formatters = [];
2223

2324
/**
2425
* Constructor
@@ -37,39 +38,49 @@ public function __construct(array $arguments)
3738
$this->input = array_pop($arguments);
3839

3940
if ($arguments !== []) {
40-
$parsedArguments = $this->parseArguments($arguments);
41-
42-
if (!empty($parsedArguments['options'])) {
43-
$this->options = $parsedArguments['options'];
44-
}
45-
if (!empty($parsedArguments['formatter'])) {
46-
$this->formatter = $parsedArguments['formatter'];
47-
}
41+
$this->parseArguments($arguments);
4842
}
4943
}
5044

5145
/**
5246
* @param Arguments $arguments array of arguments to be parsed (@see $_SERVER['argv'])
53-
* @return ParsedArguments an associative array of key=>value arguments
5447
*/
55-
private function parseArguments(array $arguments): array
48+
private function parseArguments(array $arguments): void
5649
{
57-
$aParsedArguments = [];
58-
5950
foreach ($arguments as $argument) {
6051
// --foo --bar=baz
6152
if (str_starts_with((string) $argument, '--')) {
6253
$equalPosition = strpos((string) $argument, '=');
6354

64-
// --bar=baz
6555
if ($equalPosition !== false) {
6656
$key = substr((string) $argument, 2, $equalPosition - 2);
6757
$value = substr((string) $argument, $equalPosition + 1);
68-
$aParsedArguments[$key] = $value;
58+
59+
if ($key === 'options') {
60+
$this->options = $value;
61+
} elseif ($key === 'formatter') {
62+
$this->parseFormatterSpec($value);
63+
}
6964
}
7065
}
7166
}
67+
}
7268

73-
return $aParsedArguments;
69+
/**
70+
* Parse a formatter specification like "plain" or "gitlab-ci:/path/to/file.json"
71+
*/
72+
private function parseFormatterSpec(string $formatterSpec): void
73+
{
74+
$colonPosition = strpos($formatterSpec, ':');
75+
76+
if ($colonPosition !== false) {
77+
// Format: formatter:path
78+
$formatterName = substr($formatterSpec, 0, $colonPosition);
79+
$outputPath = substr($formatterSpec, $colonPosition + 1);
80+
$this->formatters[$formatterName] = $outputPath;
81+
} else {
82+
// Format: formatter (stdout only)
83+
$this->formatters[$formatterSpec] = null;
84+
}
7485
}
7586
}

src/CssLint/Formatter/FormatterFactory.php

Lines changed: 0 additions & 78 deletions
This file was deleted.

0 commit comments

Comments
 (0)