diff --git a/composer.json b/composer.json index 2678f24..ac4bfd9 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ ], "require": { "php": ">=8.4", + "boundwize/jsonrecast": "^0.2.1", "composer/semver": "^3.4", "entropy/entropy": "^0.4", "nette/utils": "^4.1", diff --git a/src/FileSystem/ComposerJsonPackageVersionUpdater.php b/src/FileSystem/ComposerJsonPackageVersionUpdater.php index f66dd9b..6017a21 100644 --- a/src/FileSystem/ComposerJsonPackageVersionUpdater.php +++ b/src/FileSystem/ComposerJsonPackageVersionUpdater.php @@ -4,32 +4,46 @@ namespace Rector\Jack\FileSystem; -use Nette\Utils\Strings; +use Boundwize\JsonRecast\JsonRecast; +use Boundwize\JsonRecast\Node\NodeJson; +use Boundwize\JsonRecast\Node\StringNode; +use Boundwize\JsonRecast\NodePath\NodeJsonPath; +use Boundwize\JsonRecast\NodeVisitor\NodeJsonVisitorAbstract; final class ComposerJsonPackageVersionUpdater { public static function update(string $composerJsonContents, string $packageName, string $newVersion): string { - // replace using regex, to keep original composer.json format - $allChanges = Strings::replace( - $composerJsonContents, - // find - sprintf('#"%s": "(.*?)"#', $packageName), - // replace - sprintf('"%s": "%s"', $packageName, $newVersion) - ); - - $skippedKeys = ['suggest', 'replace', 'provide', 'conflict']; - - foreach ($skippedKeys as $skippedKey) { - $regexKeyContent = sprintf('#"%s"\s*:\s*{[^}]*}#', $skippedKey); - $skippedContent = Strings::match($composerJsonContents, $regexKeyContent); - - if ($skippedContent !== null) { - $allChanges = Strings::replace($allChanges, $regexKeyContent, $skippedContent[0]); + $jsonDocument = JsonRecast::parse($composerJsonContents); + + $jsonRecastResult = JsonRecast::traverse( + $jsonDocument, + new class($packageName, $newVersion) extends NodeJsonVisitorAbstract { + public function __construct( + private readonly string $packageName, + private readonly string $newVersion, + ) { + } + + public function enterNode(NodeJson $nodeJson, NodeJsonPath $nodeJsonPath): ?NodeJson + { + if (! $nodeJson instanceof StringNode) { + return null; + } + + foreach (['require', 'require-dev'] as $composerPackageSection) { + if (! $nodeJsonPath->matches([$composerPackageSection, $this->packageName])) { + continue; + } + + return new StringNode($this->newVersion); + } + + return null; + } } - } + ); - return $allChanges; + return JsonRecast::print($jsonRecastResult); } }