diff --git a/CHANGELOG.md b/CHANGELOG.md
index db26e187..5b11e679 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## Unreleased
-For a full diff see [`0.14.1...main`][0.14.1...main].
+For a full diff see [`1.0.0...main`][1.0.0...main].
+
+## [`1.0.0`][1.0.0]
+
+For a full diff see [`0.14.1...1.0.0`][0.14.1...1.0.0].
+
+### Changed
+
+* Adjusted `Vendor\Composer\ConfigHashNormalizer` to recursively sort hashes by key ([#424]), by [@localheinz]
## [`0.14.1`][0.14.1]
@@ -284,6 +292,7 @@ For a full diff see [`5d8b3e2...0.1.0`][5d8b3e2...0.1.0].
[0.13.1]: https://github.com/ergebnis/json-normalizer/releases/tag/0.13.1
[0.14.0]: https://github.com/ergebnis/json-normalizer/releases/tag/0.14.0
[0.14.1]: https://github.com/ergebnis/json-normalizer/releases/tag/0.14.1
+[1.0.0]: https://github.com/ergebnis/json-normalizer/releases/tag/1.0.0
[5d8b3e2...0.1.0]: https://github.com/ergebnis/json-normalizer/compare/5d8b3e2...0.1.0
[0.1.0...0.2.0]: https://github.com/ergebnis/json-normalizer/compare/0.1.0...0.2.0
@@ -304,7 +313,8 @@ For a full diff see [`5d8b3e2...0.1.0`][5d8b3e2...0.1.0].
[0.13.0...0.13.1]: https://github.com/ergebnis/json-normalizer/compare/0.13.0...0.13.1
[0.13.1...0.14.0]: https://github.com/ergebnis/json-normalizer/compare/0.13.1...0.14.0
[0.14.0...0.14.1]: https://github.com/ergebnis/json-normalizer/compare/0.14.0...0.14.1
-[0.14.1...main]: https://github.com/ergebnis/json-normalizer/compare/0.14.1...main
+[0.14.1...1.0.0]: https://github.com/ergebnis/json-normalizer/compare/0.14.1...1.0.0
+[1.0.0...main]: https://github.com/ergebnis/json-normalizer/compare/1.0.0...main
[#1]: https://github.com/ergebnis/json-normalizer/pull/1
[#2]: https://github.com/ergebnis/json-normalizer/pull/2
@@ -367,6 +377,7 @@ For a full diff see [`5d8b3e2...0.1.0`][5d8b3e2...0.1.0].
[#335]: https://github.com/ergebnis/json-normalizer/pull/335
[#384]: https://github.com/ergebnis/json-normalizer/pull/384
[#423]: https://github.com/ergebnis/json-normalizer/pull/423
+[#424]: https://github.com/ergebnis/json-normalizer/pull/424
[@BackEndTea]: https://github.com/BackEndTea
[@ergebnis]: https://github.com/ergebnis
diff --git a/README.md b/README.md
index ec9e724c..bd5448bb 100644
--- a/README.md
+++ b/README.md
@@ -389,7 +389,7 @@ When `composer.json` contains any configuration in the
* `extra`
* `scripts-descriptions`
-sections, the `Vendor\Composer\ConfigHashNormalizer` will sort the content of these sections by key in ascending order.
+sections, the `Vendor\Composer\ConfigHashNormalizer` will sort the content of these sections by key in ascending order. If a value is an object, it will continue to sort its properties by name.
:bulb: Find out more about the `config` section at [Composer: The composer.json schema](https://getcomposer.org/doc/06-config.md).
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index f5b1447d..aab96b98 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -60,12 +60,13 @@
+
+ $value
+ $value
+
$value
-
- $value
-
diff --git a/src/Vendor/Composer/ConfigHashNormalizer.php b/src/Vendor/Composer/ConfigHashNormalizer.php
index b559aaf0..85c0bdc9 100644
--- a/src/Vendor/Composer/ConfigHashNormalizer.php
+++ b/src/Vendor/Composer/ConfigHashNormalizer.php
@@ -48,15 +48,7 @@ public function normalize(Json $json): Json
}
foreach ($objectProperties as $name => $value) {
- $config = (array) $decoded->{$name};
-
- if (0 === \count($config)) {
- continue;
- }
-
- \ksort($config);
-
- $decoded->{$name} = $config;
+ $decoded->{$name} = self::sortByKey($value);
}
/** @var string $encoded */
@@ -64,4 +56,28 @@ public function normalize(Json $json): Json
return Json::fromEncoded($encoded);
}
+
+ /**
+ * @param null|array|bool|false|\stdClass|string $value
+ *
+ * @return null|array|bool|false|\stdClass|string
+ */
+ private static function sortByKey($value)
+ {
+ if (!\is_object($value)) {
+ return $value;
+ }
+
+ $sorted = (array) $value;
+
+ if ([] === $sorted) {
+ return $value;
+ }
+
+ \ksort($sorted);
+
+ return \array_map(static function ($value) {
+ return self::sortByKey($value);
+ }, $sorted);
+ }
}
diff --git a/test/Unit/Vendor/Composer/ConfigHashNormalizerTest.php b/test/Unit/Vendor/Composer/ConfigHashNormalizerTest.php
index ef2994e4..9cf9c0d2 100644
--- a/test/Unit/Vendor/Composer/ConfigHashNormalizerTest.php
+++ b/test/Unit/Vendor/Composer/ConfigHashNormalizerTest.php
@@ -140,6 +140,62 @@ public function testNormalizeSortsConfigHashIfPropertyExists(string $property):
self::assertJsonStringEqualsJsonStringNormalized($expected->encoded(), $normalized->encoded());
}
+ /**
+ * @dataProvider provideProperty
+ */
+ public function testNormalizeSortsConfigHashRecursivelyIfPropertyExists(string $property): void
+ {
+ $json = Json::fromEncoded(
+ <<normalize($json);
+
+ self::assertJsonStringEqualsJsonStringNormalized($expected->encoded(), $normalized->encoded());
+ }
+
/**
* @return \Generator>
*/