Skip to content

Commit 2761c45

Browse files
committed
feat: add css at-rule lint
Signed-off-by: Emilien Escalle <[email protected]>
1 parent d4e3010 commit 2761c45

24 files changed

+666
-421
lines changed

.github/workflows/update-css-referential.yml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,8 @@ jobs:
2121
with:
2222
php-version: 8.3
2323

24-
- name: Run CSS Referential Scraper
25-
run: php bin/css-referential-scraper
26-
27-
- name: Run CSS Referential Generator
28-
run: php bin/css-referential-generator
29-
30-
- name: Run PHP CS Fixer
31-
run: composer php-cs-fixer:fix
24+
- name: Generate CSS Referential
25+
run: composer run-script generate-css-referential
3226

3327
- name: Push changes and create pull request
3428
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,18 @@ test-load-fixtures: ## Execute tests and load fixtures for given PHP version
3434
@$(call run-php,composer test:load-fixtures $(filter-out $@,$(MAKECMDGOALS)))
3535

3636
lint: ## Execute lint for given PHP version
37-
@$(call run-php,composer php-cs-fixer $(filter-out $@,$(MAKECMDGOALS)))
37+
$(MAKE) php-cs-fixer $(filter-out $@,$(MAKECMDGOALS))
38+
$(MAKE) rector $(filter-out $@,$(MAKECMDGOALS))
39+
$(MAKE) phpstan $(filter-out $@,$(MAKECMDGOALS))
3840

3941
lint-fix: ## Execute lint fixing for given PHP version
42+
$(MAKE) php-cs-fixer-fix $(filter-out $@,$(MAKECMDGOALS))
43+
$(MAKE) rector-fix $(filter-out $@,$(MAKECMDGOALS))
44+
45+
php-cs-fixer: ## Execute php-cs-fixer for given PHP version
46+
@$(call run-php,composer php-cs-fixer $(filter-out $@,$(MAKECMDGOALS)))
47+
48+
php-cs-fixer-fix: ## Execute php-cs-fixer fixing for given PHP version
4049
@$(call run-php,composer php-cs-fixer:fix $(filter-out $@,$(MAKECMDGOALS)))
4150

4251
rector: ## Execute rector for given PHP version
@@ -51,6 +60,9 @@ phpstan: ## Execute PHPStan for given PHP version
5160
ci: ## Execute CI scripts for given PHP version
5261
@$(call run-php,composer ci $(filter-out $@,$(MAKECMDGOALS)))
5362

63+
generate-css-referentials: ## Generate referentials for given PHP version
64+
@$(call run-php,composer generate-css-referentials $(filter-out $@,$(MAKECMDGOALS)))
65+
5466
## Run PHP for given version
5567
define run-php
5668
@docker run -it --rm -v ${PWD}:${PWD} -w ${PWD} "${IMAGE}" $(1)

