From a2f4219e4cfdc1bc7c457257f327bc102e7175b6 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 16:31:53 +0200 Subject: [PATCH 001/118] Add additional fixture for test --- packages/framework/tests/Feature/DataCollectionTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/framework/tests/Feature/DataCollectionTest.php b/packages/framework/tests/Feature/DataCollectionTest.php index df35e788f3f..9f5479f4dee 100644 --- a/packages/framework/tests/Feature/DataCollectionTest.php +++ b/packages/framework/tests/Feature/DataCollectionTest.php @@ -34,11 +34,13 @@ public function testYamlCollections() { $this->directory('resources/collections/foo'); $this->markdown('resources/collections/foo/foo.yaml', matter: ['title' => 'Foo']); - $this->file('resources/collections/foo/bar.yml'); + $this->file('resources/collections/foo/bar.yml', "---\ntitle: Bar\n---"); + $this->file('resources/collections/foo/baz.yml'); $this->assertEquals(new DataCollections([ 'foo/foo.yaml' => new FrontMatter(['title' => 'Foo']), - 'foo/bar.yml' => new FrontMatter([]), + 'foo/bar.yml' => new FrontMatter(['title' => 'Bar']), + 'foo/baz.yml' => new FrontMatter([]), ]), DataCollections::yaml('foo')); } From 71e2212aed530c421680da93f40b05ba344a68a9 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 16:48:34 +0200 Subject: [PATCH 002/118] Test YAML collections without triple dashes --- .../framework/tests/Feature/DataCollectionTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/framework/tests/Feature/DataCollectionTest.php b/packages/framework/tests/Feature/DataCollectionTest.php index 9f5479f4dee..bea53f0ef6d 100644 --- a/packages/framework/tests/Feature/DataCollectionTest.php +++ b/packages/framework/tests/Feature/DataCollectionTest.php @@ -44,6 +44,16 @@ public function testYamlCollections() ]), DataCollections::yaml('foo')); } + public function testYamlCollectionsWithoutTripleDashes() + { + $this->directory('resources/collections/foo'); + $this->file('resources/collections/foo/foo.yml', 'title: Foo'); + + $this->assertEquals(new DataCollections([ + 'foo/foo.yml' => new FrontMatter(['title' => 'Foo']), + ]), DataCollections::yaml('foo')); + } + public function testJsonCollections() { $this->directory('resources/collections/foo'); From ff896418527acb5b8695fb3abc17f766873bb8ed Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 17:25:04 +0200 Subject: [PATCH 003/118] Support parsing Yaml data files with both dashes and without --- .../framework/src/Support/DataCollections.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollections.php b/packages/framework/src/Support/DataCollections.php index b5ab786edef..f683801a5ec 100644 --- a/packages/framework/src/Support/DataCollections.php +++ b/packages/framework/src/Support/DataCollections.php @@ -5,6 +5,9 @@ namespace Hyde\Support; use Hyde\Facades\Filesystem; +use Symfony\Component\Yaml\Yaml; +use Hyde\Markdown\Models\FrontMatter; +use Spatie\YamlFrontMatter\YamlFrontMatter; use Hyde\Framework\Actions\MarkdownFileParser; use Hyde\Framework\Concerns\InteractsWithDirectories; use Illuminate\Support\Collection; @@ -68,7 +71,17 @@ public static function yaml(string $name): static static::needsDirectory(static::$sourceDirectory); return new static(static::findFiles($name, ['yaml', 'yml'])->mapWithKeys(function (string $file): array { - return [static::makeIdentifier($file) => MarkdownFileParser::parse($file)->matter()]; + $content = Filesystem::get($file); + + if (str_starts_with($content, '---')) { + $document = YamlFrontMatter::markdownCompatibleParse($content); + $matter = new FrontMatter($document->matter()); + } else { + $parsed = Yaml::parse($content) ?: []; + $matter = new FrontMatter($parsed); + } + + return [static::makeIdentifier($file) => $matter]; })); } From 30b6365da3cf921a875856e751e0d87520d21611 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 17:33:49 +0200 Subject: [PATCH 004/118] Yaml data files no longer need to start with triple dashes --- docs/digging-deeper/collections.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/digging-deeper/collections.md b/docs/digging-deeper/collections.md index f0ebb7b79fb..f0224e85f23 100644 --- a/docs/digging-deeper/collections.md +++ b/docs/digging-deeper/collections.md @@ -129,14 +129,10 @@ Here is an approximation of the data types contained by the variable created abo Assuming the Yaml document looks like this: ```yaml ---- name: "John Doe" email: "john@example.org" ``` ->warning Note that the Yaml file should start with `---` to be parsed correctly. - - ## Json Collections ### Usage From 5b2af72411c40b926a831bc49f19147fef9bcf3f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 17:37:17 +0200 Subject: [PATCH 005/118] Simplify Yaml data file parsing --- packages/framework/src/Support/DataCollections.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/framework/src/Support/DataCollections.php b/packages/framework/src/Support/DataCollections.php index f683801a5ec..ceba103e305 100644 --- a/packages/framework/src/Support/DataCollections.php +++ b/packages/framework/src/Support/DataCollections.php @@ -7,7 +7,6 @@ use Hyde\Facades\Filesystem; use Symfony\Component\Yaml\Yaml; use Hyde\Markdown\Models\FrontMatter; -use Spatie\YamlFrontMatter\YamlFrontMatter; use Hyde\Framework\Actions\MarkdownFileParser; use Hyde\Framework\Concerns\InteractsWithDirectories; use Illuminate\Support\Collection; @@ -74,13 +73,12 @@ public static function yaml(string $name): static $content = Filesystem::get($file); if (str_starts_with($content, '---')) { - $document = YamlFrontMatter::markdownCompatibleParse($content); - $matter = new FrontMatter($document->matter()); - } else { - $parsed = Yaml::parse($content) ?: []; - $matter = new FrontMatter($parsed); + $content = Str::between($content, '---', '---'); } + $parsed = Yaml::parse($content) ?: []; + $matter = new FrontMatter($parsed); + return [static::makeIdentifier($file) => $matter]; })); } From e4e68086d619f9d541d562dca37a22ef1c8b1b00 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 18:53:25 +0200 Subject: [PATCH 006/118] Update RELEASE_NOTES.md --- RELEASE_NOTES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a099118b402..1671f6fceb9 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -173,6 +173,7 @@ Due to the scope of the rewrite, the easiest and fastest way to upgrade your cod - For a full comparison of the changes, you may see the PR that introduced the new API: https://github.com/hydephp/develop/pull/1568/files - For information on how to use the new Navigation API, see the documentation: https://hydephp.com/docs/2.x/navigation-api +- If you use DataCollections, you should read the upgrade path below as there are breaking changes to the DataCollection API. ### HTML ID changes @@ -216,3 +217,9 @@ For example, if you triggered the media transfer with a build service method cal (new TransferMediaAssets())->run(); ``` + +### DataCollection API changes + +The DataCollections feature has been reworked to improve the developer experience and make it more consistent with the rest of the API. + +Unfortunately, this means that existing setups may need to be adjusted to work with the new API. From 5d655cb4a60d96c501a88d6d40f1d0715c0c8ef4 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 18:53:34 +0200 Subject: [PATCH 007/118] Update RELEASE_NOTES.md --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1671f6fceb9..a23fc612755 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -223,3 +223,7 @@ For example, if you triggered the media transfer with a build service method cal The DataCollections feature has been reworked to improve the developer experience and make it more consistent with the rest of the API. Unfortunately, this means that existing setups may need to be adjusted to work with the new API. + +#### Issues that may arise + +If you get a Yaml `ParseException` stating that "Multiple documents are not supported in", you must remove the `---` separator YAML documents in your data files. From daa7a3a5cd4a481de098eb556ebdd39a6bc7dce7 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 18:53:37 +0200 Subject: [PATCH 008/118] Revert "Update RELEASE_NOTES.md" This reverts commit 5d655cb4a60d96c501a88d6d40f1d0715c0c8ef4. --- RELEASE_NOTES.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a23fc612755..1671f6fceb9 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -223,7 +223,3 @@ For example, if you triggered the media transfer with a build service method cal The DataCollections feature has been reworked to improve the developer experience and make it more consistent with the rest of the API. Unfortunately, this means that existing setups may need to be adjusted to work with the new API. - -#### Issues that may arise - -If you get a Yaml `ParseException` stating that "Multiple documents are not supported in", you must remove the `---` separator YAML documents in your data files. From 6ebc9c9626128940cb581c65aa112693a0e13c9e Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 20:54:02 +0200 Subject: [PATCH 009/118] Unwrap unnecessary condition --- packages/framework/src/Support/DataCollections.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/framework/src/Support/DataCollections.php b/packages/framework/src/Support/DataCollections.php index ceba103e305..79e53dc32d5 100644 --- a/packages/framework/src/Support/DataCollections.php +++ b/packages/framework/src/Support/DataCollections.php @@ -71,10 +71,7 @@ public static function yaml(string $name): static return new static(static::findFiles($name, ['yaml', 'yml'])->mapWithKeys(function (string $file): array { $content = Filesystem::get($file); - - if (str_starts_with($content, '---')) { - $content = Str::between($content, '---', '---'); - } + $content = Str::between($content, '---', '---'); $parsed = Yaml::parse($content) ?: []; $matter = new FrontMatter($parsed); From 22c4b667a36c6312d89cacd72147ee7c4ea702cb Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 21:22:00 +0200 Subject: [PATCH 010/118] No longer automatically create the data collections directory Fixes https://github.com/hydephp/develop/issues/1735 --- RELEASE_NOTES.md | 4 ++- docs/digging-deeper/collections.md | 1 - .../framework/src/Support/DataCollections.php | 9 ------ .../tests/Feature/DataCollectionTest.php | 28 ------------------- 4 files changed, 3 insertions(+), 39 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7bea4c45323..e4c43f306b0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -25,7 +25,7 @@ This serves two purposes: - Changed how the documentation search is generated, to be an `InMemoryPage` instead of a post-build task. - Media asset files are now copied using the new build task instead of the deprecated `BuildService::transferMediaAssets()` method. - Calling the `Include::path()` method will no longer create the includes directory in https://github.com/hydephp/develop/pull/1707 - +- Calling the `DataCollections` methods will no longer create the data collections directory in https://github.com/hydephp/develop/pull/1732 ### Deprecated - for soon-to-be removed features. @@ -223,3 +223,5 @@ For example, if you triggered the media transfer with a build service method cal The DataCollections feature has been reworked to improve the developer experience and make it more consistent with the rest of the API. Unfortunately, this means that existing setups may need to be adjusted to work with the new API. + +- Calling the `DataCollections` methods will no longer create the data collections directory automatically diff --git a/docs/digging-deeper/collections.md b/docs/digging-deeper/collections.md index 89745bd0c57..b83bef6647f 100644 --- a/docs/digging-deeper/collections.md +++ b/docs/digging-deeper/collections.md @@ -32,7 +32,6 @@ Follow these conventions and creating dynamic static sites will be a breeze. 6. While not enforced, each subdirectory should probably only have the same filetype to prevent developer confusion. 7. Unlike source files for pages, files starting with underscores are not ignored. 8. You can customize the source directory for collections through a service provider. -9. If the base source directory does not exist, it will be created for you. ## Available Collection Types diff --git a/packages/framework/src/Support/DataCollections.php b/packages/framework/src/Support/DataCollections.php index 79e53dc32d5..00d54371089 100644 --- a/packages/framework/src/Support/DataCollections.php +++ b/packages/framework/src/Support/DataCollections.php @@ -8,7 +8,6 @@ use Symfony\Component\Yaml\Yaml; use Hyde\Markdown\Models\FrontMatter; use Hyde\Framework\Actions\MarkdownFileParser; -use Hyde\Framework\Concerns\InteractsWithDirectories; use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -35,8 +34,6 @@ */ class DataCollections extends Collection { - use InteractsWithDirectories; - /** * The base directory for all data collections. Can be modified using a service provider. */ @@ -51,8 +48,6 @@ class DataCollections extends Collection */ public static function markdown(string $name): static { - static::needsDirectory(static::$sourceDirectory); - return new static(static::findFiles($name, 'md')->mapWithKeys(function (string $file): array { return [static::makeIdentifier($file) => MarkdownFileParser::parse($file)]; })); @@ -67,8 +62,6 @@ public static function markdown(string $name): static */ public static function yaml(string $name): static { - static::needsDirectory(static::$sourceDirectory); - return new static(static::findFiles($name, ['yaml', 'yml'])->mapWithKeys(function (string $file): array { $content = Filesystem::get($file); $content = Str::between($content, '---', '---'); @@ -89,8 +82,6 @@ public static function yaml(string $name): static */ public static function json(string $name, bool $asArray = false): static { - static::needsDirectory(static::$sourceDirectory); - return new static(static::findFiles($name, 'json')->mapWithKeys(function (string $file) use ($asArray): array { return [static::makeIdentifier($file) => json_decode(Filesystem::get($file), $asArray)]; })); diff --git a/packages/framework/tests/Feature/DataCollectionTest.php b/packages/framework/tests/Feature/DataCollectionTest.php index bea53f0ef6d..f144e9fd367 100644 --- a/packages/framework/tests/Feature/DataCollectionTest.php +++ b/packages/framework/tests/Feature/DataCollectionTest.php @@ -4,12 +4,10 @@ namespace Hyde\Framework\Testing\Feature; -use Hyde\Hyde; use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; use Hyde\Support\DataCollections; use Hyde\Testing\TestCase; -use Illuminate\Support\Facades\File; /** * @covers \Hyde\Support\DataCollections @@ -170,30 +168,4 @@ public function testSourceDirectoryCanBeChanged() DataCollections::$sourceDirectory = 'resources/collections'; } - - public function testSourceDirectoryIsAutomaticallyAddedIfMissing() - { - $this->directory('resources/collections'); - File::deleteDirectory(Hyde::path('resources/collections')); - $this->assertDirectoryDoesNotExist(Hyde::path('resources/collections')); - - DataCollections::markdown('foo'); - - $this->assertDirectoryExists(Hyde::path('resources/collections')); - } - - public function testCustomSourceDirectoryIsAutomaticallyAddedIfMissing() - { - $this->directory('foo'); - File::deleteDirectory(Hyde::path('foo')); - - $this->assertDirectoryDoesNotExist(Hyde::path('foo')); - - DataCollections::$sourceDirectory = 'foo'; - DataCollections::markdown('bar'); - - $this->assertDirectoryExists(Hyde::path('foo')); - - DataCollections::$sourceDirectory = 'resources/collections'; - } } From 4245ba5f08eae765225a634d5362bfe08559884d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 23 Jun 2024 21:54:11 +0200 Subject: [PATCH 011/118] Breaking: Rename class `DataCollections` to `DataCollection` See https://github.com/hydephp/develop/issues/1712 --- RELEASE_NOTES.md | 13 +++-- _ide_helper.php | 2 +- app/config.php | 2 +- docs/digging-deeper/collections.md | 32 ++++++------ docs/digging-deeper/helpers.md | 10 ++-- ...DataCollections.php => DataCollection.php} | 8 +-- .../tests/Feature/DataCollectionTest.php | 50 +++++++++---------- .../tests/Unit/DataCollectionUnitTest.php | 22 ++++---- 8 files changed, 73 insertions(+), 66 deletions(-) rename packages/framework/src/Support/{DataCollections.php => DataCollection.php} (93%) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e4c43f306b0..53fc95b9911 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -17,6 +17,7 @@ This serves two purposes: ### Changed - **Breaking:** The internals of the navigation system has been rewritten into a new Navigation API. This change is breaking for custom navigation implementations. For more information, see below. - **Breaking:** The `hyde.features` configuration format has changed to use Enums instead of static method calls. For more information, see below. +- **Breaking:** Renamed class `DataCollections` to `DataCollection`. For more information, see below. - Minor: Navigation menu items are now no longer filtered by duplicates (meaning two items with the same label can now exist in the same menu) in https://github.com/hydephp/develop/pull/1573 - Minor: Due to changes in the navigation system, it is possible that existing configuration files will need to be adjusted in order for menus to look the same (in terms of ordering etc.) - Minor: The documentation article component now supports disabling the semantic rendering using a falsy value in https://github.com/hydephp/develop/pull/1566 @@ -25,7 +26,7 @@ This serves two purposes: - Changed how the documentation search is generated, to be an `InMemoryPage` instead of a post-build task. - Media asset files are now copied using the new build task instead of the deprecated `BuildService::transferMediaAssets()` method. - Calling the `Include::path()` method will no longer create the includes directory in https://github.com/hydephp/develop/pull/1707 -- Calling the `DataCollections` methods will no longer create the data collections directory in https://github.com/hydephp/develop/pull/1732 +- Calling the `DataCollection` methods will no longer create the data collections directory in https://github.com/hydephp/develop/pull/1732 ### Deprecated - for soon-to-be removed features. @@ -220,8 +221,14 @@ For example, if you triggered the media transfer with a build service method cal ### DataCollection API changes -The DataCollections feature has been reworked to improve the developer experience and make it more consistent with the rest of the API. +The DataCollection feature has been reworked to improve the developer experience and make it more consistent with the rest of the API. Unfortunately, this means that existing setups may need to be adjusted to work with the new API. -- Calling the `DataCollections` methods will no longer create the data collections directory automatically +#### Upgrade guide + +- The `DataCollections` class has been renamed to `DataCollection`. If you have used the `DataCollections` class in your code, you will need to update your code to use the new class name. + +#### Minor impact + +- Calling the `DataCollection` methods will no longer create the data collections directory automatically diff --git a/_ide_helper.php b/_ide_helper.php index 8cc231f850f..228893b3169 100644 --- a/_ide_helper.php +++ b/_ide_helper.php @@ -40,7 +40,7 @@ class Features extends \Hyde\Facades\Features {} class Config extends \Hyde\Facades\Config {} /** @mixin \Illuminate\Filesystem\Filesystem */ class Filesystem extends \Hyde\Facades\Filesystem {} -class DataCollections extends \Hyde\Support\DataCollections {} +class DataCollection extends \Hyde\Support\DataCollection {} class Includes extends \Hyde\Support\Includes {} /** @mixin \Hyde\Foundation\Kernel\RouteCollection */ class Routes extends \Hyde\Foundation\Facades\Routes {} diff --git a/app/config.php b/app/config.php index 22206637aff..ab5db6907b0 100644 --- a/app/config.php +++ b/app/config.php @@ -103,7 +103,7 @@ 'MarkdownPage' => \Hyde\Pages\MarkdownPage::class, 'MarkdownPost' => \Hyde\Pages\MarkdownPost::class, 'DocumentationPage' => \Hyde\Pages\DocumentationPage::class, - 'DataCollections' => \Hyde\Support\DataCollections::class, + 'DataCollection' => \Hyde\Support\DataCollection::class, 'Includes' => \Hyde\Support\Includes::class, 'Feature' => \Hyde\Enums\Feature::class, ], diff --git a/docs/digging-deeper/collections.md b/docs/digging-deeper/collections.md index b83bef6647f..b3040ecb900 100644 --- a/docs/digging-deeper/collections.md +++ b/docs/digging-deeper/collections.md @@ -8,7 +8,7 @@ navigation: ## Introduction to Hyde Data Collections -Hyde provides `DataCollections`, a subset of [Laravel Collections](https://laravel.com/docs/10.x/collections) giving you a similar developer experience to working with Eloquent Collections. +Hyde provides `DataCollection`, a subset of [Laravel Collections](https://laravel.com/docs/10.x/collections) giving you a similar developer experience to working with Eloquent Collections. However, instead of accessing a database, it's all entirely file-based using static data files such as Markdown, Yaml, and JSON files which get parsed into objects that you can easily work with. @@ -24,7 +24,7 @@ In short, HydePHP finds files in the specified directory, turns each file into a To make collections easy to use and understand, Hyde makes a few assumptions about the structure of your collections. Follow these conventions and creating dynamic static sites will be a breeze. -1. Collections are accessed through static methods in the `DataCollections` class. +1. Collections are accessed through static methods in the `DataCollection` class. 2. Collections are stored as files in subdirectories of the `resources/collections` directory. 3. To get a collection, specify name of the subdirectory the files are stored in. 4. Data will be parsed into differing objects depending on which facade method you use. See the table below. @@ -41,9 +41,9 @@ Follow these conventions and creating dynamic static sites will be a breeze. The following facade methods for creating data collections are available: ```php -\Hyde\Support\DataCollections::markdown(string $name); -\Hyde\Support\DataCollections::yaml(string $name); -\Hyde\Support\DataCollections::json(string $name, bool $asArray = false); +\Hyde\Support\DataCollection::markdown(string $name); +\Hyde\Support\DataCollection::yaml(string $name); +\Hyde\Support\DataCollection::json(string $name, bool $asArray = false); ``` ### Quick Reference Table @@ -60,7 +60,7 @@ The following facade methods for creating data collections are available: ### Usage ```php -$collection = \Hyde\Support\DataCollections::markdown('name'); +$collection = \Hyde\Support\DataCollection::markdown('name'); ``` ### Example returns @@ -68,7 +68,7 @@ $collection = \Hyde\Support\DataCollections::markdown('name'); Here is an approximation of the data types contained by the variable created above: ```php -\Hyde\Support\DataCollections { +\Hyde\Support\DataCollection { "testimonials/1.md" => Hyde\Markdown\Models\MarkdownDocument "testimonials/2.md" => Hyde\Markdown\Models\MarkdownDocument "testimonials/3.md" => Hyde\Markdown\Models\MarkdownDocument @@ -107,7 +107,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit... ### Usage ```php -$collection = \Hyde\Support\DataCollections::yaml('name'); +$collection = \Hyde\Support\DataCollection::yaml('name'); ``` ### Example returns @@ -115,7 +115,7 @@ $collection = \Hyde\Support\DataCollections::yaml('name'); Here is an approximation of the data types contained by the variable created above: ```php -\Hyde\Support\DataCollections { +\Hyde\Support\DataCollection { "authors/1.yaml" => Hyde\Markdown\Models\FrontMatter { +data: array:1 [ "name" => "John Doe", @@ -137,13 +137,13 @@ email: "john@example.org" ### Usage ```php -$collection = \Hyde\Support\DataCollections::json('name'); +$collection = \Hyde\Support\DataCollection::json('name'); ``` By default, the entries will be returned as `stdClass` objects. If you want to return an associative array instead, pass `true` as the second parameter: ```php -$collection = \Hyde\Support\DataCollections::json('name', true); +$collection = \Hyde\Support\DataCollection::json('name', true); ``` Since both return values use native PHP types, there are no example returns added here, as I'm sure you can imagine what they look like. @@ -186,18 +186,18 @@ resources/collections #### Using the Facade to Access the Collections -Now for the fun part! We will use the `DataCollections::markdown()` to access all our files into a convenient object. +Now for the fun part! We will use the `DataCollection::markdown()` to access all our files into a convenient object. The class is registered with an alias, so you don't need to include any namespaces when in a Blade file. The general syntax to use the facade is as follows: ```blade -DataCollections::markdown('subdirectory_name') +DataCollection::markdown('subdirectory_name') ``` -This will return a Hyde DataCollections object, containing our Markdown files as MarkdownDocument objects. Here is a quick look at the object the facade returns: +This will return a Hyde DataCollection object, containing our Markdown files as MarkdownDocument objects. Here is a quick look at the object the facade returns: -
^ Hyde\Support\DataCollections {#651 
+
^ Hyde\Support\DataCollection {#651 
   #items: array:3 [
     "testimonials/1.md" => Hyde\Markdown\Models\MarkdownDocument {#653 
       +matter: Hyde\Markdown\Models\FrontMatter {#652 }
@@ -222,7 +222,7 @@ we are able to get the author from the front matter, and the content from the bo
 
 ```blade
 // filepath _pages/testimonials.blade.php
-@foreach(DataCollections::markdown('testimonials') as $testimonial)
+@foreach(DataCollection::markdown('testimonials') as $testimonial)
     

{{ $testimonial->body }}

{{ $testimonial->matter['author'] }} diff --git a/docs/digging-deeper/helpers.md b/docs/digging-deeper/helpers.md index 62a89033a16..6414fba758a 100644 --- a/docs/digging-deeper/helpers.md +++ b/docs/digging-deeper/helpers.md @@ -14,20 +14,20 @@ Note that these helpers targets those who write custom code and Blade templates, ## File-based Collections -Hyde provides `DataCollections`, a subset of [Laravel Collections](https://laravel.com/docs/10.x/collections) giving you a similar developer experience to working with Eloquent Collections. However, instead of accessing a database, +Hyde provides `DataCollection`, a subset of [Laravel Collections](https://laravel.com/docs/10.x/collections) giving you a similar developer experience to working with Eloquent Collections. However, instead of accessing a database, it's all entirely file-based using static data files such as Markdown, Yaml, and JSON files which get parsed into objects that you can easily work with. ```php -use Hyde\Support\DataCollections; +use Hyde\Support\DataCollection; // Gets all Markdown files in resources/collections/$name directory -DataCollections::markdown(string $name); +DataCollection::markdown(string $name); // Gets all YAML files in resources/collections/$name directory -DataCollections::yaml(string $name); +DataCollection::yaml(string $name); // Gets all JSON files in resources/collections/$name directory -DataCollections::json(string $name, bool $asArray = false); +DataCollection::json(string $name, bool $asArray = false); ``` See the [File-based Collections](collections) documentation for the full details. diff --git a/packages/framework/src/Support/DataCollections.php b/packages/framework/src/Support/DataCollection.php similarity index 93% rename from packages/framework/src/Support/DataCollections.php rename to packages/framework/src/Support/DataCollection.php index 00d54371089..ffedb51e2cf 100644 --- a/packages/framework/src/Support/DataCollections.php +++ b/packages/framework/src/Support/DataCollection.php @@ -32,7 +32,7 @@ * All collections are indexed by their filename, which is relative * to the configured data collection source directory. */ -class DataCollections extends Collection +class DataCollection extends Collection { /** * The base directory for all data collections. Can be modified using a service provider. @@ -44,7 +44,7 @@ class DataCollections extends Collection * * Each Markdown file will be parsed into a MarkdownDocument with front matter. * - * @return DataCollections + * @return DataCollection */ public static function markdown(string $name): static { @@ -58,7 +58,7 @@ public static function markdown(string $name): static * * Each YAML file will be parsed into a FrontMatter object. * - * @return DataCollections + * @return DataCollection */ public static function yaml(string $name): static { @@ -78,7 +78,7 @@ public static function yaml(string $name): static * * Each JSON file will be parsed into a stdClass object, or an associative array, depending on the second parameter. * - * @return DataCollections + * @return DataCollection */ public static function json(string $name, bool $asArray = false): static { diff --git a/packages/framework/tests/Feature/DataCollectionTest.php b/packages/framework/tests/Feature/DataCollectionTest.php index f144e9fd367..9889472841e 100644 --- a/packages/framework/tests/Feature/DataCollectionTest.php +++ b/packages/framework/tests/Feature/DataCollectionTest.php @@ -6,11 +6,11 @@ use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; -use Hyde\Support\DataCollections; +use Hyde\Support\DataCollection; use Hyde\Testing\TestCase; /** - * @covers \Hyde\Support\DataCollections + * @covers \Hyde\Support\DataCollection * * @see \Hyde\Framework\Testing\Unit\DataCollectionUnitTest */ @@ -22,10 +22,10 @@ public function testMarkdownCollections() $this->markdown('resources/collections/foo/foo.md', 'Hello World', ['title' => 'Foo']); $this->file('resources/collections/foo/bar.md'); - $this->assertEquals(new DataCollections([ + $this->assertEquals(new DataCollection([ 'foo/foo.md' => new MarkdownDocument(['title' => 'Foo'], 'Hello World'), 'foo/bar.md' => new MarkdownDocument([], ''), - ]), DataCollections::markdown('foo')); + ]), DataCollection::markdown('foo')); } public function testYamlCollections() @@ -35,11 +35,11 @@ public function testYamlCollections() $this->file('resources/collections/foo/bar.yml', "---\ntitle: Bar\n---"); $this->file('resources/collections/foo/baz.yml'); - $this->assertEquals(new DataCollections([ + $this->assertEquals(new DataCollection([ 'foo/foo.yaml' => new FrontMatter(['title' => 'Foo']), 'foo/bar.yml' => new FrontMatter(['title' => 'Bar']), 'foo/baz.yml' => new FrontMatter([]), - ]), DataCollections::yaml('foo')); + ]), DataCollection::yaml('foo')); } public function testYamlCollectionsWithoutTripleDashes() @@ -47,9 +47,9 @@ public function testYamlCollectionsWithoutTripleDashes() $this->directory('resources/collections/foo'); $this->file('resources/collections/foo/foo.yml', 'title: Foo'); - $this->assertEquals(new DataCollections([ + $this->assertEquals(new DataCollection([ 'foo/foo.yml' => new FrontMatter(['title' => 'Foo']), - ]), DataCollections::yaml('foo')); + ]), DataCollection::yaml('foo')); } public function testJsonCollections() @@ -58,10 +58,10 @@ public function testJsonCollections() $this->file('resources/collections/foo/foo.json', json_encode(['foo' => 'bar'])); $this->file('resources/collections/foo/bar.json'); - $this->assertEquals(new DataCollections([ + $this->assertEquals(new DataCollection([ 'foo/foo.json' => (object) ['foo' => 'bar'], 'foo/bar.json' => null, - ]), DataCollections::json('foo')); + ]), DataCollection::json('foo')); } public function testJsonCollectionsAsArrays() @@ -70,24 +70,24 @@ public function testJsonCollectionsAsArrays() $this->file('resources/collections/foo/foo.json', json_encode(['foo' => 'bar'])); $this->file('resources/collections/foo/bar.json'); - $this->assertEquals(new DataCollections([ + $this->assertEquals(new DataCollection([ 'foo/foo.json' => ['foo' => 'bar'], 'foo/bar.json' => null, - ]), DataCollections::json('foo', true)); + ]), DataCollection::json('foo', true)); } public function testFindMarkdownFilesMethodReturnsEmptyArrayIfTheSpecifiedDirectoryDoesNotExist() { - $this->assertIsArray(DataCollections::markdown('foo')->keys()->toArray()); - $this->assertEmpty(DataCollections::markdown('foo')->keys()->toArray()); + $this->assertIsArray(DataCollection::markdown('foo')->keys()->toArray()); + $this->assertEmpty(DataCollection::markdown('foo')->keys()->toArray()); } public function testFindMarkdownFilesMethodReturnsEmptyArrayIfNoFilesAreFoundInSpecifiedDirectory() { $this->directory('resources/collections/foo'); - $this->assertIsArray(DataCollections::markdown('foo')->keys()->toArray()); - $this->assertEmpty(DataCollections::markdown('foo')->keys()->toArray()); + $this->assertIsArray(DataCollection::markdown('foo')->keys()->toArray()); + $this->assertEmpty(DataCollection::markdown('foo')->keys()->toArray()); } public function testFindMarkdownFilesMethodReturnsAnArrayOfMarkdownFilesInTheSpecifiedDirectory() @@ -99,7 +99,7 @@ public function testFindMarkdownFilesMethodReturnsAnArrayOfMarkdownFilesInTheSpe $this->assertSame([ 'foo/bar.md', 'foo/foo.md', - ], DataCollections::markdown('foo')->keys()->toArray()); + ], DataCollection::markdown('foo')->keys()->toArray()); } public function testFindMarkdownFilesMethodDoesNotIncludeFilesInSubdirectories() @@ -111,7 +111,7 @@ public function testFindMarkdownFilesMethodDoesNotIncludeFilesInSubdirectories() $this->assertSame([ 'foo/foo.md', - ], DataCollections::markdown('foo')->keys()->toArray()); + ], DataCollection::markdown('foo')->keys()->toArray()); } public function testFindMarkdownFilesMethodDoesNotIncludeFilesWithExtensionsOtherThanMd() @@ -122,7 +122,7 @@ public function testFindMarkdownFilesMethodDoesNotIncludeFilesWithExtensionsOthe $this->assertSame([ 'foo/foo.md', - ], DataCollections::markdown('foo')->keys()->toArray()); + ], DataCollection::markdown('foo')->keys()->toArray()); } public function testFindMarkdownFilesMethodDoesNotRemoveFilesStartingWithAnUnderscore() @@ -132,7 +132,7 @@ public function testFindMarkdownFilesMethodDoesNotRemoveFilesStartingWithAnUnder $this->assertSame([ 'foo/_foo.md', - ], DataCollections::markdown('foo')->keys()->toArray()); + ], DataCollection::markdown('foo')->keys()->toArray()); } public function testStaticMarkdownHelperDiscoversAndParsesMarkdownFilesInTheSpecifiedDirectory() @@ -144,7 +144,7 @@ public function testStaticMarkdownHelperDiscoversAndParsesMarkdownFilesInTheSpec $this->assertEquals([ 'foo/foo.md' => new MarkdownDocument([], ''), 'foo/bar.md' => new MarkdownDocument([], ''), - ], DataCollections::markdown('foo')->toArray()); + ], DataCollection::markdown('foo')->toArray()); } public function testStaticMarkdownHelperDoestNotIgnoreFilesStartingWithAnUnderscore() @@ -153,19 +153,19 @@ public function testStaticMarkdownHelperDoestNotIgnoreFilesStartingWithAnUndersc $this->file('resources/collections/foo/foo.md'); $this->file('resources/collections/foo/_bar.md'); - $this->assertCount(2, DataCollections::markdown('foo')); + $this->assertCount(2, DataCollection::markdown('foo')); } public function testSourceDirectoryCanBeChanged() { - DataCollections::$sourceDirectory = 'foo'; + DataCollection::$sourceDirectory = 'foo'; $this->directory('foo/bar'); $this->file('foo/bar/foo.md'); $this->assertSame([ 'bar/foo.md', - ], DataCollections::markdown('bar')->keys()->toArray()); + ], DataCollection::markdown('bar')->keys()->toArray()); - DataCollections::$sourceDirectory = 'resources/collections'; + DataCollection::$sourceDirectory = 'resources/collections'; } } diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index fc4cfdea17b..cfea0468e04 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -5,14 +5,14 @@ namespace Hyde\Framework\Testing\Unit; use Hyde\Hyde; -use Hyde\Support\DataCollections; +use Hyde\Support\DataCollection; use Hyde\Testing\UnitTestCase; use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Collection; use Mockery; /** - * @covers \Hyde\Support\DataCollections + * @covers \Hyde\Support\DataCollection * * @see \Hyde\Framework\Testing\Feature\DataCollectionTest */ @@ -22,27 +22,27 @@ class DataCollectionUnitTest extends UnitTestCase public function testClassHasStaticSourceDirectoryProperty() { - $this->assertSame('resources/collections', DataCollections::$sourceDirectory); + $this->assertSame('resources/collections', DataCollection::$sourceDirectory); } public function testConstructorCreatesNewDataCollectionInstance() { - $this->assertInstanceOf(DataCollections::class, new DataCollections()); + $this->assertInstanceOf(DataCollection::class, new DataCollection()); } public function testClassExtendsCollectionClass() { - $this->assertInstanceOf(Collection::class, new DataCollections()); + $this->assertInstanceOf(Collection::class, new DataCollection()); } public function testCanConvertCollectionToArray() { - $this->assertSame([], (new DataCollections())->toArray()); + $this->assertSame([], (new DataCollection())->toArray()); } public function testCanConvertCollectionToJson() { - $this->assertSame('[]', (new DataCollections())->toJson()); + $this->assertSame('[]', (new DataCollection())->toJson()); } public function testFindMarkdownFilesCallsProperGlobPattern() @@ -54,7 +54,7 @@ public function testFindMarkdownFilesCallsProperGlobPattern() app()->instance(Filesystem::class, $filesystem); - DataCollections::markdown('foo')->keys()->toArray(); + DataCollection::markdown('foo')->keys()->toArray(); $this->addToAssertionCount(Mockery::getContainer()->mockery_getExpectationCount()); Mockery::close(); @@ -69,7 +69,7 @@ public function testFindMarkdownFilesWithNoFiles() app()->instance(Filesystem::class, $filesystem); - $this->assertSame([], DataCollections::markdown('foo')->keys()->toArray()); + $this->assertSame([], DataCollection::markdown('foo')->keys()->toArray()); Mockery::close(); } @@ -84,13 +84,13 @@ public function testFindMarkdownFilesWithFiles() app()->instance(Filesystem::class, $filesystem); - $this->assertSame(['bar.md'], DataCollections::markdown('foo')->keys()->toArray()); + $this->assertSame(['bar.md'], DataCollection::markdown('foo')->keys()->toArray()); Mockery::close(); } public function testStaticMarkdownHelperReturnsNewDataCollectionInstance() { - $this->assertInstanceOf(DataCollections::class, DataCollections::markdown('foo')); + $this->assertInstanceOf(DataCollection::class, DataCollection::markdown('foo')); } } From a24aa34412226c865fdbb663aa70351300c17797 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 09:42:56 +0200 Subject: [PATCH 012/118] Add mockable data collection test class --- .../tests/Unit/DataCollectionUnitTest.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index cfea0468e04..4cc703c0676 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -20,6 +20,13 @@ class DataCollectionUnitTest extends UnitTestCase { protected static bool $needsKernel = true; + protected function tearDown(): void + { + MockableDataCollection::tearDown(); + + parent::tearDown(); + } + public function testClassHasStaticSourceDirectoryProperty() { $this->assertSame('resources/collections', DataCollection::$sourceDirectory); @@ -94,3 +101,23 @@ public function testStaticMarkdownHelperReturnsNewDataCollectionInstance() $this->assertInstanceOf(DataCollection::class, DataCollection::markdown('foo')); } } + +class MockableDataCollection extends DataCollection +{ + protected static array $mockFiles = []; + + protected static function findFiles(string $name, array|string $extensions): Collection + { + return collect(static::$mockFiles); + } + + public static function mockFiles(array $files): void + { + static::$mockFiles = $files; + } + + public static function tearDown(): void + { + static::$mockFiles = []; + } +} From 02b8b0663af262f254cac5ded3a1802efdd10848 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 09:48:44 +0200 Subject: [PATCH 013/118] Get by name --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 4cc703c0676..cea8cf15b1a 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -108,7 +108,7 @@ class MockableDataCollection extends DataCollection protected static function findFiles(string $name, array|string $extensions): Collection { - return collect(static::$mockFiles); + return collect(static::$mockFiles[$name]); } public static function mockFiles(array $files): void From 6c91c63e5aced05b9820e052bd5acaadc3daf500 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 09:49:18 +0200 Subject: [PATCH 014/118] Normalize file contents syntax --- packages/framework/src/Support/DataCollection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index ffedb51e2cf..a8fd789b4b0 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -63,7 +63,7 @@ public static function markdown(string $name): static public static function yaml(string $name): static { return new static(static::findFiles($name, ['yaml', 'yml'])->mapWithKeys(function (string $file): array { - $content = Filesystem::get($file); + $content = Filesystem::getContents($file); $content = Str::between($content, '---', '---'); $parsed = Yaml::parse($content) ?: []; @@ -83,7 +83,7 @@ public static function yaml(string $name): static public static function json(string $name, bool $asArray = false): static { return new static(static::findFiles($name, 'json')->mapWithKeys(function (string $file) use ($asArray): array { - return [static::makeIdentifier($file) => json_decode(Filesystem::get($file), $asArray)]; + return [static::makeIdentifier($file) => json_decode(Filesystem::getContents($file), $asArray)]; })); } From dd63ffe1c7a03a591d430b865bf5864805573487 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 09:55:33 +0200 Subject: [PATCH 015/118] Add array based glob helper --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index cea8cf15b1a..47d51c23752 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -120,4 +120,11 @@ public static function tearDown(): void { static::$mockFiles = []; } + + protected static function arrayGlob(array $files, string $name, array|string $extensions): array + { + return array_filter($files, function (string $file) use ($name, $extensions): bool { + return str_contains($file, $name) && str_contains($file, $extensions); + }, ARRAY_FILTER_USE_KEY); + } } From b577a4804207ab1d06826dfa989c205f04ee02f6 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 09:55:52 +0200 Subject: [PATCH 016/118] Collect using array glob --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 47d51c23752..cdac3cee973 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -108,7 +108,7 @@ class MockableDataCollection extends DataCollection protected static function findFiles(string $name, array|string $extensions): Collection { - return collect(static::$mockFiles[$name]); + return collect(static::arrayGlob(static::$mockFiles, $name, $extensions)); } public static function mockFiles(array $files): void From db628613ab9d4b7521886cdbc1ac0d6d4bc3166c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 09:56:00 +0200 Subject: [PATCH 017/118] Annotate generics --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index cdac3cee973..1ded27cc22c 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -111,6 +111,9 @@ protected static function findFiles(string $name, array|string $extensions): Col return collect(static::arrayGlob(static::$mockFiles, $name, $extensions)); } + /** + * @param array $files Filename as key, file contents as value. + */ public static function mockFiles(array $files): void { static::$mockFiles = $files; From dd698abe3298e324597d961798dafdad5f0e93a8 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 09:56:05 +0200 Subject: [PATCH 018/118] Assert generics and test mock setup --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 1ded27cc22c..016dd5adc24 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -116,6 +116,13 @@ protected static function findFiles(string $name, array|string $extensions): Col */ public static function mockFiles(array $files): void { + foreach ($files as $file => $contents) { + assert(is_string($file), 'File name must be a string.'); + assert(is_string($contents), 'File contents must be a string.'); + assert(str_contains($file, '/'), 'File must be in a directory.'); + assert(str_contains($file, '.'), 'File must have an extension.'); + } + static::$mockFiles = $files; } From d9bb52adee743da4afda6428685b7bc1773e6fb3 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 10:04:36 +0200 Subject: [PATCH 019/118] Mock the filesystem --- .../framework/tests/Unit/DataCollectionUnitTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 016dd5adc24..3a78022ee93 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -123,6 +123,17 @@ public static function mockFiles(array $files): void assert(str_contains($file, '.'), 'File must have an extension.'); } + $filesystem = Mockery::mock(Filesystem::class); + $filesystem->shouldReceive('get') + ->andReturnUsing(function (string $file) use ($files) { + $file = unslash(str_replace(Hyde::path(), '', $file)); + $files = static::arrayGlob($files, $file, 'md'); + + return array_values($files)[0] ?? ''; + }); + + app()->instance(Filesystem::class, $filesystem); + static::$mockFiles = $files; } From e8af560121c4103094ed9f1bb736c15dff16e054 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 14:14:43 +0200 Subject: [PATCH 020/118] Simplify testing helpers --- .../tests/Unit/DataCollectionUnitTest.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 3a78022ee93..381e08d95a2 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -5,11 +5,14 @@ namespace Hyde\Framework\Testing\Unit; use Hyde\Hyde; +use Illuminate\Support\Str; use Hyde\Support\DataCollection; use Hyde\Testing\UnitTestCase; use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Collection; use Mockery; +use Hyde\Markdown\Models\FrontMatter; +use Hyde\Markdown\Models\MarkdownDocument; /** * @covers \Hyde\Support\DataCollection @@ -108,7 +111,7 @@ class MockableDataCollection extends DataCollection protected static function findFiles(string $name, array|string $extensions): Collection { - return collect(static::arrayGlob(static::$mockFiles, $name, $extensions)); + return collect(static::$mockFiles)->keys(); } /** @@ -127,9 +130,9 @@ public static function mockFiles(array $files): void $filesystem->shouldReceive('get') ->andReturnUsing(function (string $file) use ($files) { $file = unslash(str_replace(Hyde::path(), '', $file)); - $files = static::arrayGlob($files, $file, 'md'); + $files = collect($files)->mapWithKeys(fn ($contents, $file) => [Str::before(basename($file), '.') => $contents])->all(); - return array_values($files)[0] ?? ''; + return $files[$file] ?? ''; }); app()->instance(Filesystem::class, $filesystem); @@ -141,11 +144,4 @@ public static function tearDown(): void { static::$mockFiles = []; } - - protected static function arrayGlob(array $files, string $name, array|string $extensions): array - { - return array_filter($files, function (string $file) use ($name, $extensions): bool { - return str_contains($file, $name) && str_contains($file, $extensions); - }, ARRAY_FILTER_USE_KEY); - } } From ba3b72a126abe47696278233a718af747f67bb0a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 14:15:05 +0200 Subject: [PATCH 021/118] Test Markdown method returns collection of Markdown documents --- .../tests/Unit/DataCollectionUnitTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 381e08d95a2..de2b58bc6a9 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -103,6 +103,23 @@ public function testStaticMarkdownHelperReturnsNewDataCollectionInstance() { $this->assertInstanceOf(DataCollection::class, DataCollection::markdown('foo')); } + + public function testMarkdownMethodReturnsCollectionOfMarkdownDocuments() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => 'bar', + 'foo/baz.md' => 'baz', + ]); + + $collection = MockableDataCollection::markdown('foo'); + + $this->assertContainsOnlyInstancesOf(MarkdownDocument::class, $collection); + + $this->assertSame([ + 'bar' => 'bar', + 'baz' => 'baz', + ], $collection->map(fn ($value) => (string) $value)->all()); + } } class MockableDataCollection extends DataCollection From 8474481fa903caaa2dee5a38f7e5755729700a4e Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 14:15:34 +0200 Subject: [PATCH 022/118] Test Yaml method returns collection of front matter objects --- .../tests/Unit/DataCollectionUnitTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index de2b58bc6a9..140b2fdd76c 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -120,6 +120,23 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocuments() 'baz' => 'baz', ], $collection->map(fn ($value) => (string) $value)->all()); } + + public function testYamlMethodReturnsCollectionOfFrontMatterObjects() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => '---\nfoo: bar\n---', + 'foo/baz.yml' => '---\nfoo: baz\n---', + ]); + + $collection = MockableDataCollection::yaml('foo'); + + $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); + + $this->assertSame([ + 'bar' => ['foo' => 'bar'], + 'baz' => ['foo' => 'baz'], + ], $collection->map(fn ($value) => $value->toArray())->all()); + } } class MockableDataCollection extends DataCollection From 07e9cc3ba42391da28a06d6e96a78da8d3e26cad Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 14:25:20 +0200 Subject: [PATCH 023/118] Update mocks to better match real setup --- .../framework/tests/Unit/DataCollectionUnitTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 140b2fdd76c..f56d0719652 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -116,8 +116,8 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocuments() $this->assertContainsOnlyInstancesOf(MarkdownDocument::class, $collection); $this->assertSame([ - 'bar' => 'bar', - 'baz' => 'baz', + 'foo/bar.md' => 'bar', + 'foo/baz.md' => 'baz', ], $collection->map(fn ($value) => (string) $value)->all()); } @@ -133,8 +133,8 @@ public function testYamlMethodReturnsCollectionOfFrontMatterObjects() $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); $this->assertSame([ - 'bar' => ['foo' => 'bar'], - 'baz' => ['foo' => 'baz'], + 'foo/bar.yml' => ['foo' => 'bar'], + 'foo/baz.yml' => ['foo' => 'baz'], ], $collection->map(fn ($value) => $value->toArray())->all()); } } @@ -145,7 +145,7 @@ class MockableDataCollection extends DataCollection protected static function findFiles(string $name, array|string $extensions): Collection { - return collect(static::$mockFiles)->keys(); + return collect(static::$mockFiles)->keys()->map(fn ($file) => parent::makeIdentifier($file))->values(); } /** @@ -163,7 +163,7 @@ public static function mockFiles(array $files): void $filesystem = Mockery::mock(Filesystem::class); $filesystem->shouldReceive('get') ->andReturnUsing(function (string $file) use ($files) { - $file = unslash(str_replace(Hyde::path(), '', $file)); + $file = Str::before(basename($file), '.'); $files = collect($files)->mapWithKeys(fn ($contents, $file) => [Str::before(basename($file), '.') => $contents])->all(); return $files[$file] ?? ''; From 330940e3e53c71d17126ee667a362a577c3d15ba Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 14:27:13 +0200 Subject: [PATCH 024/118] Fix quote usage --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index f56d0719652..69c5f745a05 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -124,8 +124,8 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocuments() public function testYamlMethodReturnsCollectionOfFrontMatterObjects() { MockableDataCollection::mockFiles([ - 'foo/bar.yml' => '---\nfoo: bar\n---', - 'foo/baz.yml' => '---\nfoo: baz\n---', + 'foo/bar.yml' => "---\nfoo: bar\n---", + 'foo/baz.yml' => "---\nfoo: baz\n---", ]); $collection = MockableDataCollection::yaml('foo'); From c2ad0e9a4d0dc86c1b4977512e3b95c3b1ce57d7 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 14:35:48 +0200 Subject: [PATCH 025/118] Test differing Yaml syntaxes --- .../tests/Unit/DataCollectionUnitTest.php | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 69c5f745a05..a89d7d93480 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -137,6 +137,57 @@ public function testYamlMethodReturnsCollectionOfFrontMatterObjects() 'foo/baz.yml' => ['foo' => 'baz'], ], $collection->map(fn ($value) => $value->toArray())->all()); } + + public function testYamlCollectionsDoNotRequireTripleDashes() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => 'foo: bar', + 'foo/baz.yml' => 'foo: baz', + ]); + + $collection = MockableDataCollection::yaml('foo'); + + $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); + + $this->assertSame([ + 'foo/bar.yml' => ['foo' => 'bar'], + 'foo/baz.yml' => ['foo' => 'baz'], + ], $collection->map(fn ($value) => $value->toArray())->all()); + } + + public function testYamlCollectionsAcceptTripleDashes() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => "---\nfoo: bar\n---", + 'foo/baz.yml' => "---\nfoo: baz", + ]); + + $collection = MockableDataCollection::yaml('foo'); + + $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); + + $this->assertSame([ + 'foo/bar.yml' => ['foo' => 'bar'], + 'foo/baz.yml' => ['foo' => 'baz'], + ], $collection->map(fn ($value) => $value->toArray())->all()); + } + + public function testYamlCollectionsSupportYamlAndYmlFileExtensions() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yaml' => "---\nfoo: bar\n---", + 'foo/baz.yml' => "---\nfoo: baz\n---", + ]); + + $collection = MockableDataCollection::yaml('foo'); + + $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); + + $this->assertSame([ + 'foo/bar.yaml' => ['foo' => 'bar'], + 'foo/baz.yml' => ['foo' => 'baz'], + ], $collection->map(fn ($value) => $value->toArray())->all()); + } } class MockableDataCollection extends DataCollection From ae8523dcd2522ca5cb5a985ef6903b6410913ad4 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 14:36:28 +0200 Subject: [PATCH 026/118] Test Yaml fault tolerance --- .../tests/Unit/DataCollectionUnitTest.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index a89d7d93480..65d5185414f 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -188,6 +188,61 @@ public function testYamlCollectionsSupportYamlAndYmlFileExtensions() 'foo/baz.yml' => ['foo' => 'baz'], ], $collection->map(fn ($value) => $value->toArray())->all()); } + + public function testYamlCollectionsHandleLeadingAndTrailingNewlines() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => "\nfoo: bar\n", + 'foo/baz.yml' => "\nfoo: baz", + 'foo/qux.yml' => "foo: qux\n", + ]); + + $collection = MockableDataCollection::yaml('foo'); + + $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); + + $this->assertSame([ + 'foo/bar.yml' => ['foo' => 'bar'], + 'foo/baz.yml' => ['foo' => 'baz'], + 'foo/qux.yml' => ['foo' => 'qux'], + ], $collection->map(fn ($value) => $value->toArray())->all()); + } + + public function testYamlCollectionsHandleTrailingWhitespace() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => 'foo: bar ', + 'foo/baz.yml' => 'foo: baz ', + ]); + + $collection = MockableDataCollection::yaml('foo'); + + $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); + + $this->assertSame([ + 'foo/bar.yml' => ['foo' => 'bar'], + 'foo/baz.yml' => ['foo' => 'baz'], + ], $collection->map(fn ($value) => $value->toArray())->all()); + } + + public function testYamlCollectionsHandleLeadingAndTrailingNewlinesAndTrailingWhitespace() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => "\nfoo: bar \n", + 'foo/baz.yml' => "\nfoo: baz\n ", + 'foo/qux.yml' => "foo: qux \n", + ]); + + $collection = MockableDataCollection::yaml('foo'); + + $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); + + $this->assertSame([ + 'foo/bar.yml' => ['foo' => 'bar'], + 'foo/baz.yml' => ['foo' => 'baz'], + 'foo/qux.yml' => ['foo' => 'qux'], + ], $collection->map(fn ($value) => $value->toArray())->all()); + } } class MockableDataCollection extends DataCollection From bc8c4fc207595028c0940ffb01417398a683655a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 14:41:46 +0200 Subject: [PATCH 027/118] Extract helper to assert front matter collection structure --- .../tests/Unit/DataCollectionUnitTest.php | 63 +++++++------------ 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 65d5185414f..14ae99aa8da 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -128,14 +128,10 @@ public function testYamlMethodReturnsCollectionOfFrontMatterObjects() 'foo/baz.yml' => "---\nfoo: baz\n---", ]); - $collection = MockableDataCollection::yaml('foo'); - - $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); - - $this->assertSame([ + $this->assertFrontMatterCollectionStructure([ 'foo/bar.yml' => ['foo' => 'bar'], 'foo/baz.yml' => ['foo' => 'baz'], - ], $collection->map(fn ($value) => $value->toArray())->all()); + ], MockableDataCollection::yaml('foo')); } public function testYamlCollectionsDoNotRequireTripleDashes() @@ -145,14 +141,10 @@ public function testYamlCollectionsDoNotRequireTripleDashes() 'foo/baz.yml' => 'foo: baz', ]); - $collection = MockableDataCollection::yaml('foo'); - - $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); - - $this->assertSame([ + $this->assertFrontMatterCollectionStructure([ 'foo/bar.yml' => ['foo' => 'bar'], 'foo/baz.yml' => ['foo' => 'baz'], - ], $collection->map(fn ($value) => $value->toArray())->all()); + ], MockableDataCollection::yaml('foo')); } public function testYamlCollectionsAcceptTripleDashes() @@ -162,14 +154,10 @@ public function testYamlCollectionsAcceptTripleDashes() 'foo/baz.yml' => "---\nfoo: baz", ]); - $collection = MockableDataCollection::yaml('foo'); - - $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); - - $this->assertSame([ + $this->assertFrontMatterCollectionStructure([ 'foo/bar.yml' => ['foo' => 'bar'], 'foo/baz.yml' => ['foo' => 'baz'], - ], $collection->map(fn ($value) => $value->toArray())->all()); + ], MockableDataCollection::yaml('foo')); } public function testYamlCollectionsSupportYamlAndYmlFileExtensions() @@ -179,14 +167,10 @@ public function testYamlCollectionsSupportYamlAndYmlFileExtensions() 'foo/baz.yml' => "---\nfoo: baz\n---", ]); - $collection = MockableDataCollection::yaml('foo'); - - $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); - - $this->assertSame([ + $this->assertFrontMatterCollectionStructure([ 'foo/bar.yaml' => ['foo' => 'bar'], 'foo/baz.yml' => ['foo' => 'baz'], - ], $collection->map(fn ($value) => $value->toArray())->all()); + ], MockableDataCollection::yaml('foo')); } public function testYamlCollectionsHandleLeadingAndTrailingNewlines() @@ -197,15 +181,11 @@ public function testYamlCollectionsHandleLeadingAndTrailingNewlines() 'foo/qux.yml' => "foo: qux\n", ]); - $collection = MockableDataCollection::yaml('foo'); - - $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); - - $this->assertSame([ + $this->assertFrontMatterCollectionStructure([ 'foo/bar.yml' => ['foo' => 'bar'], 'foo/baz.yml' => ['foo' => 'baz'], 'foo/qux.yml' => ['foo' => 'qux'], - ], $collection->map(fn ($value) => $value->toArray())->all()); + ], MockableDataCollection::yaml('foo')); } public function testYamlCollectionsHandleTrailingWhitespace() @@ -215,14 +195,10 @@ public function testYamlCollectionsHandleTrailingWhitespace() 'foo/baz.yml' => 'foo: baz ', ]); - $collection = MockableDataCollection::yaml('foo'); - - $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); - - $this->assertSame([ + $this->assertFrontMatterCollectionStructure([ 'foo/bar.yml' => ['foo' => 'bar'], 'foo/baz.yml' => ['foo' => 'baz'], - ], $collection->map(fn ($value) => $value->toArray())->all()); + ], MockableDataCollection::yaml('foo')); } public function testYamlCollectionsHandleLeadingAndTrailingNewlinesAndTrailingWhitespace() @@ -233,15 +209,18 @@ public function testYamlCollectionsHandleLeadingAndTrailingNewlinesAndTrailingWh 'foo/qux.yml' => "foo: qux \n", ]); - $collection = MockableDataCollection::yaml('foo'); - - $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); - - $this->assertSame([ + $this->assertFrontMatterCollectionStructure([ 'foo/bar.yml' => ['foo' => 'bar'], 'foo/baz.yml' => ['foo' => 'baz'], 'foo/qux.yml' => ['foo' => 'qux'], - ], $collection->map(fn ($value) => $value->toArray())->all()); + ], MockableDataCollection::yaml('foo')); + } + + protected function assertFrontMatterCollectionStructure(array $expected, DataCollection $collection): void + { + $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); + + $this->assertSame($expected, $collection->map(fn ($value) => $value->toArray())->all()); } } From d36c1a706d949159a5ac9e659f570b6ce91f1934 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 15:08:07 +0200 Subject: [PATCH 028/118] Add helper to assert Json collection structure --- .../tests/Unit/DataCollectionUnitTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 14ae99aa8da..f832c090fe1 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -222,6 +222,20 @@ protected function assertFrontMatterCollectionStructure(array $expected, DataCol $this->assertSame($expected, $collection->map(fn ($value) => $value->toArray())->all()); } + + protected function assertJsonCollectionStructure(array $expected, DataCollection $collection, bool $asArray = false): void + { + if ($asArray) { + $this->assertContainsOnly('array', $collection); + } else { + $this->assertContainsOnly('object', $collection); + } + + $this->assertSame( + collect($expected)->map(fn ($value) => (array) $value)->all(), + $collection->map(fn ($value) => (array) $value)->all() + ); + } } class MockableDataCollection extends DataCollection From 46a2bb11d2fa436bd21c179c5c54d4d486f9a69a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 15:08:19 +0200 Subject: [PATCH 029/118] Simplify assertion handling --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index f832c090fe1..4cff6decc60 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -229,12 +229,12 @@ protected function assertJsonCollectionStructure(array $expected, DataCollection $this->assertContainsOnly('array', $collection); } else { $this->assertContainsOnly('object', $collection); + + $expected = collect($expected)->map(fn ($value) => (array) $value)->all(); + $collection = $collection->map(fn ($value) => (array) $value); } - $this->assertSame( - collect($expected)->map(fn ($value) => (array) $value)->all(), - $collection->map(fn ($value) => (array) $value)->all() - ); + $this->assertSame($expected, $collection->all()); } } From 7535cdca2c0d591e260f6d1f09207494e323fa21 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 15:08:14 +0200 Subject: [PATCH 030/118] Test Json collection handling --- .../tests/Unit/DataCollectionUnitTest.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 4cff6decc60..b80b68cbb62 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -216,6 +216,32 @@ public function testYamlCollectionsHandleLeadingAndTrailingNewlinesAndTrailingWh ], MockableDataCollection::yaml('foo')); } + public function testJsonMethodReturnsCollectionOfJsonDecodedObjects() + { + MockableDataCollection::mockFiles([ + 'foo/bar.json' => '{"foo": "bar"}', + 'foo/baz.json' => '{"foo": "baz"}', + ]); + + $this->assertJsonCollectionStructure([ + 'foo/bar.json' => (object) ['foo' => 'bar'], + 'foo/baz.json' => (object) ['foo' => 'baz'], + ], MockableDataCollection::json('foo')); + } + + public function testJsonMethodReturnsCollectionOfJsonDecodedArrays() + { + MockableDataCollection::mockFiles([ + 'foo/bar.json' => '{"foo": "bar"}', + 'foo/baz.json' => '{"foo": "baz"}', + ]); + + $this->assertJsonCollectionStructure([ + 'foo/bar.json' => ['foo' => 'bar'], + 'foo/baz.json' => ['foo' => 'baz'], + ], MockableDataCollection::json('foo', true), true); + } + protected function assertFrontMatterCollectionStructure(array $expected, DataCollection $collection): void { $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); From 9ce2c983c65727f831e7b5e5c8502a03dafcb900 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 17:53:52 +0200 Subject: [PATCH 031/118] Remove duplicated word from documentation --- packages/framework/src/Support/DataCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index a8fd789b4b0..dcd2938dd34 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -27,7 +27,7 @@ * they allow you to quickly access the data collections. * * To use them retrieve a collection, call a facade method with the - * name of the data collection subdirectory directory. + * name of the data collection subdirectory. * * All collections are indexed by their filename, which is relative * to the configured data collection source directory. From d491da16faab80e430591cdd240ae47820ae143c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:12:33 +0200 Subject: [PATCH 032/118] Refactor to extract helper method --- .../framework/src/Support/DataCollection.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index dcd2938dd34..181e57d7fc2 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -48,9 +48,9 @@ class DataCollection extends Collection */ public static function markdown(string $name): static { - return new static(static::findFiles($name, 'md')->mapWithKeys(function (string $file): array { + return static::discover($name, 'md', function (string $file): array { return [static::makeIdentifier($file) => MarkdownFileParser::parse($file)]; - })); + }); } /** @@ -62,7 +62,7 @@ public static function markdown(string $name): static */ public static function yaml(string $name): static { - return new static(static::findFiles($name, ['yaml', 'yml'])->mapWithKeys(function (string $file): array { + return static::discover($name, ['yaml', 'yml'], function (string $file): array { $content = Filesystem::getContents($file); $content = Str::between($content, '---', '---'); @@ -70,7 +70,7 @@ public static function yaml(string $name): static $matter = new FrontMatter($parsed); return [static::makeIdentifier($file) => $matter]; - })); + }); } /** @@ -82,9 +82,9 @@ public static function yaml(string $name): static */ public static function json(string $name, bool $asArray = false): static { - return new static(static::findFiles($name, 'json')->mapWithKeys(function (string $file) use ($asArray): array { + return static::discover($name, 'json', function (string $file) use ($asArray): array { return [static::makeIdentifier($file) => json_decode(Filesystem::getContents($file), $asArray)]; - })); + }); } protected static function findFiles(string $name, array|string $extensions): Collection @@ -98,4 +98,9 @@ protected static function makeIdentifier(string $path): string { return unslash(Str::after($path, static::$sourceDirectory)); } + + protected static function discover(string $name, array|string $extensions, callable $callback): static + { + return new static(static::findFiles($name, $extensions)->mapWithKeys($callback)); + } } From aaffe2cbecc2dbf7bd71fa319c95f46dab6c71ae Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:13:57 +0200 Subject: [PATCH 033/118] Move up helper method --- packages/framework/src/Support/DataCollection.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 181e57d7fc2..ad9009e5ac5 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -87,6 +87,11 @@ public static function json(string $name, bool $asArray = false): static }); } + protected static function discover(string $name, array|string $extensions, callable $callback): static + { + return new static(static::findFiles($name, $extensions)->mapWithKeys($callback)); + } + protected static function findFiles(string $name, array|string $extensions): Collection { return Filesystem::smartGlob(sprintf('%s/%s/*.{%s}', @@ -98,9 +103,4 @@ protected static function makeIdentifier(string $path): string { return unslash(Str::after($path, static::$sourceDirectory)); } - - protected static function discover(string $name, array|string $extensions, callable $callback): static - { - return new static(static::findFiles($name, $extensions)->mapWithKeys($callback)); - } } From 194899d95ab38100b000731492034a01e90c0bd5 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:14:36 +0200 Subject: [PATCH 034/118] Rename callback parameter --- packages/framework/src/Support/DataCollection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index ad9009e5ac5..5f4b8a1bdb8 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -87,9 +87,9 @@ public static function json(string $name, bool $asArray = false): static }); } - protected static function discover(string $name, array|string $extensions, callable $callback): static + protected static function discover(string $name, array|string $extensions, callable $mapUsing): static { - return new static(static::findFiles($name, $extensions)->mapWithKeys($callback)); + return new static(static::findFiles($name, $extensions)->mapWithKeys($mapUsing)); } protected static function findFiles(string $name, array|string $extensions): Collection From f89f40801d55f497f3c061aaff53a9743ddfc539 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:25:27 +0200 Subject: [PATCH 035/118] Refactor to further extract shared logic to helper --- .../framework/src/Support/DataCollection.php | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 5f4b8a1bdb8..7185e45e16c 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -4,9 +4,11 @@ namespace Hyde\Support; +use stdClass; use Hyde\Facades\Filesystem; use Symfony\Component\Yaml\Yaml; use Hyde\Markdown\Models\FrontMatter; +use Hyde\Markdown\Models\MarkdownDocument; use Hyde\Framework\Actions\MarkdownFileParser; use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -48,8 +50,8 @@ class DataCollection extends Collection */ public static function markdown(string $name): static { - return static::discover($name, 'md', function (string $file): array { - return [static::makeIdentifier($file) => MarkdownFileParser::parse($file)]; + return static::discover($name, 'md', function (string $file): MarkdownDocument { + return MarkdownFileParser::parse($file); }); } @@ -62,14 +64,14 @@ public static function markdown(string $name): static */ public static function yaml(string $name): static { - return static::discover($name, ['yaml', 'yml'], function (string $file): array { + return static::discover($name, ['yaml', 'yml'], function (string $file): FrontMatter { $content = Filesystem::getContents($file); $content = Str::between($content, '---', '---'); $parsed = Yaml::parse($content) ?: []; $matter = new FrontMatter($parsed); - return [static::makeIdentifier($file) => $matter]; + return $matter; }); } @@ -82,14 +84,16 @@ public static function yaml(string $name): static */ public static function json(string $name, bool $asArray = false): static { - return static::discover($name, 'json', function (string $file) use ($asArray): array { - return [static::makeIdentifier($file) => json_decode(Filesystem::getContents($file), $asArray)]; + return static::discover($name, 'json', function (string $file) use ($asArray): stdClass|array { + return json_decode(Filesystem::getContents($file), $asArray); }); } - protected static function discover(string $name, array|string $extensions, callable $mapUsing): static + protected static function discover(string $name, array|string $extensions, callable $parseUsing): static { - return new static(static::findFiles($name, $extensions)->mapWithKeys($mapUsing)); + return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing): array { + return [static::makeIdentifier($file) => $parseUsing($file)]; + })); } protected static function findFiles(string $name, array|string $extensions): Collection From a80b3d41993d3e97c458005265fd140bbe288dae Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:25:58 +0200 Subject: [PATCH 036/118] Add null return type support pending https://github.com/hydephp/develop/issues/1736 --- packages/framework/src/Support/DataCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 7185e45e16c..ea1ccb468d2 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -84,7 +84,7 @@ public static function yaml(string $name): static */ public static function json(string $name, bool $asArray = false): static { - return static::discover($name, 'json', function (string $file) use ($asArray): stdClass|array { + return static::discover($name, 'json', function (string $file) use ($asArray): stdClass|array|null { return json_decode(Filesystem::getContents($file), $asArray); }); } From ca6f87d74e114ef73678a8db87f1b9d633556f8a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:28:11 +0200 Subject: [PATCH 037/118] Annotate helper method generics --- packages/framework/src/Support/DataCollection.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index ea1ccb468d2..37ba61fafe1 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -89,6 +89,11 @@ public static function json(string $name, bool $asArray = false): static }); } + /** + * @param array|string $extensions + * @param callable(string): mixed $parseUsing (string $file): mixed + * @return static + */ protected static function discover(string $name, array|string $extensions, callable $parseUsing): static { return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing): array { From 0b54e3d42c19cc4c48b7487f7a6e4efc9ad9b87b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:35:12 +0200 Subject: [PATCH 038/118] Cleanup PHPDoc comment --- packages/framework/src/Support/DataCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 37ba61fafe1..49d88816951 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -91,7 +91,7 @@ public static function json(string $name, bool $asArray = false): static /** * @param array|string $extensions - * @param callable(string): mixed $parseUsing (string $file): mixed + * @param callable(string): mixed $parseUsing * @return static */ protected static function discover(string $name, array|string $extensions, callable $parseUsing): static From c232397c8d455d0fa9bafb2912ce9ca43c3f9ddd Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:35:54 +0200 Subject: [PATCH 039/118] Annotate helper method generics --- packages/framework/src/Support/DataCollection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 49d88816951..daa1ff3a9f0 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -101,6 +101,10 @@ protected static function discover(string $name, array|string $extensions, calla })); } + /** + * @param array|string $extensions + * @return Collection + */ protected static function findFiles(string $name, array|string $extensions): Collection { return Filesystem::smartGlob(sprintf('%s/%s/*.{%s}', From 1edcc244ee52154469ca6e7f7a4ace098fa4eef2 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:36:33 +0200 Subject: [PATCH 040/118] Inline local variable --- packages/framework/src/Support/DataCollection.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index daa1ff3a9f0..9fe74bbce35 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -69,9 +69,8 @@ public static function yaml(string $name): static $content = Str::between($content, '---', '---'); $parsed = Yaml::parse($content) ?: []; - $matter = new FrontMatter($parsed); - return $matter; + return new FrontMatter($parsed); }); } From 308a9c11f718a4b9735e50f8da5650dea43550c0 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:36:58 +0200 Subject: [PATCH 041/118] Reorder newlines --- packages/framework/src/Support/DataCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 9fe74bbce35..40d3ec1974c 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -66,8 +66,8 @@ public static function yaml(string $name): static { return static::discover($name, ['yaml', 'yml'], function (string $file): FrontMatter { $content = Filesystem::getContents($file); - $content = Str::between($content, '---', '---'); + $content = Str::between($content, '---', '---'); $parsed = Yaml::parse($content) ?: []; return new FrontMatter($parsed); From c2f0faa3d6988e8ff86304225a7a58302f6d6b6d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 19:38:54 +0200 Subject: [PATCH 042/118] Normalize PHPDoc types --- packages/framework/src/Support/DataCollection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 40d3ec1974c..8804b99f552 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -46,7 +46,7 @@ class DataCollection extends Collection * * Each Markdown file will be parsed into a MarkdownDocument with front matter. * - * @return DataCollection + * @return static */ public static function markdown(string $name): static { @@ -60,7 +60,7 @@ public static function markdown(string $name): static * * Each YAML file will be parsed into a FrontMatter object. * - * @return DataCollection + * @return static */ public static function yaml(string $name): static { @@ -79,7 +79,7 @@ public static function yaml(string $name): static * * Each JSON file will be parsed into a stdClass object, or an associative array, depending on the second parameter. * - * @return DataCollection + * @return static */ public static function json(string $name, bool $asArray = false): static { From 24532a1f9fe01135ba84048d618c8992c0000c6b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 20:35:02 +0200 Subject: [PATCH 043/118] Introduce local variable --- packages/framework/src/Support/DataCollection.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 8804b99f552..bc8f534e2e5 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -84,7 +84,9 @@ public static function yaml(string $name): static public static function json(string $name, bool $asArray = false): static { return static::discover($name, 'json', function (string $file) use ($asArray): stdClass|array|null { - return json_decode(Filesystem::getContents($file), $asArray); + $contents = Filesystem::getContents($file); + + return json_decode($contents, $asArray); }); } From 25b4aeb9bba26549e6c9b7716247efd6a31f3b1b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 21:09:17 +0200 Subject: [PATCH 044/118] Update Data Collections to validate JSON data files Fixes https://github.com/hydephp/develop/issues/1736 --- RELEASE_NOTES.md | 7 +++ .../framework/src/Support/DataCollection.php | 7 ++- .../tests/Feature/DataCollectionTest.php | 8 +-- .../tests/Unit/DataCollectionUnitTest.php | 62 +++++++++++++++++++ 4 files changed, 79 insertions(+), 5 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 53fc95b9911..980fdd8e6dd 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -22,6 +22,7 @@ This serves two purposes: - Minor: Due to changes in the navigation system, it is possible that existing configuration files will need to be adjusted in order for menus to look the same (in terms of ordering etc.) - Minor: The documentation article component now supports disabling the semantic rendering using a falsy value in https://github.com/hydephp/develop/pull/1566 - Minor: Changed the default build task message to make it more concise in https://github.com/hydephp/develop/pull/1659 +- Minor: Data collection files are now validated for syntax errors during discovery in https://github.com/hydephp/develop/pull/1732 - The `hasFeature` method on the Hyde facade and HydeKernel now only accepts a Feature enum value instead of a string for its parameter. - Changed how the documentation search is generated, to be an `InMemoryPage` instead of a post-build task. - Media asset files are now copied using the new build task instead of the deprecated `BuildService::transferMediaAssets()` method. @@ -232,3 +233,9 @@ Unfortunately, this means that existing setups may need to be adjusted to work w #### Minor impact - Calling the `DataCollection` methods will no longer create the data collections directory automatically + +#### Issues that may arise + +If you start getting `InvalidArgumentException` when using the `DataCollection` class, it may be due to malformed data collection files. +Starting from this version, we validate the syntax of JSON and YAML files during discovery, to help you catch errors early. +See https://github.com/hydephp/develop/issues/1736 for more information. diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index bc8f534e2e5..85b0e78cfd1 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -6,6 +6,7 @@ use stdClass; use Hyde\Facades\Filesystem; +use InvalidArgumentException; use Symfony\Component\Yaml\Yaml; use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; @@ -83,9 +84,13 @@ public static function yaml(string $name): static */ public static function json(string $name, bool $asArray = false): static { - return static::discover($name, 'json', function (string $file) use ($asArray): stdClass|array|null { + return static::discover($name, 'json', function (string $file) use ($asArray): stdClass|array { $contents = Filesystem::getContents($file); + if (! json_validate($contents)) { + throw new InvalidArgumentException("Invalid JSON in file: '$file'"); + } + return json_decode($contents, $asArray); }); } diff --git a/packages/framework/tests/Feature/DataCollectionTest.php b/packages/framework/tests/Feature/DataCollectionTest.php index 9889472841e..70587bdd95d 100644 --- a/packages/framework/tests/Feature/DataCollectionTest.php +++ b/packages/framework/tests/Feature/DataCollectionTest.php @@ -56,11 +56,11 @@ public function testJsonCollections() { $this->directory('resources/collections/foo'); $this->file('resources/collections/foo/foo.json', json_encode(['foo' => 'bar'])); - $this->file('resources/collections/foo/bar.json'); + $this->file('resources/collections/foo/bar.json', '{"bar": "baz"}'); $this->assertEquals(new DataCollection([ 'foo/foo.json' => (object) ['foo' => 'bar'], - 'foo/bar.json' => null, + 'foo/bar.json' => (object) ['bar' => 'baz'], ]), DataCollection::json('foo')); } @@ -68,11 +68,11 @@ public function testJsonCollectionsAsArrays() { $this->directory('resources/collections/foo'); $this->file('resources/collections/foo/foo.json', json_encode(['foo' => 'bar'])); - $this->file('resources/collections/foo/bar.json'); + $this->file('resources/collections/foo/bar.json', '{"bar": "baz"}'); $this->assertEquals(new DataCollection([ 'foo/foo.json' => ['foo' => 'bar'], - 'foo/bar.json' => null, + 'foo/bar.json' => ['bar' => 'baz'], ]), DataCollection::json('foo', true)); } diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index b80b68cbb62..336bb209e12 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -6,6 +6,7 @@ use Hyde\Hyde; use Illuminate\Support\Str; +use InvalidArgumentException; use Hyde\Support\DataCollection; use Hyde\Testing\UnitTestCase; use Illuminate\Filesystem\Filesystem; @@ -242,6 +243,67 @@ public function testJsonMethodReturnsCollectionOfJsonDecodedArrays() ], MockableDataCollection::json('foo', true), true); } + public function testJsonMethodThrowsExceptionForInvalidJson() + { + MockableDataCollection::mockFiles([ + 'foo/bar.json' => '{"foo": "bar"', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json'"); + + MockableDataCollection::json('foo'); + } + + public function testJsonMethodThrowsExceptionForInvalidJsonWithArray() + { + MockableDataCollection::mockFiles([ + 'foo/bar.json' => '{"foo": "bar"', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json'"); + + MockableDataCollection::json('foo', true); + } + + public function testJsonMethodThrowsExceptionForEmptyJson() + { + MockableDataCollection::mockFiles([ + 'foo/bar.json' => '', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json'"); + + MockableDataCollection::json('foo'); + } + + public function testJsonMethodThrowsExceptionForBlankJson() + { + MockableDataCollection::mockFiles([ + 'foo/bar.json' => ' ', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json'"); + + MockableDataCollection::json('foo'); + } + + public function testJsonMethodThrowsExceptionWhenJustOneFileIsInvalid() + { + MockableDataCollection::mockFiles([ + 'foo/bar.json' => '{"foo": "bar"}', + 'foo/baz.json' => '{"foo": "baz"', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/baz.json'"); + + MockableDataCollection::json('foo'); + } + protected function assertFrontMatterCollectionStructure(array $expected, DataCollection $collection): void { $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); From ebebaa17e670f9fcfbe7db9cad97d469dca1fb66 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 21:11:59 +0200 Subject: [PATCH 045/118] Convert string interpolation to a 'sprintf()' call --- packages/framework/src/Support/DataCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 85b0e78cfd1..0065b32981b 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -88,7 +88,7 @@ public static function json(string $name, bool $asArray = false): static $contents = Filesystem::getContents($file); if (! json_validate($contents)) { - throw new InvalidArgumentException("Invalid JSON in file: '$file'"); + throw new InvalidArgumentException(sprintf("Invalid JSON in file: '%s'", $file)); } return json_decode($contents, $asArray); From 06dfada5a81b62f7ad61ab0fc7d1ed37f095ccb5 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 21:20:23 +0200 Subject: [PATCH 046/118] Display the error that caused the JSON decoding to fail --- .../framework/src/Support/DataCollection.php | 2 +- .../tests/Unit/DataCollectionUnitTest.php | 34 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 0065b32981b..fdea66b8867 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -88,7 +88,7 @@ public static function json(string $name, bool $asArray = false): static $contents = Filesystem::getContents($file); if (! json_validate($contents)) { - throw new InvalidArgumentException(sprintf("Invalid JSON in file: '%s'", $file)); + throw new InvalidArgumentException(sprintf("Invalid JSON in file: '%s' (%s)", $file, json_last_error_msg())); } return json_decode($contents, $asArray); diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 336bb209e12..7f3eab684f5 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -250,7 +250,7 @@ public function testJsonMethodThrowsExceptionForInvalidJson() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json'"); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); } @@ -262,7 +262,7 @@ public function testJsonMethodThrowsExceptionForInvalidJsonWithArray() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json'"); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo', true); } @@ -274,7 +274,7 @@ public function testJsonMethodThrowsExceptionForEmptyJson() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json'"); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); } @@ -286,7 +286,7 @@ public function testJsonMethodThrowsExceptionForBlankJson() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json'"); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); } @@ -299,7 +299,31 @@ public function testJsonMethodThrowsExceptionWhenJustOneFileIsInvalid() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/baz.json'"); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/baz.json' (Syntax error)"); + + MockableDataCollection::json('foo'); + } + + public function testJsonMethodThrowsExceptionForOtherReasonsThanSyntaxErrorWithUtfError() + { + MockableDataCollection::mockFiles([ + 'foo/utf.json' => "\xB1\x31", + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/utf.json' (Malformed UTF-8 characters, possibly incorrectly encoded)"); + + MockableDataCollection::json('foo'); + } + + public function testJsonMethodThrowsExceptionForOtherReasonsThanSyntaxErrorWithControlCharacterError() + { + MockableDataCollection::mockFiles([ + 'foo/control.json' => "\x19\x31", + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid JSON in file: 'foo/control.json' (Control character error, possibly incorrectly encoded)"); MockableDataCollection::json('foo'); } From 9702722e7413ca1e5b61ac7b47a6e447668e45f6 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 24 Jun 2024 21:22:49 +0200 Subject: [PATCH 047/118] Document example to help issue searching lead to upgrade path --- RELEASE_NOTES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 980fdd8e6dd..715166a87c5 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -239,3 +239,9 @@ Unfortunately, this means that existing setups may need to be adjusted to work w If you start getting `InvalidArgumentException` when using the `DataCollection` class, it may be due to malformed data collection files. Starting from this version, we validate the syntax of JSON and YAML files during discovery, to help you catch errors early. See https://github.com/hydephp/develop/issues/1736 for more information. + +For example, an empty or malformed JSON file will now throw an exception like this: + +```php +InvalidArgumentException: Invalid JSON in file: 'foo/baz.json' (Syntax error) +``` From b217795239460c06f5157a1cd01924d7e096b2f2 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 11:53:29 +0200 Subject: [PATCH 048/118] Throw a normalized exception for issues parsing Yaml This better matches the JSON exception throwing. See https://github.com/hydephp/develop/issues/1736 --- RELEASE_NOTES.md | 2 ++ .../framework/src/Support/DataCollection.php | 8 ++++- .../tests/Unit/DataCollectionUnitTest.php | 36 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 715166a87c5..73109113ac5 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -245,3 +245,5 @@ For example, an empty or malformed JSON file will now throw an exception like th ```php InvalidArgumentException: Invalid JSON in file: 'foo/baz.json' (Syntax error) ``` + +In order to normalize the thrown exceptions, we now rethrow Yaml `ParseException` as `InvalidArgumentException` to match the JSON validation. diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index fdea66b8867..21c7a728674 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -11,6 +11,7 @@ use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; use Hyde\Framework\Actions\MarkdownFileParser; +use Symfony\Component\Yaml\Exception\ParseException; use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -69,7 +70,12 @@ public static function yaml(string $name): static $content = Filesystem::getContents($file); $content = Str::between($content, '---', '---'); - $parsed = Yaml::parse($content) ?: []; + + try { + $parsed = Yaml::parse($content) ?: []; + } catch (ParseException $exception) { + throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, $exception->getMessage()), previous: $exception); + } return new FrontMatter($parsed); }); diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 7f3eab684f5..08b9ffc0404 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -217,6 +217,42 @@ public function testYamlCollectionsHandleLeadingAndTrailingNewlinesAndTrailingWh ], MockableDataCollection::yaml('foo')); } + public function testYamlCollectionsThrowExceptionForInvalidYaml() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => "---\nfoo: 'bar", + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid YAML in file: 'foo/bar.yml' (Malformed inline YAML string at line 2 (near \"foo: 'bar\").)"); + + MockableDataCollection::yaml('foo'); + } + + public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorWithUtfError() + { + MockableDataCollection::mockFiles([ + 'foo/utf.yml' => "foo: \xB1\x31", + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid YAML in file: 'foo/utf.yml' (The YAML value does not appear to be valid UTF-8.)"); + + MockableDataCollection::yaml('foo'); + } + + public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorWithTabsError() + { + MockableDataCollection::mockFiles([ + 'foo/tabs.yml' => "foo:\n\tbar", + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid YAML in file: 'foo/tabs.yml' (A YAML file cannot contain tabs as indentation at line 2 (near \" bar\").)"); + + MockableDataCollection::yaml('foo'); + } + public function testJsonMethodReturnsCollectionOfJsonDecodedObjects() { MockableDataCollection::mockFiles([ From d08b1873b0ef0083569073f4a77c9a2af9d71527 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:13:28 +0200 Subject: [PATCH 049/118] Throw parse exception for empty Yaml to match `json_validate` See https://github.com/hydephp/develop/issues/1736#issuecomment-2188298689 --- RELEASE_NOTES.md | 1 + .../framework/src/Support/DataCollection.php | 4 ++++ .../tests/Feature/DataCollectionTest.php | 2 -- .../tests/Unit/DataCollectionUnitTest.php | 24 +++++++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 73109113ac5..15cd68599a1 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -247,3 +247,4 @@ InvalidArgumentException: Invalid JSON in file: 'foo/baz.json' (Syntax error) ``` In order to normalize the thrown exceptions, we now rethrow Yaml `ParseException` as `InvalidArgumentException` to match the JSON validation. +Additionally, an exception will be thrown if a JSON or YAML file is empty, as this is unlikely to be intentional. diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 21c7a728674..a551eb60c37 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -72,6 +72,10 @@ public static function yaml(string $name): static $content = Str::between($content, '---', '---'); try { + if (blank(trim($content))) { + throw new ParseException('File is empty'); + } + $parsed = Yaml::parse($content) ?: []; } catch (ParseException $exception) { throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, $exception->getMessage()), previous: $exception); diff --git a/packages/framework/tests/Feature/DataCollectionTest.php b/packages/framework/tests/Feature/DataCollectionTest.php index 70587bdd95d..09ac73dacd0 100644 --- a/packages/framework/tests/Feature/DataCollectionTest.php +++ b/packages/framework/tests/Feature/DataCollectionTest.php @@ -33,12 +33,10 @@ public function testYamlCollections() $this->directory('resources/collections/foo'); $this->markdown('resources/collections/foo/foo.yaml', matter: ['title' => 'Foo']); $this->file('resources/collections/foo/bar.yml', "---\ntitle: Bar\n---"); - $this->file('resources/collections/foo/baz.yml'); $this->assertEquals(new DataCollection([ 'foo/foo.yaml' => new FrontMatter(['title' => 'Foo']), 'foo/bar.yml' => new FrontMatter(['title' => 'Bar']), - 'foo/baz.yml' => new FrontMatter([]), ]), DataCollection::yaml('foo')); } diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 08b9ffc0404..cb64171a241 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -229,6 +229,30 @@ public function testYamlCollectionsThrowExceptionForInvalidYaml() MockableDataCollection::yaml('foo'); } + public function testYamlCollectionsThrowExceptionForEmptyYaml() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => '', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid YAML in file: 'foo/bar.yml' (File is empty)"); + + MockableDataCollection::yaml('foo'); + } + + public function testYamlCollectionsThrowExceptionForBlankYaml() + { + MockableDataCollection::mockFiles([ + 'foo/bar.yml' => ' ', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid YAML in file: 'foo/bar.yml' (File is empty)"); + + MockableDataCollection::yaml('foo'); + } + public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorWithUtfError() { MockableDataCollection::mockFiles([ From f93cb0eec23bd2cfb27f1a31221bf23f96573a29 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:19:07 +0200 Subject: [PATCH 050/118] Document code reasoning --- packages/framework/src/Support/DataCollection.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index a551eb60c37..2559eadd7ed 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -73,6 +73,7 @@ public static function yaml(string $name): static try { if (blank(trim($content))) { + // We throw an exception here in order to match the behavior of the JSON validation. throw new ParseException('File is empty'); } From b0d1e224072791a12e98eeaadf986eaf88e8b2ee Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:26:16 +0200 Subject: [PATCH 051/118] Remove unreachable null coalesce --- packages/framework/src/Support/DataCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 2559eadd7ed..98b2d3f735f 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -77,7 +77,7 @@ public static function yaml(string $name): static throw new ParseException('File is empty'); } - $parsed = Yaml::parse($content) ?: []; + $parsed = Yaml::parse($content); } catch (ParseException $exception) { throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, $exception->getMessage()), previous: $exception); } From 65aadb5b6ad20871d292e1b15b232db93a2ef70b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:27:52 +0200 Subject: [PATCH 052/118] Trim trailing periods to make message fit in better --- packages/framework/src/Support/DataCollection.php | 2 +- packages/framework/tests/Unit/DataCollectionUnitTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 98b2d3f735f..443ce5831b9 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -79,7 +79,7 @@ public static function yaml(string $name): static $parsed = Yaml::parse($content); } catch (ParseException $exception) { - throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, $exception->getMessage()), previous: $exception); + throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); } return new FrontMatter($parsed); diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index cb64171a241..20de280cb73 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -224,7 +224,7 @@ public function testYamlCollectionsThrowExceptionForInvalidYaml() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid YAML in file: 'foo/bar.yml' (Malformed inline YAML string at line 2 (near \"foo: 'bar\").)"); + $this->expectExceptionMessage("Invalid YAML in file: 'foo/bar.yml' (Malformed inline YAML string at line 2 (near \"foo: 'bar\"))"); MockableDataCollection::yaml('foo'); } @@ -260,7 +260,7 @@ public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorW ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid YAML in file: 'foo/utf.yml' (The YAML value does not appear to be valid UTF-8.)"); + $this->expectExceptionMessage("Invalid YAML in file: 'foo/utf.yml' (The YAML value does not appear to be valid UTF-8)"); MockableDataCollection::yaml('foo'); } @@ -272,7 +272,7 @@ public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorW ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid YAML in file: 'foo/tabs.yml' (A YAML file cannot contain tabs as indentation at line 2 (near \" bar\").)"); + $this->expectExceptionMessage("Invalid YAML in file: 'foo/tabs.yml' (A YAML file cannot contain tabs as indentation at line 2 (near \" bar\"))"); MockableDataCollection::yaml('foo'); } From d4c970e947b98cb417fd8e2038e4c85cfcaa7c8c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:28:46 +0200 Subject: [PATCH 053/118] Import used functions --- packages/framework/src/Support/DataCollection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 443ce5831b9..96549f18d9f 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -15,10 +15,14 @@ use Illuminate\Support\Collection; use Illuminate\Support\Str; +use function trim; +use function blank; +use function rtrim; use function implode; use function json_decode; use function sprintf; use function unslash; +use function json_last_error_msg; /** * Automatically generates Laravel Collections from static data files, From 9f52220f8e2db60f297ad27b80987c55583b5a2d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:31:49 +0200 Subject: [PATCH 054/118] Extract helper methods responsible for parsing each file --- .../framework/src/Support/DataCollection.php | 63 ++++++++++++------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 96549f18d9f..375a6f978ad 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -57,7 +57,7 @@ class DataCollection extends Collection public static function markdown(string $name): static { return static::discover($name, 'md', function (string $file): MarkdownDocument { - return MarkdownFileParser::parse($file); + return static::parseMarkdownFile($file); }); } @@ -71,22 +71,7 @@ public static function markdown(string $name): static public static function yaml(string $name): static { return static::discover($name, ['yaml', 'yml'], function (string $file): FrontMatter { - $content = Filesystem::getContents($file); - - $content = Str::between($content, '---', '---'); - - try { - if (blank(trim($content))) { - // We throw an exception here in order to match the behavior of the JSON validation. - throw new ParseException('File is empty'); - } - - $parsed = Yaml::parse($content); - } catch (ParseException $exception) { - throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); - } - - return new FrontMatter($parsed); + return static::parseYamlFile($file); }); } @@ -100,13 +85,7 @@ public static function yaml(string $name): static public static function json(string $name, bool $asArray = false): static { return static::discover($name, 'json', function (string $file) use ($asArray): stdClass|array { - $contents = Filesystem::getContents($file); - - if (! json_validate($contents)) { - throw new InvalidArgumentException(sprintf("Invalid JSON in file: '%s' (%s)", $file, json_last_error_msg())); - } - - return json_decode($contents, $asArray); + return static::parseJsonFile($file, $asArray); }); } @@ -137,4 +116,40 @@ protected static function makeIdentifier(string $path): string { return unslash(Str::after($path, static::$sourceDirectory)); } + + protected static function parseMarkdownFile(string $file): MarkdownDocument + { + return MarkdownFileParser::parse($file); + } + + protected static function parseYamlFile(string $file): FrontMatter + { + $content = Filesystem::getContents($file); + + $content = Str::between($content, '---', '---'); + + try { + if (blank(trim($content))) { + // We throw an exception here in order to match the behavior of the JSON validation. + throw new ParseException('File is empty'); + } + + $parsed = Yaml::parse($content); + } catch (ParseException $exception) { + throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); + } + + return new FrontMatter($parsed); + } + + protected static function parseJsonFile(string $file, bool $asArray): stdClass|array + { + $contents = Filesystem::getContents($file); + + if (! json_validate($contents)) { + throw new InvalidArgumentException(sprintf("Invalid JSON in file: '%s' (%s)", $file, json_last_error_msg())); + } + + return json_decode($contents, $asArray); + } } From 2d2cfe8c42cde32c7d1f2bb373e3a220437080d7 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:33:04 +0200 Subject: [PATCH 055/118] Convert simplified closures to arrow functions --- packages/framework/src/Support/DataCollection.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 375a6f978ad..b6a405409ea 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -56,9 +56,7 @@ class DataCollection extends Collection */ public static function markdown(string $name): static { - return static::discover($name, 'md', function (string $file): MarkdownDocument { - return static::parseMarkdownFile($file); - }); + return static::discover($name, 'md', fn (string $file): MarkdownDocument => static::parseMarkdownFile($file)); } /** @@ -70,9 +68,7 @@ public static function markdown(string $name): static */ public static function yaml(string $name): static { - return static::discover($name, ['yaml', 'yml'], function (string $file): FrontMatter { - return static::parseYamlFile($file); - }); + return static::discover($name, ['yaml', 'yml'], fn (string $file): FrontMatter => static::parseYamlFile($file)); } /** @@ -84,9 +80,7 @@ public static function yaml(string $name): static */ public static function json(string $name, bool $asArray = false): static { - return static::discover($name, 'json', function (string $file) use ($asArray): stdClass|array { - return static::parseJsonFile($file, $asArray); - }); + return static::discover($name, 'json', fn (string $file): stdClass|array => static::parseJsonFile($file, $asArray)); } /** From a99947a13b77b7e6d0cecabc9633921d5851ece5 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:35:49 +0200 Subject: [PATCH 056/118] Document throws --- packages/framework/src/Support/DataCollection.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index b6a405409ea..5d5bce3a0f0 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -116,6 +116,7 @@ protected static function parseMarkdownFile(string $file): MarkdownDocument return MarkdownFileParser::parse($file); } + /** @throws InvalidArgumentException If the YAML is invalid and cannot be parsed. */ protected static function parseYamlFile(string $file): FrontMatter { $content = Filesystem::getContents($file); @@ -136,6 +137,7 @@ protected static function parseYamlFile(string $file): FrontMatter return new FrontMatter($parsed); } + /** @throws InvalidArgumentException If the JSON is invalid and cannot be parsed. */ protected static function parseJsonFile(string $file, bool $asArray): stdClass|array { $contents = Filesystem::getContents($file); From df820efa5af9bd304c6abbc19c7d8a7039c37c09 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:37:50 +0200 Subject: [PATCH 057/118] Move return into try/catch block Makes the intended execution flow clearer --- packages/framework/src/Support/DataCollection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 5d5bce3a0f0..4497f0043ee 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -130,11 +130,11 @@ protected static function parseYamlFile(string $file): FrontMatter } $parsed = Yaml::parse($content); + + return new FrontMatter($parsed); } catch (ParseException $exception) { throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); } - - return new FrontMatter($parsed); } /** @throws InvalidArgumentException If the JSON is invalid and cannot be parsed. */ From 9b33cfd1ee464d83d775de65c345769926dbf479 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:38:06 +0200 Subject: [PATCH 058/118] Inline local variable --- packages/framework/src/Support/DataCollection.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 4497f0043ee..afde91abdac 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -129,9 +129,7 @@ protected static function parseYamlFile(string $file): FrontMatter throw new ParseException('File is empty'); } - $parsed = Yaml::parse($content); - - return new FrontMatter($parsed); + return new FrontMatter(Yaml::parse($content)); } catch (ParseException $exception) { throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); } From b933be67e91f275a29f66a775996fdaa62c5073a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:38:18 +0200 Subject: [PATCH 059/118] Cleanup formatting --- packages/framework/src/Support/DataCollection.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index afde91abdac..8fffc214795 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -120,7 +120,6 @@ protected static function parseMarkdownFile(string $file): MarkdownDocument protected static function parseYamlFile(string $file): FrontMatter { $content = Filesystem::getContents($file); - $content = Str::between($content, '---', '---'); try { From fc67b1178a364f321b4f028add769d265f2a395e Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:41:35 +0200 Subject: [PATCH 060/118] Use first class callable syntax where possible --- packages/framework/src/Support/DataCollection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 8fffc214795..e0a5dcef0fc 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -56,7 +56,7 @@ class DataCollection extends Collection */ public static function markdown(string $name): static { - return static::discover($name, 'md', fn (string $file): MarkdownDocument => static::parseMarkdownFile($file)); + return static::discover($name, 'md', static::parseMarkdownFile(...)); } /** @@ -68,7 +68,7 @@ public static function markdown(string $name): static */ public static function yaml(string $name): static { - return static::discover($name, ['yaml', 'yml'], fn (string $file): FrontMatter => static::parseYamlFile($file)); + return static::discover($name, ['yaml', 'yml'], static::parseYamlFile(...)); } /** From 88a92042295302ce208c00c9c2cdda67e19ff0c1 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:43:54 +0200 Subject: [PATCH 061/118] Support passing extra arguments to discover method In order to support first class callables --- packages/framework/src/Support/DataCollection.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index e0a5dcef0fc..704e48a9c6b 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -80,7 +80,7 @@ public static function yaml(string $name): static */ public static function json(string $name, bool $asArray = false): static { - return static::discover($name, 'json', fn (string $file): stdClass|array => static::parseJsonFile($file, $asArray)); + return static::discover($name, 'json', static::parseJsonFile(...), [$asArray]); } /** @@ -88,10 +88,10 @@ public static function json(string $name, bool $asArray = false): static * @param callable(string): mixed $parseUsing * @return static */ - protected static function discover(string $name, array|string $extensions, callable $parseUsing): static + protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $args = []): static { - return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing): array { - return [static::makeIdentifier($file) => $parseUsing($file)]; + return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing, $args): array { + return [static::makeIdentifier($file) => $parseUsing($file, ...$args)]; })); } From dc26d8826d26ebda2d38ba46b3e072b4271fc658 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:44:29 +0200 Subject: [PATCH 062/118] Clarify parameter name --- packages/framework/src/Support/DataCollection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 704e48a9c6b..9e6ea8c80d2 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -88,10 +88,10 @@ public static function json(string $name, bool $asArray = false): static * @param callable(string): mixed $parseUsing * @return static */ - protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $args = []): static + protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $extraArgs = []): static { - return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing, $args): array { - return [static::makeIdentifier($file) => $parseUsing($file, ...$args)]; + return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing, $extraArgs): array { + return [static::makeIdentifier($file) => $parseUsing($file, ...$extraArgs)]; })); } From ea04ed1448bb3fa8b1f4a204ce21c4c42515d94d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:45:31 +0200 Subject: [PATCH 063/118] Narrow down mixed generic return type to actual supported types --- packages/framework/src/Support/DataCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 9e6ea8c80d2..a709d76cce8 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -86,7 +86,7 @@ public static function json(string $name, bool $asArray = false): static /** * @param array|string $extensions * @param callable(string): mixed $parseUsing - * @return static + * @return static */ protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $extraArgs = []): static { From 670dd7aeeb4b7482dea96fd0039f16bac5e4212a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:53:24 +0200 Subject: [PATCH 064/118] Extract testing helper --- .../tests/Unit/DataCollectionUnitTest.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 20de280cb73..313147c5fea 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -112,14 +112,10 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocuments() 'foo/baz.md' => 'baz', ]); - $collection = MockableDataCollection::markdown('foo'); - - $this->assertContainsOnlyInstancesOf(MarkdownDocument::class, $collection); - - $this->assertSame([ + $this->asserMarkdownCollectionStructure([ 'foo/bar.md' => 'bar', 'foo/baz.md' => 'baz', - ], $collection->map(fn ($value) => (string) $value)->all()); + ], MockableDataCollection::markdown('foo')); } public function testYamlMethodReturnsCollectionOfFrontMatterObjects() @@ -388,6 +384,13 @@ public function testJsonMethodThrowsExceptionForOtherReasonsThanSyntaxErrorWithC MockableDataCollection::json('foo'); } + protected function asserMarkdownCollectionStructure(array $expected, DataCollection $collection): void + { + $this->assertContainsOnlyInstancesOf(MarkdownDocument::class, $collection); + + $this->assertSame($expected, $collection->map(fn ($value) => (string) $value)->all()); + } + protected function assertFrontMatterCollectionStructure(array $expected, DataCollection $collection): void { $this->assertContainsOnlyInstancesOf(FrontMatter::class, $collection); From e32f6f8508cecd6e9fb9f9541011d660a57145d9 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 12:58:17 +0200 Subject: [PATCH 065/118] Update testing helper to compare front matter --- .../tests/Unit/DataCollectionUnitTest.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 313147c5fea..204b4713a1b 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -388,7 +388,21 @@ protected function asserMarkdownCollectionStructure(array $expected, DataCollect { $this->assertContainsOnlyInstancesOf(MarkdownDocument::class, $collection); - $this->assertSame($expected, $collection->map(fn ($value) => (string) $value)->all()); + if ($collection->contains(fn (MarkdownDocument $document) => filled($document->matter()->toArray()))) { + $expected = collect($expected)->map(fn ($value) => [ + 'matter' => $value['matter'], + 'content' => $value['content'], + ])->all(); + + $collection = $collection->map(fn (MarkdownDocument $document) => [ + 'matter' => $document->matter()->toArray(), + 'content' => $document->markdown()->body(), + ]); + + $this->assertSame($expected, $collection->all()); + } else { + $this->assertSame($expected, $collection->map(fn ($value) => (string) $value)->all()); + } } protected function assertFrontMatterCollectionStructure(array $expected, DataCollection $collection): void From 6c1497b321bbadb5f9f3819ceb8fecf623212c78 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 13:00:08 +0200 Subject: [PATCH 066/118] Support mixed states --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 204b4713a1b..ca9fe6d3c77 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -389,10 +389,10 @@ protected function asserMarkdownCollectionStructure(array $expected, DataCollect $this->assertContainsOnlyInstancesOf(MarkdownDocument::class, $collection); if ($collection->contains(fn (MarkdownDocument $document) => filled($document->matter()->toArray()))) { - $expected = collect($expected)->map(fn ($value) => [ + $expected = collect($expected)->map(fn ($value) => is_array($value) ? [ 'matter' => $value['matter'], 'content' => $value['content'], - ])->all(); + ] : (string) $value)->all(); $collection = $collection->map(fn (MarkdownDocument $document) => [ 'matter' => $document->matter()->toArray(), From 8be9527a9cd55396c0561c160196bd9e58b8f55e Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 13:00:20 +0200 Subject: [PATCH 067/118] Test Markdown collections with front matter --- .../tests/Unit/DataCollectionUnitTest.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index ca9fe6d3c77..9666bea8426 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -118,6 +118,44 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocuments() ], MockableDataCollection::markdown('foo')); } + public function testMarkdownMethodReturnsCollectionOfMarkdownDocumentsWithFrontMatter() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => "---\nfoo: bar\n---\nbar", + 'foo/baz.md' => "---\nfoo: baz\n---\nbaz", + ]); + + $this->asserMarkdownCollectionStructure([ + 'foo/bar.md' => [ + 'matter' => ['foo' => 'bar'], + 'content' => 'bar', + ], + 'foo/baz.md' => [ + 'matter' => ['foo' => 'baz'], + 'content' => 'baz', + ], + ], MockableDataCollection::markdown('foo')); + } + + public function testMarkdownMethodReturnsCollectionOfMarkdownDocumentsWithOnlyOneHavingFrontMatter() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => 'bar', + 'foo/baz.md' => "---\nfoo: baz\n---\nbaz", + ]); + + $this->asserMarkdownCollectionStructure([ + 'foo/bar.md' => [ + 'matter' => [], + 'content' => 'bar', + ], + 'foo/baz.md' => [ + 'matter' => ['foo' => 'baz'], + 'content' => 'baz', + ], + ], MockableDataCollection::markdown('foo')); + } + public function testYamlMethodReturnsCollectionOfFrontMatterObjects() { MockableDataCollection::mockFiles([ From bde59d8c9683de7cb119213015e510fc33dca8ed Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 13:07:03 +0200 Subject: [PATCH 068/118] Add edge case tests --- .../tests/Unit/DataCollectionUnitTest.php | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 9666bea8426..ee03c4cff40 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -156,6 +156,75 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocumentsWithOnlyOn ], MockableDataCollection::markdown('foo')); } + public function testMarkdownMethodWithEmptyFrontMatter() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => "---\n---\nbar", + ]); + + $this->asserMarkdownCollectionStructure([ + 'foo/bar.md' => 'bar', + ], MockableDataCollection::markdown('foo')); + } + + public function testMarkdownMethodWithEmptyFrontMatterAndContent() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => "---\n---", + ]); + + $this->asserMarkdownCollectionStructure([ + 'foo/bar.md' => '', + ], MockableDataCollection::markdown('foo')); + } + + public function testMarkdownMethodWithEmptyContent() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => "---\nfoo: bar\n---", + ]); + + $this->asserMarkdownCollectionStructure([ + 'foo/bar.md' => [ + 'matter' => ['foo' => 'bar'], + 'content' => '', + ], + ], MockableDataCollection::markdown('foo')); + } + + public function testMarkdownMethodWithEmptyFile() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => '', + ]); + + $this->asserMarkdownCollectionStructure([ + 'foo/bar.md' => '', + ], MockableDataCollection::markdown('foo')); + } + + public function testMarkdownMethodWithUnterminatedFrontMatter() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => "---\nfoo: bar\nbar", + ]); + + $this->asserMarkdownCollectionStructure([ + 'foo/bar.md' => "---\nfoo: bar\nbar", + ], MockableDataCollection::markdown('foo')); + } + + public function testMarkdownMethodWithUninitializedFrontMatter() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => "foo: bar\n---\nbar", + ]); + + $this->asserMarkdownCollectionStructure([ + 'foo/bar.md' => "foo: bar\n---\nbar", + ], MockableDataCollection::markdown('foo')); + } + public function testYamlMethodReturnsCollectionOfFrontMatterObjects() { MockableDataCollection::mockFiles([ From 11a9205e99b1c6cdf5ad49b62e628bf7639e5448 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 13:07:33 +0200 Subject: [PATCH 069/118] Fix typo in testing helper name --- .../tests/Unit/DataCollectionUnitTest.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index ee03c4cff40..5566c291862 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -112,7 +112,7 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocuments() 'foo/baz.md' => 'baz', ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => 'bar', 'foo/baz.md' => 'baz', ], MockableDataCollection::markdown('foo')); @@ -125,7 +125,7 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocumentsWithFrontM 'foo/baz.md' => "---\nfoo: baz\n---\nbaz", ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => [ 'matter' => ['foo' => 'bar'], 'content' => 'bar', @@ -144,7 +144,7 @@ public function testMarkdownMethodReturnsCollectionOfMarkdownDocumentsWithOnlyOn 'foo/baz.md' => "---\nfoo: baz\n---\nbaz", ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => [ 'matter' => [], 'content' => 'bar', @@ -162,7 +162,7 @@ public function testMarkdownMethodWithEmptyFrontMatter() 'foo/bar.md' => "---\n---\nbar", ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => 'bar', ], MockableDataCollection::markdown('foo')); } @@ -173,7 +173,7 @@ public function testMarkdownMethodWithEmptyFrontMatterAndContent() 'foo/bar.md' => "---\n---", ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => '', ], MockableDataCollection::markdown('foo')); } @@ -184,7 +184,7 @@ public function testMarkdownMethodWithEmptyContent() 'foo/bar.md' => "---\nfoo: bar\n---", ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => [ 'matter' => ['foo' => 'bar'], 'content' => '', @@ -198,7 +198,7 @@ public function testMarkdownMethodWithEmptyFile() 'foo/bar.md' => '', ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => '', ], MockableDataCollection::markdown('foo')); } @@ -209,7 +209,7 @@ public function testMarkdownMethodWithUnterminatedFrontMatter() 'foo/bar.md' => "---\nfoo: bar\nbar", ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => "---\nfoo: bar\nbar", ], MockableDataCollection::markdown('foo')); } @@ -220,7 +220,7 @@ public function testMarkdownMethodWithUninitializedFrontMatter() 'foo/bar.md' => "foo: bar\n---\nbar", ]); - $this->asserMarkdownCollectionStructure([ + $this->assertMarkdownCollectionStructure([ 'foo/bar.md' => "foo: bar\n---\nbar", ], MockableDataCollection::markdown('foo')); } @@ -491,7 +491,7 @@ public function testJsonMethodThrowsExceptionForOtherReasonsThanSyntaxErrorWithC MockableDataCollection::json('foo'); } - protected function asserMarkdownCollectionStructure(array $expected, DataCollection $collection): void + protected function assertMarkdownCollectionStructure(array $expected, DataCollection $collection): void { $this->assertContainsOnlyInstancesOf(MarkdownDocument::class, $collection); From 30c2a7425332f7b57d3ca77f4454c1aa6dccc0db Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 13:08:07 +0200 Subject: [PATCH 070/118] Test Markdown method with invalid front matter --- .../framework/tests/Unit/DataCollectionUnitTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 5566c291862..07d1a408727 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -14,6 +14,7 @@ use Mockery; use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; +use Symfony\Component\Yaml\Exception\ParseException; /** * @covers \Hyde\Support\DataCollection @@ -225,6 +226,17 @@ public function testMarkdownMethodWithUninitializedFrontMatter() ], MockableDataCollection::markdown('foo')); } + public function testMarkdownMethodWithInvalidFrontMatter() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => "---\nfoo: 'bar\n---\nbar", + ]); + + $this->expectException(ParseException::class); + + MockableDataCollection::markdown('foo'); + } + public function testYamlMethodReturnsCollectionOfFrontMatterObjects() { MockableDataCollection::mockFiles([ From 9ca7a795f37817d84a35e7e245a30ba7769fcb8b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 13:13:18 +0200 Subject: [PATCH 071/118] Normalize exception handling for Markdown data files --- packages/framework/src/Support/DataCollection.php | 7 ++++++- packages/framework/tests/Unit/DataCollectionUnitTest.php | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index a709d76cce8..f72f471aa82 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -111,9 +111,14 @@ protected static function makeIdentifier(string $path): string return unslash(Str::after($path, static::$sourceDirectory)); } + /** @throws InvalidArgumentException If the Markdown is invalid and cannot be parsed. */ protected static function parseMarkdownFile(string $file): MarkdownDocument { - return MarkdownFileParser::parse($file); + try { + return MarkdownFileParser::parse($file); + } catch (ParseException $exception) { + throw new InvalidArgumentException(sprintf("Invalid Markdown in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); + } } /** @throws InvalidArgumentException If the YAML is invalid and cannot be parsed. */ diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 07d1a408727..097712f0490 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -14,7 +14,6 @@ use Mockery; use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; -use Symfony\Component\Yaml\Exception\ParseException; /** * @covers \Hyde\Support\DataCollection @@ -232,7 +231,8 @@ public function testMarkdownMethodWithInvalidFrontMatter() 'foo/bar.md' => "---\nfoo: 'bar\n---\nbar", ]); - $this->expectException(ParseException::class); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid Markdown in file: 'foo/bar.md' (Malformed inline YAML string at line 1 (near \"foo: 'bar\"))"); MockableDataCollection::markdown('foo'); } From 4ecabc9a581a439e559a9ebfbb00e7a835cf5b7b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 14:14:25 +0200 Subject: [PATCH 072/118] Throw an exception when Markdown data file is empty And no front matter is set --- .../framework/src/Support/DataCollection.php | 8 ++++- .../tests/Feature/DataCollectionTest.php | 32 +++++++++---------- .../tests/Unit/DataCollectionUnitTest.php | 20 ++++++------ 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index f72f471aa82..995c44bb174 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -115,7 +115,13 @@ protected static function makeIdentifier(string $path): string protected static function parseMarkdownFile(string $file): MarkdownDocument { try { - return MarkdownFileParser::parse($file); + $document = MarkdownFileParser::parse($file); + + if (blank(trim($document->markdown()->body())) && $document->matter()->toArray() === []) { + throw new ParseException('File is empty'); + } + + return $document; } catch (ParseException $exception) { throw new InvalidArgumentException(sprintf("Invalid Markdown in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); } diff --git a/packages/framework/tests/Feature/DataCollectionTest.php b/packages/framework/tests/Feature/DataCollectionTest.php index 09ac73dacd0..7577da7de8a 100644 --- a/packages/framework/tests/Feature/DataCollectionTest.php +++ b/packages/framework/tests/Feature/DataCollectionTest.php @@ -20,11 +20,11 @@ public function testMarkdownCollections() { $this->directory('resources/collections/foo'); $this->markdown('resources/collections/foo/foo.md', 'Hello World', ['title' => 'Foo']); - $this->file('resources/collections/foo/bar.md'); + $this->file('resources/collections/foo/bar.md', 'Foo'); $this->assertEquals(new DataCollection([ 'foo/foo.md' => new MarkdownDocument(['title' => 'Foo'], 'Hello World'), - 'foo/bar.md' => new MarkdownDocument([], ''), + 'foo/bar.md' => new MarkdownDocument([], 'Foo'), ]), DataCollection::markdown('foo')); } @@ -91,8 +91,8 @@ public function testFindMarkdownFilesMethodReturnsEmptyArrayIfNoFilesAreFoundInS public function testFindMarkdownFilesMethodReturnsAnArrayOfMarkdownFilesInTheSpecifiedDirectory() { $this->directory('resources/collections/foo'); - $this->file('resources/collections/foo/foo.md'); - $this->file('resources/collections/foo/bar.md'); + $this->file('resources/collections/foo/foo.md', 'Foo'); + $this->file('resources/collections/foo/bar.md', 'Bar'); $this->assertSame([ 'foo/bar.md', @@ -104,8 +104,8 @@ public function testFindMarkdownFilesMethodDoesNotIncludeFilesInSubdirectories() { $this->directory('resources/collections/foo'); $this->directory('resources/collections/foo/bar'); - $this->file('resources/collections/foo/foo.md'); - $this->file('resources/collections/foo/bar/bar.md'); + $this->file('resources/collections/foo/foo.md', 'Foo'); + $this->file('resources/collections/foo/bar/bar.md', 'Bar'); $this->assertSame([ 'foo/foo.md', @@ -115,8 +115,8 @@ public function testFindMarkdownFilesMethodDoesNotIncludeFilesInSubdirectories() public function testFindMarkdownFilesMethodDoesNotIncludeFilesWithExtensionsOtherThanMd() { $this->directory('resources/collections/foo'); - $this->file('resources/collections/foo/foo.md'); - $this->file('resources/collections/foo/bar.txt'); + $this->file('resources/collections/foo/foo.md', 'Foo'); + $this->file('resources/collections/foo/bar.txt', 'Bar'); $this->assertSame([ 'foo/foo.md', @@ -126,7 +126,7 @@ public function testFindMarkdownFilesMethodDoesNotIncludeFilesWithExtensionsOthe public function testFindMarkdownFilesMethodDoesNotRemoveFilesStartingWithAnUnderscore() { $this->directory('resources/collections/foo'); - $this->file('resources/collections/foo/_foo.md'); + $this->file('resources/collections/foo/_foo.md', 'Foo'); $this->assertSame([ 'foo/_foo.md', @@ -136,20 +136,20 @@ public function testFindMarkdownFilesMethodDoesNotRemoveFilesStartingWithAnUnder public function testStaticMarkdownHelperDiscoversAndParsesMarkdownFilesInTheSpecifiedDirectory() { $this->directory('resources/collections/foo'); - $this->file('resources/collections/foo/foo.md'); - $this->file('resources/collections/foo/bar.md'); + $this->file('resources/collections/foo/foo.md', 'Foo'); + $this->file('resources/collections/foo/bar.md', 'Bar'); $this->assertEquals([ - 'foo/foo.md' => new MarkdownDocument([], ''), - 'foo/bar.md' => new MarkdownDocument([], ''), + 'foo/foo.md' => new MarkdownDocument([], 'Foo'), + 'foo/bar.md' => new MarkdownDocument([], 'Bar'), ], DataCollection::markdown('foo')->toArray()); } public function testStaticMarkdownHelperDoestNotIgnoreFilesStartingWithAnUnderscore() { $this->directory('resources/collections/foo'); - $this->file('resources/collections/foo/foo.md'); - $this->file('resources/collections/foo/_bar.md'); + $this->file('resources/collections/foo/foo.md', 'Foo'); + $this->file('resources/collections/foo/_bar.md', 'Bar'); $this->assertCount(2, DataCollection::markdown('foo')); } @@ -158,7 +158,7 @@ public function testSourceDirectoryCanBeChanged() { DataCollection::$sourceDirectory = 'foo'; $this->directory('foo/bar'); - $this->file('foo/bar/foo.md'); + $this->file('foo/bar/foo.md', 'Foo'); $this->assertSame([ 'bar/foo.md', diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 097712f0490..696f6fbfdbf 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -167,18 +167,19 @@ public function testMarkdownMethodWithEmptyFrontMatter() ], MockableDataCollection::markdown('foo')); } - public function testMarkdownMethodWithEmptyFrontMatterAndContent() + public function testMarkdownMethodWithEmptyFrontMatterAndContentThrowsException() { MockableDataCollection::mockFiles([ 'foo/bar.md' => "---\n---", ]); - $this->assertMarkdownCollectionStructure([ - 'foo/bar.md' => '', - ], MockableDataCollection::markdown('foo')); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid Markdown in file: 'foo/bar.md' (File is empty)"); + + MockableDataCollection::markdown('foo'); } - public function testMarkdownMethodWithEmptyContent() + public function testMarkdownMethodWithEmptyContentIsAcceptableIfFrontMatterIsSet() { MockableDataCollection::mockFiles([ 'foo/bar.md' => "---\nfoo: bar\n---", @@ -192,15 +193,16 @@ public function testMarkdownMethodWithEmptyContent() ], MockableDataCollection::markdown('foo')); } - public function testMarkdownMethodWithEmptyFile() + public function testMarkdownMethodWithEmptyFileThrowsException() { MockableDataCollection::mockFiles([ 'foo/bar.md' => '', ]); - $this->assertMarkdownCollectionStructure([ - 'foo/bar.md' => '', - ], MockableDataCollection::markdown('foo')); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid Markdown in file: 'foo/bar.md' (File is empty)"); + + MockableDataCollection::markdown('foo'); } public function testMarkdownMethodWithUnterminatedFrontMatter() From 005dc15f0a1a863505eb97a54fdeb3883f458dad Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 14:14:44 +0200 Subject: [PATCH 073/118] Reorder test methods --- .../tests/Unit/DataCollectionUnitTest.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 696f6fbfdbf..b8bcc94aae8 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -167,6 +167,18 @@ public function testMarkdownMethodWithEmptyFrontMatter() ], MockableDataCollection::markdown('foo')); } + public function testMarkdownMethodWithEmptyFileThrowsException() + { + MockableDataCollection::mockFiles([ + 'foo/bar.md' => '', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid Markdown in file: 'foo/bar.md' (File is empty)"); + + MockableDataCollection::markdown('foo'); + } + public function testMarkdownMethodWithEmptyFrontMatterAndContentThrowsException() { MockableDataCollection::mockFiles([ @@ -193,18 +205,6 @@ public function testMarkdownMethodWithEmptyContentIsAcceptableIfFrontMatterIsSet ], MockableDataCollection::markdown('foo')); } - public function testMarkdownMethodWithEmptyFileThrowsException() - { - MockableDataCollection::mockFiles([ - 'foo/bar.md' => '', - ]); - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid Markdown in file: 'foo/bar.md' (File is empty)"); - - MockableDataCollection::markdown('foo'); - } - public function testMarkdownMethodWithUnterminatedFrontMatter() { MockableDataCollection::mockFiles([ From ab762b917524d63e98cbb002ff9c32d337d57695 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 14:15:13 +0200 Subject: [PATCH 074/118] Remove unnecessary trim already run in blank function --- packages/framework/src/Support/DataCollection.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 995c44bb174..26f1d064780 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -15,7 +15,6 @@ use Illuminate\Support\Collection; use Illuminate\Support\Str; -use function trim; use function blank; use function rtrim; use function implode; @@ -117,7 +116,7 @@ protected static function parseMarkdownFile(string $file): MarkdownDocument try { $document = MarkdownFileParser::parse($file); - if (blank(trim($document->markdown()->body())) && $document->matter()->toArray() === []) { + if (blank($document->markdown()->body()) && $document->matter()->toArray() === []) { throw new ParseException('File is empty'); } @@ -134,7 +133,7 @@ protected static function parseYamlFile(string $file): FrontMatter $content = Str::between($content, '---', '---'); try { - if (blank(trim($content))) { + if (blank($content)) { // We throw an exception here in order to match the behavior of the JSON validation. throw new ParseException('File is empty'); } From 6d81d4188db719b2c0b1153d3be151a6c131660d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 15:41:10 +0200 Subject: [PATCH 075/118] Cleanup code --- packages/framework/src/Support/DataCollection.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 26f1d064780..49677e51a72 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -134,7 +134,6 @@ protected static function parseYamlFile(string $file): FrontMatter try { if (blank($content)) { - // We throw an exception here in order to match the behavior of the JSON validation. throw new ParseException('File is empty'); } From 5cf056db63ca44da5674e6d609eab8ddc1ebc9dd Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 15:41:53 +0200 Subject: [PATCH 076/118] Introduce local variable --- packages/framework/src/Support/DataCollection.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 49677e51a72..387a7593f85 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -90,7 +90,9 @@ public static function json(string $name, bool $asArray = false): static protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $extraArgs = []): static { return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing, $extraArgs): array { - return [static::makeIdentifier($file) => $parseUsing($file, ...$extraArgs)]; + $parsed = $parseUsing($file, ...$extraArgs); + + return [static::makeIdentifier($file) => $parsed]; })); } From 6ffd6f94fa7aa804354d96ca0552d992e977bcef Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 15:47:57 +0200 Subject: [PATCH 077/118] Refactor to have single try/catch block in loop --- .../framework/src/Support/DataCollection.php | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 387a7593f85..3e3be1eabb3 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -5,6 +5,7 @@ namespace Hyde\Support; use stdClass; +use Illuminate\Support\Arr; use Hyde\Facades\Filesystem; use InvalidArgumentException; use Symfony\Component\Yaml\Yaml; @@ -86,11 +87,25 @@ public static function json(string $name, bool $asArray = false): static * @param array|string $extensions * @param callable(string): mixed $parseUsing * @return static + * + * @throws \InvalidArgumentException if the file is empty or cannot be parsed. */ protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $extraArgs = []): static { - return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing, $extraArgs): array { - $parsed = $parseUsing($file, ...$extraArgs); + return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($extensions, $parseUsing, $extraArgs): array { + try { + $parsed = $parseUsing($file, ...$extraArgs); + } catch (ParseException $exception) { + $array = Arr::wrap($extensions); + $type = match (array_shift($array)) { + 'md' => 'Markdown', + 'yaml', 'yml' => 'YAML', + 'json' => 'JSON', + default => 'Unknown', + }; + + throw new InvalidArgumentException(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($exception->getMessage(), '.')), previous: $exception); + } return [static::makeIdentifier($file) => $parsed]; })); @@ -112,46 +127,35 @@ protected static function makeIdentifier(string $path): string return unslash(Str::after($path, static::$sourceDirectory)); } - /** @throws InvalidArgumentException If the Markdown is invalid and cannot be parsed. */ protected static function parseMarkdownFile(string $file): MarkdownDocument { - try { - $document = MarkdownFileParser::parse($file); - - if (blank($document->markdown()->body()) && $document->matter()->toArray() === []) { - throw new ParseException('File is empty'); - } + $document = MarkdownFileParser::parse($file); - return $document; - } catch (ParseException $exception) { - throw new InvalidArgumentException(sprintf("Invalid Markdown in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); + if (blank($document->markdown()->body()) && $document->matter()->toArray() === []) { + throw new ParseException('File is empty'); } + + return $document; } - /** @throws InvalidArgumentException If the YAML is invalid and cannot be parsed. */ protected static function parseYamlFile(string $file): FrontMatter { $content = Filesystem::getContents($file); $content = Str::between($content, '---', '---'); - try { - if (blank($content)) { - throw new ParseException('File is empty'); - } - - return new FrontMatter(Yaml::parse($content)); - } catch (ParseException $exception) { - throw new InvalidArgumentException(sprintf("Invalid YAML in file: '%s' (%s)", $file, rtrim($exception->getMessage(), '.')), previous: $exception); + if (blank($content)) { + throw new ParseException('File is empty'); } + + return new FrontMatter(Yaml::parse($content)); } - /** @throws InvalidArgumentException If the JSON is invalid and cannot be parsed. */ protected static function parseJsonFile(string $file, bool $asArray): stdClass|array { $contents = Filesystem::getContents($file); if (! json_validate($contents)) { - throw new InvalidArgumentException(sprintf("Invalid JSON in file: '%s' (%s)", $file, json_last_error_msg())); + throw new ParseException(json_last_error_msg()); } return json_decode($contents, $asArray); From 415a32ae102721207a3e2beac855544295b88546 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 15:55:50 +0200 Subject: [PATCH 078/118] Simplify and cleanup exception file type label handing --- .../framework/src/Support/DataCollection.php | 8 +------ .../tests/Unit/DataCollectionUnitTest.php | 24 +++++++++---------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 3e3be1eabb3..d37624d34a7 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -96,13 +96,7 @@ protected static function discover(string $name, array|string $extensions, calla try { $parsed = $parseUsing($file, ...$extraArgs); } catch (ParseException $exception) { - $array = Arr::wrap($extensions); - $type = match (array_shift($array)) { - 'md' => 'Markdown', - 'yaml', 'yml' => 'YAML', - 'json' => 'JSON', - default => 'Unknown', - }; + $type = $extensions === 'md' ? 'Markdown' : ucfirst(Arr::first(Arr::wrap($extensions))); throw new InvalidArgumentException(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($exception->getMessage(), '.')), previous: $exception); } diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index b8bcc94aae8..cc7a3a92044 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -341,7 +341,7 @@ public function testYamlCollectionsThrowExceptionForInvalidYaml() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid YAML in file: 'foo/bar.yml' (Malformed inline YAML string at line 2 (near \"foo: 'bar\"))"); + $this->expectExceptionMessage("Invalid Yaml in file: 'foo/bar.yml' (Malformed inline YAML string at line 2 (near \"foo: 'bar\"))"); MockableDataCollection::yaml('foo'); } @@ -353,7 +353,7 @@ public function testYamlCollectionsThrowExceptionForEmptyYaml() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid YAML in file: 'foo/bar.yml' (File is empty)"); + $this->expectExceptionMessage("Invalid Yaml in file: 'foo/bar.yml' (File is empty)"); MockableDataCollection::yaml('foo'); } @@ -365,7 +365,7 @@ public function testYamlCollectionsThrowExceptionForBlankYaml() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid YAML in file: 'foo/bar.yml' (File is empty)"); + $this->expectExceptionMessage("Invalid Yaml in file: 'foo/bar.yml' (File is empty)"); MockableDataCollection::yaml('foo'); } @@ -377,7 +377,7 @@ public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorW ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid YAML in file: 'foo/utf.yml' (The YAML value does not appear to be valid UTF-8)"); + $this->expectExceptionMessage("Invalid Yaml in file: 'foo/utf.yml' (The YAML value does not appear to be valid UTF-8)"); MockableDataCollection::yaml('foo'); } @@ -389,7 +389,7 @@ public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorW ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid YAML in file: 'foo/tabs.yml' (A YAML file cannot contain tabs as indentation at line 2 (near \" bar\"))"); + $this->expectExceptionMessage("Invalid Yaml in file: 'foo/tabs.yml' (A YAML file cannot contain tabs as indentation at line 2 (near \" bar\"))"); MockableDataCollection::yaml('foo'); } @@ -427,7 +427,7 @@ public function testJsonMethodThrowsExceptionForInvalidJson() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json' (Syntax error)"); + $this->expectExceptionMessage("Invalid Json in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); } @@ -439,7 +439,7 @@ public function testJsonMethodThrowsExceptionForInvalidJsonWithArray() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json' (Syntax error)"); + $this->expectExceptionMessage("Invalid Json in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo', true); } @@ -451,7 +451,7 @@ public function testJsonMethodThrowsExceptionForEmptyJson() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json' (Syntax error)"); + $this->expectExceptionMessage("Invalid Json in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); } @@ -463,7 +463,7 @@ public function testJsonMethodThrowsExceptionForBlankJson() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/bar.json' (Syntax error)"); + $this->expectExceptionMessage("Invalid Json in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); } @@ -476,7 +476,7 @@ public function testJsonMethodThrowsExceptionWhenJustOneFileIsInvalid() ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/baz.json' (Syntax error)"); + $this->expectExceptionMessage("Invalid Json in file: 'foo/baz.json' (Syntax error)"); MockableDataCollection::json('foo'); } @@ -488,7 +488,7 @@ public function testJsonMethodThrowsExceptionForOtherReasonsThanSyntaxErrorWithU ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/utf.json' (Malformed UTF-8 characters, possibly incorrectly encoded)"); + $this->expectExceptionMessage("Invalid Json in file: 'foo/utf.json' (Malformed UTF-8 characters, possibly incorrectly encoded)"); MockableDataCollection::json('foo'); } @@ -500,7 +500,7 @@ public function testJsonMethodThrowsExceptionForOtherReasonsThanSyntaxErrorWithC ]); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid JSON in file: 'foo/control.json' (Control character error, possibly incorrectly encoded)"); + $this->expectExceptionMessage("Invalid Json in file: 'foo/control.json' (Control character error, possibly incorrectly encoded)"); MockableDataCollection::json('foo'); } From 558f407d9559e2d18c601cb297a8b43aeb65ef1f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:03:07 +0200 Subject: [PATCH 079/118] Create ParseException.php --- .../src/Markdown/Exceptions/ParseException.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 packages/framework/src/Markdown/Exceptions/ParseException.php diff --git a/packages/framework/src/Markdown/Exceptions/ParseException.php b/packages/framework/src/Markdown/Exceptions/ParseException.php new file mode 100644 index 00000000000..e2ebc466a16 --- /dev/null +++ b/packages/framework/src/Markdown/Exceptions/ParseException.php @@ -0,0 +1,12 @@ + Date: Tue, 25 Jun 2024 16:04:26 +0200 Subject: [PATCH 080/118] Throw our ParseException instead of InvalidArgumentException Gives us more control, and adds more clarity and makes it easier to debug errors. --- RELEASE_NOTES.md | 6 ++-- .../framework/src/Support/DataCollection.php | 5 ++- .../tests/Unit/DataCollectionUnitTest.php | 32 +++++++++---------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 15cd68599a1..567af1b8217 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -236,15 +236,15 @@ Unfortunately, this means that existing setups may need to be adjusted to work w #### Issues that may arise -If you start getting `InvalidArgumentException` when using the `DataCollection` class, it may be due to malformed data collection files. +If you start getting a `ParseException` when using the `DataCollection` class, it may be due to malformed data collection files. Starting from this version, we validate the syntax of JSON and YAML files during discovery, to help you catch errors early. See https://github.com/hydephp/develop/issues/1736 for more information. For example, an empty or malformed JSON file will now throw an exception like this: ```php -InvalidArgumentException: Invalid JSON in file: 'foo/baz.json' (Syntax error) +\Hyde\Markdown\Exceptions\ParseException: Invalid JSON in file: 'foo/baz.json' (Syntax error) ``` -In order to normalize the thrown exceptions, we now rethrow Yaml `ParseException` as `InvalidArgumentException` to match the JSON validation. +In order to normalize the thrown exceptions, we now rethrow `Symfony/Yaml` `ParseException` as our custom `ParseException` to match the JSON validation. Additionally, an exception will be thrown if a JSON or YAML file is empty, as this is unlikely to be intentional. diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index d37624d34a7..d884da176b0 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -7,7 +7,6 @@ use stdClass; use Illuminate\Support\Arr; use Hyde\Facades\Filesystem; -use InvalidArgumentException; use Symfony\Component\Yaml\Yaml; use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; @@ -88,7 +87,7 @@ public static function json(string $name, bool $asArray = false): static * @param callable(string): mixed $parseUsing * @return static * - * @throws \InvalidArgumentException if the file is empty or cannot be parsed. + * @throws \Hyde\Markdown\Exceptions\ParseException If the file is empty or invalid. */ protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $extraArgs = []): static { @@ -98,7 +97,7 @@ protected static function discover(string $name, array|string $extensions, calla } catch (ParseException $exception) { $type = $extensions === 'md' ? 'Markdown' : ucfirst(Arr::first(Arr::wrap($extensions))); - throw new InvalidArgumentException(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($exception->getMessage(), '.')), previous: $exception); + throw new \Hyde\Markdown\Exceptions\ParseException(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($exception->getMessage(), '.')), previous: $exception); } return [static::makeIdentifier($file) => $parsed]; diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index cc7a3a92044..863206af5d9 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -6,7 +6,7 @@ use Hyde\Hyde; use Illuminate\Support\Str; -use InvalidArgumentException; +use Hyde\Markdown\Exceptions\ParseException; use Hyde\Support\DataCollection; use Hyde\Testing\UnitTestCase; use Illuminate\Filesystem\Filesystem; @@ -173,7 +173,7 @@ public function testMarkdownMethodWithEmptyFileThrowsException() 'foo/bar.md' => '', ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Markdown in file: 'foo/bar.md' (File is empty)"); MockableDataCollection::markdown('foo'); @@ -185,7 +185,7 @@ public function testMarkdownMethodWithEmptyFrontMatterAndContentThrowsException( 'foo/bar.md' => "---\n---", ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Markdown in file: 'foo/bar.md' (File is empty)"); MockableDataCollection::markdown('foo'); @@ -233,7 +233,7 @@ public function testMarkdownMethodWithInvalidFrontMatter() 'foo/bar.md' => "---\nfoo: 'bar\n---\nbar", ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Markdown in file: 'foo/bar.md' (Malformed inline YAML string at line 1 (near \"foo: 'bar\"))"); MockableDataCollection::markdown('foo'); @@ -340,7 +340,7 @@ public function testYamlCollectionsThrowExceptionForInvalidYaml() 'foo/bar.yml' => "---\nfoo: 'bar", ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Yaml in file: 'foo/bar.yml' (Malformed inline YAML string at line 2 (near \"foo: 'bar\"))"); MockableDataCollection::yaml('foo'); @@ -352,7 +352,7 @@ public function testYamlCollectionsThrowExceptionForEmptyYaml() 'foo/bar.yml' => '', ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Yaml in file: 'foo/bar.yml' (File is empty)"); MockableDataCollection::yaml('foo'); @@ -364,7 +364,7 @@ public function testYamlCollectionsThrowExceptionForBlankYaml() 'foo/bar.yml' => ' ', ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Yaml in file: 'foo/bar.yml' (File is empty)"); MockableDataCollection::yaml('foo'); @@ -376,7 +376,7 @@ public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorW 'foo/utf.yml' => "foo: \xB1\x31", ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Yaml in file: 'foo/utf.yml' (The YAML value does not appear to be valid UTF-8)"); MockableDataCollection::yaml('foo'); @@ -388,7 +388,7 @@ public function testYamlCollectionsThrowExceptionForOtherReasonsThanSyntaxErrorW 'foo/tabs.yml' => "foo:\n\tbar", ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Yaml in file: 'foo/tabs.yml' (A YAML file cannot contain tabs as indentation at line 2 (near \" bar\"))"); MockableDataCollection::yaml('foo'); @@ -426,7 +426,7 @@ public function testJsonMethodThrowsExceptionForInvalidJson() 'foo/bar.json' => '{"foo": "bar"', ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Json in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); @@ -438,7 +438,7 @@ public function testJsonMethodThrowsExceptionForInvalidJsonWithArray() 'foo/bar.json' => '{"foo": "bar"', ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Json in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo', true); @@ -450,7 +450,7 @@ public function testJsonMethodThrowsExceptionForEmptyJson() 'foo/bar.json' => '', ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Json in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); @@ -462,7 +462,7 @@ public function testJsonMethodThrowsExceptionForBlankJson() 'foo/bar.json' => ' ', ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Json in file: 'foo/bar.json' (Syntax error)"); MockableDataCollection::json('foo'); @@ -475,7 +475,7 @@ public function testJsonMethodThrowsExceptionWhenJustOneFileIsInvalid() 'foo/baz.json' => '{"foo": "baz"', ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Json in file: 'foo/baz.json' (Syntax error)"); MockableDataCollection::json('foo'); @@ -487,7 +487,7 @@ public function testJsonMethodThrowsExceptionForOtherReasonsThanSyntaxErrorWithU 'foo/utf.json' => "\xB1\x31", ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Json in file: 'foo/utf.json' (Malformed UTF-8 characters, possibly incorrectly encoded)"); MockableDataCollection::json('foo'); @@ -499,7 +499,7 @@ public function testJsonMethodThrowsExceptionForOtherReasonsThanSyntaxErrorWithC 'foo/control.json' => "\x19\x31", ]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ParseException::class); $this->expectExceptionMessage("Invalid Json in file: 'foo/control.json' (Control character error, possibly incorrectly encoded)"); MockableDataCollection::json('foo'); From 1c7114a9e38e7e838976fcb5c647cbffdd81b435 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:05:27 +0200 Subject: [PATCH 081/118] Improve wording --- RELEASE_NOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 567af1b8217..8d86e490c8b 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -246,5 +246,5 @@ For example, an empty or malformed JSON file will now throw an exception like th \Hyde\Markdown\Exceptions\ParseException: Invalid JSON in file: 'foo/baz.json' (Syntax error) ``` -In order to normalize the thrown exceptions, we now rethrow `Symfony/Yaml` `ParseException` as our custom `ParseException` to match the JSON validation. -Additionally, an exception will be thrown if a JSON or YAML file is empty, as this is unlikely to be intentional. +In order to normalize the thrown exceptions, we now rethrow `Symfony/Yaml` `ParseException` as our custom `ParseException` to match the JSON and Markdown validation. +Additionally, an exception will be thrown if a data file is empty, as this is unlikely to be intentional. From c5bc3748b1fbbc5b79851bba531f70b981681085 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:11:55 +0200 Subject: [PATCH 082/118] Simplify type label generator to use the file extension --- packages/framework/src/Support/DataCollection.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index d884da176b0..32921d0850d 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -18,9 +18,12 @@ use function blank; use function rtrim; use function implode; +use function explode; +use function ucfirst; use function json_decode; use function sprintf; use function unslash; +use function str_replace; use function json_last_error_msg; /** @@ -91,11 +94,12 @@ public static function json(string $name, bool $asArray = false): static */ protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $extraArgs = []): static { - return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($extensions, $parseUsing, $extraArgs): array { + return new static(static::findFiles($name, $extensions)->mapWithKeys(function (string $file) use ($parseUsing, $extraArgs): array { try { $parsed = $parseUsing($file, ...$extraArgs); } catch (ParseException $exception) { - $type = $extensions === 'md' ? 'Markdown' : ucfirst(Arr::first(Arr::wrap($extensions))); + $extension = Arr::last(explode('.', $file)); + $type = ucfirst(str_replace(['md', 'yml'], ['markdown', 'yaml'], $extension)); throw new \Hyde\Markdown\Exceptions\ParseException(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($exception->getMessage(), '.')), previous: $exception); } From 260ce224db57bfd5307ed7f6a927c2923173234f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:13:36 +0200 Subject: [PATCH 083/118] Fix parse exception namespace --- RELEASE_NOTES.md | 2 +- .../Exceptions/ParseException.php | 2 +- packages/framework/src/Support/DataCollection.php | 10 +++++----- .../framework/tests/Unit/DataCollectionUnitTest.php | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) rename packages/framework/src/{Markdown => Framework}/Exceptions/ParseException.php (75%) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8d86e490c8b..08dc7771ea2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -243,7 +243,7 @@ See https://github.com/hydephp/develop/issues/1736 for more information. For example, an empty or malformed JSON file will now throw an exception like this: ```php -\Hyde\Markdown\Exceptions\ParseException: Invalid JSON in file: 'foo/baz.json' (Syntax error) +\Hyde\Framework\Exceptions\ParseException: Invalid JSON in file: 'foo/baz.json' (Syntax error) ``` In order to normalize the thrown exceptions, we now rethrow `Symfony/Yaml` `ParseException` as our custom `ParseException` to match the JSON and Markdown validation. diff --git a/packages/framework/src/Markdown/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php similarity index 75% rename from packages/framework/src/Markdown/Exceptions/ParseException.php rename to packages/framework/src/Framework/Exceptions/ParseException.php index e2ebc466a16..0c138a58718 100644 --- a/packages/framework/src/Markdown/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Hyde\Markdown\Exceptions; +namespace Hyde\Framework\Exceptions; use RuntimeException; diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index 32921d0850d..f05f4d032e7 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -6,23 +6,23 @@ use stdClass; use Illuminate\Support\Arr; +use Illuminate\Support\Str; use Hyde\Facades\Filesystem; use Symfony\Component\Yaml\Yaml; +use Illuminate\Support\Collection; use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; use Hyde\Framework\Actions\MarkdownFileParser; use Symfony\Component\Yaml\Exception\ParseException; -use Illuminate\Support\Collection; -use Illuminate\Support\Str; use function blank; use function rtrim; use function implode; use function explode; use function ucfirst; -use function json_decode; use function sprintf; use function unslash; +use function json_decode; use function str_replace; use function json_last_error_msg; @@ -90,7 +90,7 @@ public static function json(string $name, bool $asArray = false): static * @param callable(string): mixed $parseUsing * @return static * - * @throws \Hyde\Markdown\Exceptions\ParseException If the file is empty or invalid. + * @throws \Hyde\Framework\Exceptions\ParseException If the file is empty or invalid. */ protected static function discover(string $name, array|string $extensions, callable $parseUsing, array $extraArgs = []): static { @@ -101,7 +101,7 @@ protected static function discover(string $name, array|string $extensions, calla $extension = Arr::last(explode('.', $file)); $type = ucfirst(str_replace(['md', 'yml'], ['markdown', 'yaml'], $extension)); - throw new \Hyde\Markdown\Exceptions\ParseException(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($exception->getMessage(), '.')), previous: $exception); + throw new \Hyde\Framework\Exceptions\ParseException(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($exception->getMessage(), '.')), previous: $exception); } return [static::makeIdentifier($file) => $parsed]; diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 863206af5d9..cf1c8122ffc 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -4,16 +4,16 @@ namespace Hyde\Framework\Testing\Unit; +use Mockery; use Hyde\Hyde; use Illuminate\Support\Str; -use Hyde\Markdown\Exceptions\ParseException; -use Hyde\Support\DataCollection; use Hyde\Testing\UnitTestCase; -use Illuminate\Filesystem\Filesystem; +use Hyde\Support\DataCollection; use Illuminate\Support\Collection; -use Mockery; +use Illuminate\Filesystem\Filesystem; use Hyde\Markdown\Models\FrontMatter; use Hyde\Markdown\Models\MarkdownDocument; +use Hyde\Framework\Exceptions\ParseException; /** * @covers \Hyde\Support\DataCollection From cf953e64d54c3da30b99e62bd238317d463eacbb Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:15:00 +0200 Subject: [PATCH 084/118] Fix class name capitalization --- packages/framework/src/Framework/Exceptions/ParseException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 0c138a58718..371dc1a77c3 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -6,7 +6,7 @@ use RuntimeException; -class ParseException extends RunTimeException +class ParseException extends RuntimeException { // } From 2783e0d4949896014277661f5c859cb5a169e6b2 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:15:09 +0200 Subject: [PATCH 085/118] Document exception scope --- packages/framework/src/Framework/Exceptions/ParseException.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 371dc1a77c3..020a6cd6068 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -6,6 +6,9 @@ use RuntimeException; +/** + * Exception class thrown when an error occurs during parsing. + */ class ParseException extends RuntimeException { // From 54285af13c9da77b9e618a91763adf9da5374a91 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:15:26 +0200 Subject: [PATCH 086/118] Revert "Document exception scope" This reverts commit 2783e0d4949896014277661f5c859cb5a169e6b2 as it should be obvious by the class name already. --- packages/framework/src/Framework/Exceptions/ParseException.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 020a6cd6068..371dc1a77c3 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -6,9 +6,6 @@ use RuntimeException; -/** - * Exception class thrown when an error occurs during parsing. - */ class ParseException extends RuntimeException { // From 2fba023e1b7f556128d1ab3265007a80f3025198 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:16:27 +0200 Subject: [PATCH 087/118] Mark ParseException class as experimental --- packages/framework/src/Framework/Exceptions/ParseException.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 371dc1a77c3..fe1340b7259 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -6,6 +6,7 @@ use RuntimeException; +/** @experimental This class may change significantly before its release. */ class ParseException extends RuntimeException { // From d2829f296c9d12683ea1a4bdb32938e857d6eb49 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:23:32 +0200 Subject: [PATCH 088/118] Test covers ParseException --- packages/framework/tests/Unit/CustomExceptionsTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/framework/tests/Unit/CustomExceptionsTest.php b/packages/framework/tests/Unit/CustomExceptionsTest.php index 684501c3b0c..8cc198dfc88 100644 --- a/packages/framework/tests/Unit/CustomExceptionsTest.php +++ b/packages/framework/tests/Unit/CustomExceptionsTest.php @@ -18,6 +18,7 @@ * @covers \Hyde\Framework\Exceptions\RouteNotFoundException * @covers \Hyde\Framework\Exceptions\BaseUrlNotSetException * @covers \Hyde\Framework\Exceptions\UnsupportedPageTypeException + * @covers \Hyde\Framework\Exceptions\ParseException */ class CustomExceptionsTest extends UnitTestCase { From 7f4d48db4577c3070247f9eda58c44ea3a1fc580 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:17:31 +0200 Subject: [PATCH 089/118] Override parent constructor --- .../framework/src/Framework/Exceptions/ParseException.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index fe1340b7259..ddad2f37df5 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -4,10 +4,14 @@ namespace Hyde\Framework\Exceptions; +use Throwable; use RuntimeException; /** @experimental This class may change significantly before its release. */ class ParseException extends RuntimeException { - // + public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + } } From 796035714d39d1af5182b73c2965af0f834f143e Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:24:10 +0200 Subject: [PATCH 090/118] Unit test covers ParseException --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index cf1c8122ffc..832612952f1 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -17,6 +17,7 @@ /** * @covers \Hyde\Support\DataCollection + * @covers \Hyde\Framework\Exceptions\ParseException * * @see \Hyde\Framework\Testing\Feature\DataCollectionTest */ From e5d4c820f5c5726571bfe16283b72a665a62f9bb Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 16:22:45 +0200 Subject: [PATCH 091/118] Refactor to move exception message assembly to custom exception --- .../src/Framework/Exceptions/ParseException.php | 14 ++++++++++++-- packages/framework/src/Support/DataCollection.php | 10 +--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index ddad2f37df5..547050d20ca 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -6,12 +6,22 @@ use Throwable; use RuntimeException; +use Illuminate\Support\Arr; + +use function rtrim; +use function sprintf; +use function explode; +use function ucfirst; +use function str_replace; /** @experimental This class may change significantly before its release. */ class ParseException extends RuntimeException { - public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null) + public function __construct(string $file = '', ?Throwable $previous = null) { - parent::__construct($message, $code, $previous); + $extension = Arr::last(explode('.', $file)); + $type = ucfirst(str_replace(['md', 'yml'], ['markdown', 'yaml'], $extension)); + + parent::__construct(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($previous->getMessage(), '.')), previous: $previous); } } diff --git a/packages/framework/src/Support/DataCollection.php b/packages/framework/src/Support/DataCollection.php index f05f4d032e7..af70ff38a3e 100644 --- a/packages/framework/src/Support/DataCollection.php +++ b/packages/framework/src/Support/DataCollection.php @@ -5,7 +5,6 @@ namespace Hyde\Support; use stdClass; -use Illuminate\Support\Arr; use Illuminate\Support\Str; use Hyde\Facades\Filesystem; use Symfony\Component\Yaml\Yaml; @@ -16,14 +15,10 @@ use Symfony\Component\Yaml\Exception\ParseException; use function blank; -use function rtrim; use function implode; -use function explode; -use function ucfirst; use function sprintf; use function unslash; use function json_decode; -use function str_replace; use function json_last_error_msg; /** @@ -98,10 +93,7 @@ protected static function discover(string $name, array|string $extensions, calla try { $parsed = $parseUsing($file, ...$extraArgs); } catch (ParseException $exception) { - $extension = Arr::last(explode('.', $file)); - $type = ucfirst(str_replace(['md', 'yml'], ['markdown', 'yaml'], $extension)); - - throw new \Hyde\Framework\Exceptions\ParseException(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($exception->getMessage(), '.')), previous: $exception); + throw new \Hyde\Framework\Exceptions\ParseException($file, $exception); } return [static::makeIdentifier($file) => $parsed]; From f4e18ba75a6ad97e2ea8c2c1a0fb3dbaaed2f0bd Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:01:37 +0200 Subject: [PATCH 092/118] Add unit tests for custom ParseException --- .../tests/Unit/CustomExceptionsTest.php | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/framework/tests/Unit/CustomExceptionsTest.php b/packages/framework/tests/Unit/CustomExceptionsTest.php index 8cc198dfc88..4c002d7bda7 100644 --- a/packages/framework/tests/Unit/CustomExceptionsTest.php +++ b/packages/framework/tests/Unit/CustomExceptionsTest.php @@ -11,6 +11,10 @@ use Hyde\Framework\Exceptions\RouteNotFoundException; use Hyde\Framework\Exceptions\BaseUrlNotSetException; use Hyde\Framework\Exceptions\UnsupportedPageTypeException; +use Hyde\Framework\Exceptions\ParseException; +use RuntimeException; +use Throwable; +use Exception; /** * @covers \Hyde\Framework\Exceptions\FileConflictException @@ -108,4 +112,64 @@ public function testBaseUrlNotSetExceptionCode() { $this->assertSame(500, (new BaseUrlNotSetException())->getCode()); } + + public function testParseExceptionWithDefaultMessage() + { + $exception = new ParseException(); + $this->assertSame("Invalid data in file: ''", $exception->getMessage()); + } + + public function testParseExceptionWithFileName() + { + $exception = new ParseException('example.md'); + $this->assertSame("Invalid Markdown in file: 'example.md'", $exception->getMessage()); + } + + public function testParseExceptionWithFileNameAndCustomMessage() + { + $previous = new RuntimeException('Custom error message.'); + $exception = new ParseException('example.yml', $previous); + $this->assertSame("Invalid Yaml in file: 'example.yml' (Custom error message)", $exception->getMessage()); + } + + public function testParseExceptionWithTxtExtension() + { + $exception = new ParseException('example.txt'); + $this->assertSame("Invalid Text in file: 'example.txt'", $exception->getMessage()); + } + + public function testParseExceptionWithUnsupportedExtension() + { + $exception = new ParseException('example.foo'); // Todo should be data? + $this->assertSame("Invalid Foo in file: 'example.foo'", $exception->getMessage()); + } + + public function testParseExceptionWithEmptyFileNameAndCustomMessage() + { + $previous = new RuntimeException('Custom error message.'); + $exception = new ParseException('', $previous); + $this->assertSame("Invalid data in file: '' (Custom error message)", $exception->getMessage()); + } + + public function testParseExceptionWithEmptyFileNameAndEmptyPreviousMessage() + { + $previous = new RuntimeException(''); + $exception = new ParseException('', $previous); + $this->assertSame("Invalid data in file: ''", $exception->getMessage()); + } + + public function testParseExceptionWithNoPrevious() + { + $exception = new ParseException('example.md'); + $this->assertSame("Invalid Markdown in file: 'example.md'", $exception->getMessage()); + $this->assertNull($exception->getPrevious()); + } + + public function testParseExceptionWithPrevious() + { + $previous = new Exception('Parsing error.'); + $exception = new ParseException('example.md', $previous); + $this->assertSame("Invalid Markdown in file: 'example.md' (Parsing error)", $exception->getMessage()); + $this->assertSame($previous, $exception->getPrevious()); + } } From 301c44ca423df55725f75ad2f4b1322c2e3d0a64 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:02:36 +0200 Subject: [PATCH 093/118] Improve parse exception message assembly --- .../framework/src/Framework/Exceptions/ParseException.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 547050d20ca..7fd16e9fdfc 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -20,8 +20,10 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { $extension = Arr::last(explode('.', $file)); - $type = ucfirst(str_replace(['md', 'yml'], ['markdown', 'yaml'], $extension)); + $type = ucfirst(str_replace(['md', 'txt', 'yml'], ['markdown', 'text', 'yaml'], $extension)) ?: 'data'; - parent::__construct(sprintf("Invalid %s in file: '%s' (%s)", $type, $file, rtrim($previous->getMessage(), '.')), previous: $previous); + $context = ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; + + parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", $type, $file, $context)), previous: $previous); } } From fcab984bc2b54ac144366ff2b07c3ecdc15ee6ea Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:02:41 +0200 Subject: [PATCH 094/118] Revert "Unit test covers ParseException" This reverts commit 796035714d39d1af5182b73c2965af0f834f143e. --- packages/framework/tests/Unit/DataCollectionUnitTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/framework/tests/Unit/DataCollectionUnitTest.php b/packages/framework/tests/Unit/DataCollectionUnitTest.php index 832612952f1..cf1c8122ffc 100644 --- a/packages/framework/tests/Unit/DataCollectionUnitTest.php +++ b/packages/framework/tests/Unit/DataCollectionUnitTest.php @@ -17,7 +17,6 @@ /** * @covers \Hyde\Support\DataCollection - * @covers \Hyde\Framework\Exceptions\ParseException * * @see \Hyde\Framework\Testing\Feature\DataCollectionTest */ From c604c93e5a83ccb02a8283add4d3264c584129cc Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:02:52 +0200 Subject: [PATCH 095/118] Test parse exception code --- packages/framework/tests/Unit/CustomExceptionsTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/framework/tests/Unit/CustomExceptionsTest.php b/packages/framework/tests/Unit/CustomExceptionsTest.php index 4c002d7bda7..c532b2503b4 100644 --- a/packages/framework/tests/Unit/CustomExceptionsTest.php +++ b/packages/framework/tests/Unit/CustomExceptionsTest.php @@ -113,6 +113,11 @@ public function testBaseUrlNotSetExceptionCode() $this->assertSame(500, (new BaseUrlNotSetException())->getCode()); } + public function testParseExceptionCode() + { + $this->assertSame(0, (new ParseException())->getCode()); + } + public function testParseExceptionWithDefaultMessage() { $exception = new ParseException(); From eaa3045e78d85485f787cfd34e12ed90ec26862d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:03:22 +0200 Subject: [PATCH 096/118] Remove unused use statement --- packages/framework/tests/Unit/CustomExceptionsTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/framework/tests/Unit/CustomExceptionsTest.php b/packages/framework/tests/Unit/CustomExceptionsTest.php index c532b2503b4..e841e303c83 100644 --- a/packages/framework/tests/Unit/CustomExceptionsTest.php +++ b/packages/framework/tests/Unit/CustomExceptionsTest.php @@ -13,7 +13,6 @@ use Hyde\Framework\Exceptions\UnsupportedPageTypeException; use Hyde\Framework\Exceptions\ParseException; use RuntimeException; -use Throwable; use Exception; /** From 4348f1ce85b4148724514c830360711ad05f502f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:09:29 +0200 Subject: [PATCH 097/118] Set parse exception status code to 422 --- packages/framework/src/Framework/Exceptions/ParseException.php | 3 +++ packages/framework/tests/Unit/CustomExceptionsTest.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 7fd16e9fdfc..fe89cc2e677 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -17,6 +17,9 @@ /** @experimental This class may change significantly before its release. */ class ParseException extends RuntimeException { + /** @var int */ + protected $code = 422; + public function __construct(string $file = '', ?Throwable $previous = null) { $extension = Arr::last(explode('.', $file)); diff --git a/packages/framework/tests/Unit/CustomExceptionsTest.php b/packages/framework/tests/Unit/CustomExceptionsTest.php index e841e303c83..cef337a39fc 100644 --- a/packages/framework/tests/Unit/CustomExceptionsTest.php +++ b/packages/framework/tests/Unit/CustomExceptionsTest.php @@ -114,7 +114,7 @@ public function testBaseUrlNotSetExceptionCode() public function testParseExceptionCode() { - $this->assertSame(0, (new ParseException())->getCode()); + $this->assertSame(422, (new ParseException())->getCode()); } public function testParseExceptionWithDefaultMessage() From 5fb82c140f178ede8aca31b644832ae5ebb8fb38 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:11:24 +0200 Subject: [PATCH 098/118] Replace string replacement with match expression More verbose, but clearer, and probably more accurate. Has cases for the exceptions we throw this for. --- .../src/Framework/Exceptions/ParseException.php | 9 ++++++--- packages/framework/tests/Unit/CustomExceptionsTest.php | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index fe89cc2e677..dbac34a03b7 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -11,8 +11,6 @@ use function rtrim; use function sprintf; use function explode; -use function ucfirst; -use function str_replace; /** @experimental This class may change significantly before its release. */ class ParseException extends RuntimeException @@ -23,7 +21,12 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { $extension = Arr::last(explode('.', $file)); - $type = ucfirst(str_replace(['md', 'txt', 'yml'], ['markdown', 'text', 'yaml'], $extension)) ?: 'data'; + $type = match ($extension) { + 'md' => 'Markdown', + 'yaml', 'yml' => 'Yaml', + 'json' => 'Json', + default => 'data', + }; $context = ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; diff --git a/packages/framework/tests/Unit/CustomExceptionsTest.php b/packages/framework/tests/Unit/CustomExceptionsTest.php index cef337a39fc..68d9313769b 100644 --- a/packages/framework/tests/Unit/CustomExceptionsTest.php +++ b/packages/framework/tests/Unit/CustomExceptionsTest.php @@ -139,13 +139,13 @@ public function testParseExceptionWithFileNameAndCustomMessage() public function testParseExceptionWithTxtExtension() { $exception = new ParseException('example.txt'); - $this->assertSame("Invalid Text in file: 'example.txt'", $exception->getMessage()); + $this->assertSame("Invalid data in file: 'example.txt'", $exception->getMessage()); } public function testParseExceptionWithUnsupportedExtension() { - $exception = new ParseException('example.foo'); // Todo should be data? - $this->assertSame("Invalid Foo in file: 'example.foo'", $exception->getMessage()); + $exception = new ParseException('example.foo'); + $this->assertSame("Invalid data in file: 'example.foo'", $exception->getMessage()); } public function testParseExceptionWithEmptyFileNameAndCustomMessage() From ad15064946a8cbb1a23bc4aca4449227b71cc996 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:12:07 +0200 Subject: [PATCH 099/118] Add source code spacing --- packages/framework/tests/Unit/CustomExceptionsTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/framework/tests/Unit/CustomExceptionsTest.php b/packages/framework/tests/Unit/CustomExceptionsTest.php index 68d9313769b..fcaeb77b1f3 100644 --- a/packages/framework/tests/Unit/CustomExceptionsTest.php +++ b/packages/framework/tests/Unit/CustomExceptionsTest.php @@ -120,12 +120,14 @@ public function testParseExceptionCode() public function testParseExceptionWithDefaultMessage() { $exception = new ParseException(); + $this->assertSame("Invalid data in file: ''", $exception->getMessage()); } public function testParseExceptionWithFileName() { $exception = new ParseException('example.md'); + $this->assertSame("Invalid Markdown in file: 'example.md'", $exception->getMessage()); } @@ -133,18 +135,21 @@ public function testParseExceptionWithFileNameAndCustomMessage() { $previous = new RuntimeException('Custom error message.'); $exception = new ParseException('example.yml', $previous); + $this->assertSame("Invalid Yaml in file: 'example.yml' (Custom error message)", $exception->getMessage()); } public function testParseExceptionWithTxtExtension() { $exception = new ParseException('example.txt'); + $this->assertSame("Invalid data in file: 'example.txt'", $exception->getMessage()); } public function testParseExceptionWithUnsupportedExtension() { $exception = new ParseException('example.foo'); + $this->assertSame("Invalid data in file: 'example.foo'", $exception->getMessage()); } @@ -152,6 +157,7 @@ public function testParseExceptionWithEmptyFileNameAndCustomMessage() { $previous = new RuntimeException('Custom error message.'); $exception = new ParseException('', $previous); + $this->assertSame("Invalid data in file: '' (Custom error message)", $exception->getMessage()); } @@ -159,12 +165,14 @@ public function testParseExceptionWithEmptyFileNameAndEmptyPreviousMessage() { $previous = new RuntimeException(''); $exception = new ParseException('', $previous); + $this->assertSame("Invalid data in file: ''", $exception->getMessage()); } public function testParseExceptionWithNoPrevious() { $exception = new ParseException('example.md'); + $this->assertSame("Invalid Markdown in file: 'example.md'", $exception->getMessage()); $this->assertNull($exception->getPrevious()); } @@ -173,6 +181,7 @@ public function testParseExceptionWithPrevious() { $previous = new Exception('Parsing error.'); $exception = new ParseException('example.md', $previous); + $this->assertSame("Invalid Markdown in file: 'example.md' (Parsing error)", $exception->getMessage()); $this->assertSame($previous, $exception->getPrevious()); } From 05910f3880b260494f6ca029bbaed762d31053e1 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:13:21 +0200 Subject: [PATCH 100/118] Update RELEASE_NOTES.md --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 08dc7771ea2..aaa55ca2fcf 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,7 @@ This serves two purposes: ### Added - You can now specify navigation priorities by adding a numeric prefix to the source file names in https://github.com/hydephp/develop/pull/1709 - Added a new `\Hyde\Framework\Actions\PreBuildTasks\TransferMediaAssets` build task handle media assets transfers for site builds. +- Added a new `\Hyde\Framework\Exceptions\ParseException` exception class to handle parsing exceptions in data collection files in https://github.com/hydephp/develop/pull/1732 - The `\Hyde\Facades\Features` class is no longer marked as internal, and is now thus part of the public API. ### Changed From 68b096df48586cc40d95bd59f6e5da5ac21f3578 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 17:15:02 +0200 Subject: [PATCH 101/118] Extract helper method --- .../framework/src/Framework/Exceptions/ParseException.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index dbac34a03b7..0475c5427d0 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -28,8 +28,13 @@ public function __construct(string $file = '', ?Throwable $previous = null) default => 'data', }; - $context = ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; + $context = $this->getContext($previous); parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", $type, $file, $context)), previous: $previous); } + + protected function getContext(?Throwable $previous): string + { + return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; + } } From 19ac8dfdd9cad303d8d5a8f1d69367614911db6e Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:41:00 +0200 Subject: [PATCH 102/118] Inline local variable --- packages/framework/src/Framework/Exceptions/ParseException.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 0475c5427d0..78a38bd3351 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -20,8 +20,7 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { - $extension = Arr::last(explode('.', $file)); - $type = match ($extension) { + $type = match (Arr::last(explode('.', $file))) { 'md' => 'Markdown', 'yaml', 'yml' => 'Yaml', 'json' => 'Json', From 5393e369474e61f3d3b8688ab2442f835f67e508 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:41:35 +0200 Subject: [PATCH 103/118] Extract helper method --- .../src/Framework/Exceptions/ParseException.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 78a38bd3351..cbeb87032ac 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -20,12 +20,7 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { - $type = match (Arr::last(explode('.', $file))) { - 'md' => 'Markdown', - 'yaml', 'yml' => 'Yaml', - 'json' => 'Json', - default => 'data', - }; + $type = $this->getTypeLabel($file); $context = $this->getContext($previous); @@ -36,4 +31,14 @@ protected function getContext(?Throwable $previous): string { return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; } + + protected function getTypeLabel(string $file): string + { + return match (Arr::last(explode('.', $file))) { + 'md' => 'Markdown', + 'yaml', 'yml' => 'Yaml', + 'json' => 'Json', + default => 'data', + }; + } } From 6fc3d21aa8914ae5efeebe7bd9f6b68d51e665b6 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:41:44 +0200 Subject: [PATCH 104/118] Move up method in source --- .../src/Framework/Exceptions/ParseException.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index cbeb87032ac..d0feec9aa41 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -27,11 +27,6 @@ public function __construct(string $file = '', ?Throwable $previous = null) parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", $type, $file, $context)), previous: $previous); } - protected function getContext(?Throwable $previous): string - { - return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; - } - protected function getTypeLabel(string $file): string { return match (Arr::last(explode('.', $file))) { @@ -41,4 +36,9 @@ protected function getTypeLabel(string $file): string default => 'data', }; } + + protected function getContext(?Throwable $previous): string + { + return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; + } } From 177185bac7523873086a25fe233a76f9cd5f09f6 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:41:52 +0200 Subject: [PATCH 105/118] Inline local variables --- .../framework/src/Framework/Exceptions/ParseException.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index d0feec9aa41..c78361b7415 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -20,11 +20,7 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { - $type = $this->getTypeLabel($file); - - $context = $this->getContext($previous); - - parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", $type, $file, $context)), previous: $previous); + parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", $this->getTypeLabel($file), $file, $this->getContext($previous))), previous: $previous); } protected function getTypeLabel(string $file): string From 770df88e5b7ee31d25c455cc65739c22e4f1b0ce Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:42:22 +0200 Subject: [PATCH 106/118] Convert a 'sprintf()' call to string interpolation --- packages/framework/src/Framework/Exceptions/ParseException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index c78361b7415..a5e0df0400e 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -20,7 +20,7 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { - parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", $this->getTypeLabel($file), $file, $this->getContext($previous))), previous: $previous); + parent::__construct(rtrim("Invalid {$this->getTypeLabel($file)} in file: '{$file}' {$this->getContext($previous)}"), previous: $previous); } protected function getTypeLabel(string $file): string From e1ce0c3460a47f27be47db39ba49c8a5cbf15b3e Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:42:26 +0200 Subject: [PATCH 107/118] Revert "Convert a 'sprintf()' call to string interpolation" This reverts commit 770df88e5b7ee31d25c455cc65739c22e4f1b0ce. --- packages/framework/src/Framework/Exceptions/ParseException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index a5e0df0400e..c78361b7415 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -20,7 +20,7 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { - parent::__construct(rtrim("Invalid {$this->getTypeLabel($file)} in file: '{$file}' {$this->getContext($previous)}"), previous: $previous); + parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", $this->getTypeLabel($file), $file, $this->getContext($previous))), previous: $previous); } protected function getTypeLabel(string $file): string From 3d05b2f2a6114861c72f8334ffd3ff7bf3d772fe Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:42:49 +0200 Subject: [PATCH 108/118] Split comma-separated values into multiple lines --- .../framework/src/Framework/Exceptions/ParseException.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index c78361b7415..4dc1e899d0d 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -20,7 +20,9 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { - parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", $this->getTypeLabel($file), $file, $this->getContext($previous))), previous: $previous); + parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", + $this->getTypeLabel($file), $file, $this->getContext($previous)) + ), previous: $previous); } protected function getTypeLabel(string $file): string From b2d3282a1b6b06b5deb566b9d18be411dd4da42f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:43:16 +0200 Subject: [PATCH 109/118] Extract helper method --- .../src/Framework/Exceptions/ParseException.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 4dc1e899d0d..3452b493648 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -20,9 +20,7 @@ class ParseException extends RuntimeException public function __construct(string $file = '', ?Throwable $previous = null) { - parent::__construct(rtrim(sprintf("Invalid %s in file: '%s' %s", - $this->getTypeLabel($file), $file, $this->getContext($previous)) - ), previous: $previous); + parent::__construct($this->formatMessage($file, $previous), previous: $previous); } protected function getTypeLabel(string $file): string @@ -39,4 +37,9 @@ protected function getContext(?Throwable $previous): string { return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; } + + protected function formatMessage(string $file, ?Throwable $previous): string + { + return rtrim(sprintf("Invalid %s in file: '%s' %s", $this->getTypeLabel($file), $file, $this->getContext($previous))); + } } From 323abb1cf21f3bd3a65e5a74a4e8b09caa36cbae Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:43:31 +0200 Subject: [PATCH 110/118] Move up method in source --- .../src/Framework/Exceptions/ParseException.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 3452b493648..753cb870780 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -23,6 +23,11 @@ public function __construct(string $file = '', ?Throwable $previous = null) parent::__construct($this->formatMessage($file, $previous), previous: $previous); } + protected function formatMessage(string $file, ?Throwable $previous): string + { + return rtrim(sprintf("Invalid %s in file: '%s' %s", $this->getTypeLabel($file), $file, $this->getContext($previous))); + } + protected function getTypeLabel(string $file): string { return match (Arr::last(explode('.', $file))) { @@ -37,9 +42,4 @@ protected function getContext(?Throwable $previous): string { return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; } - - protected function formatMessage(string $file, ?Throwable $previous): string - { - return rtrim(sprintf("Invalid %s in file: '%s' %s", $this->getTypeLabel($file), $file, $this->getContext($previous))); - } } From 8fba6af90c92c11e287963f2cc9bbc66f4debb8c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 18:46:18 +0200 Subject: [PATCH 111/118] Conditionally assemble file path string --- .../framework/src/Framework/Exceptions/ParseException.php | 4 +++- packages/framework/tests/Unit/CustomExceptionsTest.php | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 753cb870780..aaf4724ae0a 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -25,7 +25,9 @@ public function __construct(string $file = '', ?Throwable $previous = null) protected function formatMessage(string $file, ?Throwable $previous): string { - return rtrim(sprintf("Invalid %s in file: '%s' %s", $this->getTypeLabel($file), $file, $this->getContext($previous))); + $fileLabel = $file ? sprintf(": '%s'", $file) : ''; + + return rtrim(sprintf('Invalid %s in file%s %s', $this->getTypeLabel($file), $fileLabel, $this->getContext($previous))); } protected function getTypeLabel(string $file): string diff --git a/packages/framework/tests/Unit/CustomExceptionsTest.php b/packages/framework/tests/Unit/CustomExceptionsTest.php index fcaeb77b1f3..e3894eec0a0 100644 --- a/packages/framework/tests/Unit/CustomExceptionsTest.php +++ b/packages/framework/tests/Unit/CustomExceptionsTest.php @@ -121,7 +121,7 @@ public function testParseExceptionWithDefaultMessage() { $exception = new ParseException(); - $this->assertSame("Invalid data in file: ''", $exception->getMessage()); + $this->assertSame('Invalid data in file', $exception->getMessage()); } public function testParseExceptionWithFileName() @@ -158,7 +158,7 @@ public function testParseExceptionWithEmptyFileNameAndCustomMessage() $previous = new RuntimeException('Custom error message.'); $exception = new ParseException('', $previous); - $this->assertSame("Invalid data in file: '' (Custom error message)", $exception->getMessage()); + $this->assertSame('Invalid data in file (Custom error message)', $exception->getMessage()); } public function testParseExceptionWithEmptyFileNameAndEmptyPreviousMessage() @@ -166,7 +166,7 @@ public function testParseExceptionWithEmptyFileNameAndEmptyPreviousMessage() $previous = new RuntimeException(''); $exception = new ParseException('', $previous); - $this->assertSame("Invalid data in file: ''", $exception->getMessage()); + $this->assertSame('Invalid data in file', $exception->getMessage()); } public function testParseExceptionWithNoPrevious() From 694b1cdd72933665a5b3f51d04175756e84304ad Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 19:29:53 +0200 Subject: [PATCH 112/118] Refactor and cleanup code --- .../src/Framework/Exceptions/ParseException.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index aaf4724ae0a..1611bdbd347 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -25,9 +25,7 @@ public function __construct(string $file = '', ?Throwable $previous = null) protected function formatMessage(string $file, ?Throwable $previous): string { - $fileLabel = $file ? sprintf(": '%s'", $file) : ''; - - return rtrim(sprintf('Invalid %s in file%s %s', $this->getTypeLabel($file), $fileLabel, $this->getContext($previous))); + return rtrim(sprintf('Invalid %s in file%s %s', $this->getTypeLabel($file), $this->getFileLabel($file), $this->getContext($previous))); } protected function getTypeLabel(string $file): string @@ -40,6 +38,11 @@ protected function getTypeLabel(string $file): string }; } + protected function getFileLabel(string $file): string + { + return $file ? sprintf(": '%s'", $file) : ''; + } + protected function getContext(?Throwable $previous): string { return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; From f1e82a173c49f42c1734df54902b8382fce844e6 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 19:43:48 +0200 Subject: [PATCH 113/118] Remove unnecessary parentheses --- packages/framework/src/Framework/Exceptions/ParseException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index 1611bdbd347..a53680dce61 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -45,6 +45,6 @@ protected function getFileLabel(string $file): string protected function getContext(?Throwable $previous): string { - return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; + return $previous && $previous->getMessage() ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; } } From e976cee951d770b17d6692f77eeeaebd5c525e59 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 19:43:52 +0200 Subject: [PATCH 114/118] Revert "Remove unnecessary parentheses" This reverts commit f1e82a173c49f42c1734df54902b8382fce844e6. --- packages/framework/src/Framework/Exceptions/ParseException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Exceptions/ParseException.php b/packages/framework/src/Framework/Exceptions/ParseException.php index a53680dce61..1611bdbd347 100644 --- a/packages/framework/src/Framework/Exceptions/ParseException.php +++ b/packages/framework/src/Framework/Exceptions/ParseException.php @@ -45,6 +45,6 @@ protected function getFileLabel(string $file): string protected function getContext(?Throwable $previous): string { - return $previous && $previous->getMessage() ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; + return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : ''; } } From d6a619f0b507f6ca41fb48a469c69d9796a910d7 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 19:47:08 +0200 Subject: [PATCH 115/118] Update RELEASE_NOTES.md --- RELEASE_NOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index aaa55ca2fcf..010e925d2ac 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -231,9 +231,10 @@ Unfortunately, this means that existing setups may need to be adjusted to work w - The `DataCollections` class has been renamed to `DataCollection`. If you have used the `DataCollections` class in your code, you will need to update your code to use the new class name. -#### Minor impact +#### Changes - Calling the `DataCollection` methods will no longer create the data collections directory automatically +- The `DataCollection` class now validates the syntax of all data collection files during discovery, and throws a `ParseException` if the syntax is invalid #### Issues that may arise From b2912a3d3227711a36b49bfea2528e721280daf1 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 19:47:59 +0200 Subject: [PATCH 116/118] Draft upcoming change notes See https://github.com/hydephp/develop/pull/1732#issuecomment-2186498121 --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 010e925d2ac..7fe5c37db0c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -235,6 +235,7 @@ Unfortunately, this means that existing setups may need to be adjusted to work w - Calling the `DataCollection` methods will no longer create the data collections directory automatically - The `DataCollection` class now validates the syntax of all data collection files during discovery, and throws a `ParseException` if the syntax is invalid +- The collections are now no longer keyed by the relative file path, instead only the basename identifier is used (see https://github.com/hydephp/develop/pull/1732#issuecomment-2186498121) #### Issues that may arise From 227db4f8faa338798c526bfc407e8f0f99743af4 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 19:51:17 +0200 Subject: [PATCH 117/118] Revert "Draft upcoming change notes" This reverts commit b2912a3d3227711a36b49bfea2528e721280daf1. --- RELEASE_NOTES.md | 1 - 1 file changed, 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7fe5c37db0c..010e925d2ac 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -235,7 +235,6 @@ Unfortunately, this means that existing setups may need to be adjusted to work w - Calling the `DataCollection` methods will no longer create the data collections directory automatically - The `DataCollection` class now validates the syntax of all data collection files during discovery, and throws a `ParseException` if the syntax is invalid -- The collections are now no longer keyed by the relative file path, instead only the basename identifier is used (see https://github.com/hydephp/develop/pull/1732#issuecomment-2186498121) #### Issues that may arise From 0a3a65a694584ca5054588b18163731995ee5540 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 25 Jun 2024 19:57:12 +0200 Subject: [PATCH 118/118] Clean up release notes --- RELEASE_NOTES.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 010e925d2ac..a62b07d4786 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -233,14 +233,14 @@ Unfortunately, this means that existing setups may need to be adjusted to work w #### Changes -- Calling the `DataCollection` methods will no longer create the data collections directory automatically -- The `DataCollection` class now validates the syntax of all data collection files during discovery, and throws a `ParseException` if the syntax is invalid +- Calling the `DataCollection` methods will no longer create the data collections directory automatically. +- The `DataCollection` class now validates the syntax of all data collection files during discovery, and throws a `ParseException` if the syntax is invalid. #### Issues that may arise If you start getting a `ParseException` when using the `DataCollection` class, it may be due to malformed data collection files. -Starting from this version, we validate the syntax of JSON and YAML files during discovery, to help you catch errors early. -See https://github.com/hydephp/develop/issues/1736 for more information. +Starting from this version, we validate the syntax of JSON and YAML in data files during discovery, including any front matter in Markdown data files. +We do this to help you catch errors early. See https://github.com/hydephp/develop/issues/1736 for more information. For example, an empty or malformed JSON file will now throw an exception like this: @@ -248,5 +248,5 @@ For example, an empty or malformed JSON file will now throw an exception like th \Hyde\Framework\Exceptions\ParseException: Invalid JSON in file: 'foo/baz.json' (Syntax error) ``` -In order to normalize the thrown exceptions, we now rethrow `Symfony/Yaml` `ParseException` as our custom `ParseException` to match the JSON and Markdown validation. -Additionally, an exception will be thrown if a data file is empty, as this is unlikely to be intentional. +In order to normalize the thrown exceptions, we now rethrow the `ParseException` from `Symfony/Yaml` as our custom `ParseException` to match the JSON and Markdown validation. +Additionally, an exception will be thrown if a data file is empty, as this is unlikely to be intentional. Markdown files can have an empty body if front matter is present.