Skip to content

Commit 86071bd

Browse files
committed
[TASK] Attribute CodeFormatRule to reformatted composer.json files
- Re-print composer.json on every run so a formatting-only change (indentation) is reported instead of applied silently; the runner attributes it to the virtual CodeFormatRule - Keep a real rule attributed to itself; JSON has no comments, so the re-print round-trip preserves content - Cover the behaviour with a ComposerJsonProcessResult test (rule change, pure reformat, and an already-formatted no-op)
1 parent f01474e commit 86071bd

6 files changed

Lines changed: 103 additions & 6 deletions

File tree

packages/fractor-composer-json/src/ComposerJsonFileProcessor.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,28 @@ public function canHandle(File $file): bool
3636

3737
public function handle(File $file, iterable $appliedRules): void
3838
{
39-
$fileHasChanged = \false;
39+
$rawContent = $file->getContent();
4040
$composerJson = $this->composerJsonFactory->createFromFile($file);
41-
$oldComposerJson = $this->composerJsonFactory->createFromFile($file);
4241

4342
foreach ($appliedRules as $rule) {
43+
$beforeArray = $composerJson->getJsonArray();
4444
$rule->refactor($composerJson);
4545

46-
if ($oldComposerJson->getJsonArray() !== $composerJson->getJsonArray()) {
47-
$file->changeFileContent($this->composerJsonPrinter->printToString($this->indent, $composerJson));
46+
if ($beforeArray !== $composerJson->getJsonArray()) {
4847
$file->addAppliedRule(AppliedRule::fromRule($rule));
49-
$fileHasChanged = \true;
5048
}
5149
}
5250

53-
if (! $fileHasChanged) {
51+
// Always re-print: a formatting-only change (indentation) is then
52+
// attributed to CodeFormatRule by the runner rather than applied silently.
53+
$newContent = rtrim($this->composerJsonPrinter->printToString($this->indent, $composerJson)) . "\n";
54+
55+
if ($newContent === $rawContent) {
5456
$this->changedFilesDetector->addCachableFile($file->getFilePath());
57+
return;
5558
}
59+
60+
$file->changeFileContent($newContent);
5661
}
5762

5863
public function allowedFileExtensions(): array
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace a9f\FractorComposerJson\Tests\ComposerJsonProcessResult;
6+
7+
use a9f\Fractor\Application\ValueObject\AppliedRule;
8+
use a9f\Fractor\Testing\PHPUnit\AbstractFractorTestCase;
9+
use a9f\FractorComposerJson\RemovePackageComposerJsonFractor;
10+
use PHPUnit\Framework\Attributes\DataProvider;
11+
12+
/**
13+
* The composer.json processor re-prints files it touches, so a formatting-only
14+
* change (indentation) must be attributed to the virtual CodeFormatRule — so a
15+
* re-indentation is reported rather than applied silently — while a real
16+
* transformation stays attributed to its own rule.
17+
*/
18+
final class ComposerJsonProcessResultTest extends AbstractFractorTestCase
19+
{
20+
/**
21+
* @param list<string> $expectedAppliedRules
22+
*/
23+
#[DataProvider('provideData')]
24+
public function test(string $fixture, array $expectedAppliedRules): void
25+
{
26+
$fractorTestResult = $this->doTestFile(__DIR__ . '/Fixtures/' . $fixture);
27+
28+
self::assertSame($expectedAppliedRules, $fractorTestResult->getAppliedFractorRules());
29+
}
30+
31+
/**
32+
* @return \Iterator<string, array{string, list<string>}>
33+
*/
34+
public static function provideData(): \Iterator
35+
{
36+
yield 'rule change is attributed to the real rule' => [
37+
'rule-change/composer.json.fixture',
38+
[RemovePackageComposerJsonFractor::class],
39+
];
40+
yield 'pure reformat is attributed to the virtual code-format rule' => [
41+
'reformatting/composer.json.fixture',
42+
[AppliedRule::CODE_FORMAT_RULE],
43+
];
44+
yield 'already formatted file reports no change' => ['already-formatted/composer.json.fixture', []];
45+
}
46+
47+
public function provideConfigFilePath(): string
48+
{
49+
return __DIR__ . '/config/fractor.php';
50+
}
51+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "acme/demo",
3+
"require": {
4+
"vendor1/keep": "^2.0"
5+
}
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "acme/demo",
3+
"require": {
4+
"vendor1/keep": "^2.0"
5+
}
6+
}
7+
-----
8+
{
9+
"name": "acme/demo",
10+
"require": {
11+
"vendor1/keep": "^2.0"
12+
}
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"require": {
3+
"vendor1/legacy": "^1.0",
4+
"vendor1/keep": "^2.0"
5+
}
6+
}
7+
-----
8+
{
9+
"require": {
10+
"vendor1/keep": "^2.0"
11+
}
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use a9f\Fractor\Configuration\FractorConfiguration;
6+
use a9f\FractorComposerJson\RemovePackageComposerJsonFractor;
7+
8+
return FractorConfiguration::configure()
9+
->import(__DIR__ . '/../../../config/application.php')
10+
->withConfiguredRule(RemovePackageComposerJsonFractor::class, ['vendor1/legacy']);

0 commit comments

Comments
 (0)