composer.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,22 @@
5252
],
5353
"scripts": {
5454
"test": "phpunit --colors --configuration tests/phpunit.xml",
55-
"test:load-fixtures": "php scripts/css-referential-scraper && php scripts/css-popular-downloader",
55+
"test:load-fixtures": [
56+
"php scripts/css-referential-scraper",
57+
"php scripts/css-popular-downloader"
58+
],
5659
"test:ci": "@test -d pcov.enabled=1 -d max_execution_time=0 --coverage-text --coverage-clover ./build/logs/clover.xml --coverage-html ./build/coverage/",
5760
"php-cs-fixer": "@php-cs-fixer:fix --dry-run",
5861
"php-cs-fixer:fix": "tools/vendor/bin/php-cs-fixer fix --show-progress=dots --diff --config=.php-cs-fixer.dist.php",
5962
"rector": "@rector:fix --dry-run",
6063
"rector:fix": "tools/vendor/bin/rector process src",
6164
"phpstan": "tools/vendor/bin/phpstan analyse --level max src",
65+
"generate-css-referentials": [
66+
"php scripts/css-referential-scraper",
67+
"php scripts/css-referential-generator",
68+
"@php-cs-fixer:fix src/CssLint/Referential",
69+
"tools/vendor/bin/rector process src/CssLint/Referential"
70+
],
6271
"ci": [
6372
"@php-cs-fixer",
6473
"@rector",

scripts/css-referential-generator

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
require __DIR__ . '/../vendor/autoload.php';
55

6-
function saveDataset(string $className, array $referential): void
6+
function saveReferentialData(string $className, array $referential): void
77
{
88
$datasetClass = new ReflectionClass($className);
99
$datasetFile = $datasetClass->getFileName();
@@ -27,13 +27,12 @@ $cssProperties = json_decode(file_get_contents($cssPropertiesFile), true);
2727

2828
$standardsProperties = [];
2929
$nonStandardsProperties = [];
30-
31-
foreach ($cssProperties as $propertyName => $property) {
30+
foreach ($cssProperties as $atRuleName => $property) {
3231
$isStandard = $property['standard'] ?? false;
3332
if ($isStandard) {
34-
$standardsProperties[$propertyName] = true;
33+
$standardsProperties[$atRuleName] = true;
3534
} else {
36-
$nonStandardsProperties[$propertyName] = true;
35+
$nonStandardsProperties[$atRuleName] = true;
3736
}
3837
}
3938

@@ -43,14 +42,45 @@ $missingNonStandardsProperties = [
4342
'-webkit-margin-end',
4443
'-moz-osx-font-smoothing',
4544
];
46-
foreach ($missingNonStandardsProperties as $propertyName) {
47-
if (isset($standardsProperties[$propertyName]) || isset($nonStandardsProperties[$propertyName])) {
48-
throw new Exception("Property $propertyName already exists in either standards or non-standards properties.");
45+
foreach ($missingNonStandardsProperties as $atRuleName) {
46+
if (isset($standardsProperties[$atRuleName]) || isset($nonStandardsProperties[$atRuleName])) {
47+
throw new Exception("Property $atRuleName already exists in either standards or non-standards properties.");
4948
}
50-
$nonStandardsProperties[$propertyName] = true;
49+
$nonStandardsProperties[$atRuleName] = true;
5150
}
5251

5352
ksort($standardsProperties);
53+
saveReferentialData(CssLint\Referential\Standard\PropertiesReferential::class, $standardsProperties);
5454
ksort($nonStandardsProperties);
55-
saveDataset(CssLint\Referential\StandardPropertiesReferential::class, $standardsProperties);
56-
saveDataset(CssLint\Referential\NonStandardPropertiesReferential::class, $nonStandardsProperties);
55+
saveReferentialData(CssLint\Referential\NonStandard\PropertiesReferential::class, $nonStandardsProperties);
56+
57+
$cssAtRulesFile = __DIR__ . '/../tests/fixtures/css-at-rules.json';
58+
$cssAtRules = json_decode(file_get_contents($cssAtRulesFile), true);
59+
$standardsAtRules = [];
60+
$nonStandardsAtRules = [];
61+
62+
foreach ($cssAtRules as $atRuleName => $atRule) {
63+
$isStandard = $atRule['standard'] ?? false;
64+
if ($isStandard) {
65+
$standardsAtRules[$atRuleName] = true;
66+
} else {
67+
$nonStandardsAtRules[$atRuleName] = true;
68+
}
69+
}
70+
71+
// Add missing non-standard at-rules
72+
$missingNonStandardsAtRules = [
73+
'theme',
74+
'tailwind'
75+
];
76+
foreach ($missingNonStandardsAtRules as $atRuleName) {
77+
if (isset($standardsAtRules[$atRuleName]) || isset($nonStandardsAtRules[$atRuleName])) {
78+
throw new Exception("At-rules $atRuleName already exists in either standards or non-standards at-rules.");
79+
}
80+
$nonStandardsAtRules[$atRuleName] = true;
81+
}
82+
83+
ksort($standardsAtRules);
84+
saveReferentialData(CssLint\Referential\Standard\AtRulesReferential::class, $standardsAtRules);
85+
ksort($nonStandardsAtRules);
86+
saveReferentialData(CssLint\Referential\NonStandard\AtRulesReferential::class, $nonStandardsAtRules);

scripts/css-referential-scraper

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ $forceRefresh = in_array('--force-refresh', $argv);
99

1010
$scraper = new CssReferentialScraper($forceRefresh);
1111

12-
echo "Fetching CSS property list...\n";
12+
echo "Fetching CSS referentials...\n";
1313
$referentials = $scraper->fetchReferentials();
1414

15-
echo "Fetched " . count($referentials['properties']) . " properties\n";
15+
echo "Fetched " . count($referentials['properties']) . " propertie(s)\n";
1616
$cssPropertiesFile = __DIR__ . '/../tests/fixtures/css-properties.json';
1717
$scraper->saveToJson($referentials['properties'], $cssPropertiesFile);
1818
echo "Saved to {$cssPropertiesFile}\n";
1919

20+
echo "Fetched " . count($referentials['at-rules']) . " at-rule(s)\n";
21+
$cssAtRulesFile = __DIR__ . '/../tests/fixtures/css-at-rules.json';
22+
$scraper->saveToJson($referentials['at-rules'], $cssAtRulesFile);
23+
echo "Saved to {$cssAtRulesFile}\n";
24+
2025
echo "Done.\n";

src/CssLint/CharLinter/CommentCharLinter.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
class CommentCharLinter implements CharLinter
1010
{
11-
private static $COMMENT_DELIMITER = '/';
11+
private static string $COMMENT_DELIMITER = '/';
1212

1313
/**
1414
* Performs lint for a given char, check comment part
@@ -21,6 +21,7 @@ public function lintChar(string $charValue, LintContext $lintContext): ?bool
2121
if ($this->isCommentEnd($charValue, $lintContext)) {
2222
$lintContext->setComment(false);
2323
}
24+
2425
$lintContext->setPreviousChar($charValue);
2526
return true;
2627
}
@@ -42,8 +43,6 @@ public function lintChar(string $charValue, LintContext $lintContext): ?bool
4243

4344
/**
4445
* Check if the current char is a comment
45-
* @param string $charValue
46-
* @return bool
4746
*/
4847
private function isCommentDelimiter(string $charValue): bool
4948
{
@@ -52,8 +51,6 @@ private function isCommentDelimiter(string $charValue): bool
5251

5352
/**
5453
* Check if the current char is the end of a comment
55-
* @param string $charValue
56-
* @return bool
5754
*/
5855
private function isCommentEnd(string $charValue, LintContext $lintContext): bool
5956
{
@@ -62,8 +59,6 @@ private function isCommentEnd(string $charValue, LintContext $lintContext): bool
6259

6360
/**
6461
* Check if the current char is the start of a comment
65-
* @param string $charValue
66-
* @return bool
6762
*/
6863
private function isCommentStart(string $charValue, LintContext $lintContext): bool
6964
{

src/CssLint/CharLinter/EndOfLineCharLinter.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ public function lintChar(string $charValue, LintContext $lintContext): ?bool
2121
->incrementLineNumber()
2222
->resetCharNumber();
2323
}
24+
2425
return true;
2526
}
27+
2628
return null;
2729
}
2830

src/CssLint/CharLinter/ImportCharLinter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
class ImportCharLinter implements CharLinter
1111
{
12-
private static $IMPORT_RULE = '@import';
12+
private static string $IMPORT_RULE = '@import';
1313

1414
/**
1515
* Performs lint for a given char, check @import rules

src/CssLint/CharLinter/PropertyCharLinter.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66

77
use CssLint\LintContext;
88
use CssLint\LintContextName;
9-
use CssLint\Properties;
9+
use CssLint\LintConfiguration;
1010

1111
class PropertyCharLinter implements CharLinter
1212
{
1313
public function __construct(
14-
protected readonly Properties $cssLintProperties,
14+
protected readonly LintConfiguration $lintConfiguration,
1515
) {}
1616

1717
/**
@@ -30,6 +30,7 @@ public function lintChar(string $charValue, LintContext $lintContext): ?bool
3030

3131
return null;
3232
}
33+
3334
/**
3435
* Performs lint for a given char, check property name part
3536
* @return bool|null : true if the process should continue, else false, null if this char is not a property name
@@ -50,7 +51,7 @@ protected function lintPropertyNameChar(string $charValue, LintContext $lintCont
5051
}
5152

5253
// Check if property name exists
53-
if (!$this->cssLintProperties->propertyExists($propertyName)) {
54+
if (!$this->lintConfiguration->propertyExists($propertyName)) {
5455
$lintContext->addError('Unknown CSS property "' . $propertyName . '"');
5556
}
5657

@@ -84,14 +85,18 @@ protected function lintPropertyContentChar(string $charValue, LintContext $lintC
8485
$lintContext->appendCurrentContent($charValue);
8586

8687
// End of the property content
87-
if ($charValue === ';') {
88-
// Check if the ";" is not quoted
88+
if ($charValue === ';' || $charValue === '}') {
89+
// Check if the char is not quoted
8990
$contextContent = $lintContext->getCurrentContent();
9091
if ((substr_count($contextContent, '"') & 1) === 0 && (substr_count($contextContent, "'") & 1) === 0) {
9192
$lintContext->setCurrentContext(LintContextName::CONTEXT_SELECTOR_CONTENT);
9293
}
9394

9495
if (trim($contextContent) !== '' && trim($contextContent) !== '0') {
96+
if ($charValue === '}') {
97+
$lintContext->resetCurrentContext();
98+
}
99+
95100
return true;
96101
}
97102

0 commit comments

Comments
 (0)