Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.x] Includes facade improvements #1738

Merged
merged 105 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
25733a1
Extract helper method
caendesilva Jun 26, 2024
2da4e75
Get file contents using filesystem facade
caendesilva Jun 26, 2024
2df07d5
Get file existence using filesystem facade
caendesilva Jun 26, 2024
3a73736
Create IncludesFacadeUnitTest.php
caendesilva Jun 26, 2024
dfc5581
Add test crosslinks
caendesilva Jun 26, 2024
48180bc
Generate unit test with GPT 4
caendesilva Jun 26, 2024
8a776ef
Test needs kernel
caendesilva Jun 26, 2024
b117a37
Clean up formatting
caendesilva Jun 26, 2024
d66537d
Use mockable get instead of get contents
caendesilva Jun 26, 2024
85314bf
Revert "Use mockable get instead of get contents"
caendesilva Jun 26, 2024
37e7ddd
Reapply "Use mockable get instead of get contents"
caendesilva Jun 26, 2024
84a6d93
Use Illuminate filesystem
caendesilva Jun 26, 2024
a216620
Don't mock Markdown class
caendesilva Jun 26, 2024
5bcb54d
Add missing assertions
caendesilva Jun 26, 2024
bfa129c
Clear resolved Blade instances
caendesilva Jun 26, 2024
6288483
Revert "Clear resolved Blade instances"
caendesilva Jun 26, 2024
852cb99
Remove Blade mocks
caendesilva Jun 26, 2024
60c2134
Revert "Remove Blade mocks"
caendesilva Jun 26, 2024
bd04137
Swap Blade for a mock
caendesilva Jun 26, 2024
2661b8e
Refactor to extract helper method
caendesilva Jun 26, 2024
d950c90
Refactor with GPT 4 to use the added helper method
caendesilva Jun 26, 2024
5e7869f
Move variable assignments inside closures
caendesilva Jun 26, 2024
4868497
Refactor to move Blade mocks into closure
caendesilva Jun 26, 2024
45e59ce
Cleanup formatting
caendesilva Jun 26, 2024
2658708
Add broader level feature test
caendesilva Jun 26, 2024
9f6a579
Specify page class to use the custom Markdown features
caendesilva Jun 26, 2024
f15bea8
Change page class used to render Markdown partials to MarkdownDocument
caendesilva Jun 26, 2024
9a8538c
Add test for current state
caendesilva Jun 26, 2024
83c6136
Do not inject Torchlight attribution for included partials
caendesilva Jun 26, 2024
e56494d
Clean up formatting
caendesilva Jun 26, 2024
9d4a85b
Move up test in source
caendesilva Jun 26, 2024
0225edd
Update RELEASE_NOTES.md
caendesilva Jun 26, 2024
b7a7a79
Extract helper method
caendesilva Jun 26, 2024
5c1833e
Test advanced Blade partial is compiled to HTML
caendesilva Jun 26, 2024
0127802
Remove link include requiring more setup
caendesilva Jun 26, 2024
1d25d01
Clean up whitespace
caendesilva Jun 26, 2024
ffc8e7d
Test includes usage from Blade view
caendesilva Jun 26, 2024
b642920
Test without extensions
caendesilva Jun 26, 2024
f6d9755
Add element to match others
caendesilva Jun 26, 2024
b207e9b
Clarify the test data
caendesilva Jun 26, 2024
2d8a349
Reorder lines
caendesilva Jun 26, 2024
1365f1c
Document the associated PR
caendesilva Jun 26, 2024
9cc70f6
Trim trailing whitespace from included Markdown
caendesilva Jun 26, 2024
eeb1fe4
Clarify how the test works
caendesilva Jun 26, 2024
e9b877a
Add cases for the current escape state
caendesilva Jun 26, 2024
6f656e4
Extra comment clarification
caendesilva Jun 26, 2024
cc33174
Update `Includes` helpers to return `HtmlString` objects
caendesilva Jun 26, 2024
c527e97
Replace ternary expression with if/else block
caendesilva Jun 26, 2024
4d44b83
Introduce local variables
caendesilva Jun 26, 2024
2282b8e
Wrap declaration in else block
caendesilva Jun 26, 2024
1e1520a
Unwrap common parts of if/else block
caendesilva Jun 26, 2024
370524a
Unwrap 'else'
caendesilva Jun 26, 2024
667f7d9
Invert control flow to simply conditional paths
caendesilva Jun 26, 2024
6cd529f
Move out return from condition
caendesilva Jun 26, 2024
130dd77
Merge into 'elseif'
caendesilva Jun 26, 2024
d0d6709
Revert "Merge into 'elseif'"
caendesilva Jun 26, 2024
d248f04
Revert "Move out return from condition"
caendesilva Jun 26, 2024
dc7c75c
Revert "Invert control flow to simply conditional paths"
caendesilva Jun 26, 2024
012a91d
Revert "Unwrap 'else'"
caendesilva Jun 26, 2024
de5131e
Revert "Unwrap common parts of if/else block"
caendesilva Jun 26, 2024
59448a6
Revert "Wrap declaration in else block"
caendesilva Jun 26, 2024
fc6460d
Revert "Introduce local variables"
caendesilva Jun 26, 2024
80e6e1e
Revert "Replace ternary expression with if/else block"
caendesilva Jun 26, 2024
66ced70
Extract helper method to render Markdown
caendesilva Jun 26, 2024
b923262
Extract helper method to render Blade
caendesilva Jun 26, 2024
8d7b1cb
Extract helper method to render Html
caendesilva Jun 26, 2024
9038ab0
Refactor control flow
caendesilva Jun 26, 2024
e120ba3
Unwrap 'else'
caendesilva Jun 26, 2024
c0efe45
Revert "Unwrap 'else'"
caendesilva Jun 26, 2024
760af6b
Revert "Refactor control flow"
caendesilva Jun 26, 2024
ac255ee
Extract helper to get file contents
caendesilva Jun 26, 2024
20f764a
Return null if file does not exist
caendesilva Jun 26, 2024
b1ad1fc
Introduce local variable
caendesilva Jun 26, 2024
4ac8f35
Check if contents is null
caendesilva Jun 26, 2024
01d8833
Replace ternary expression with if/else block
caendesilva Jun 26, 2024
7eb2dab
Set contents to default
caendesilva Jun 26, 2024
dae62ba
Unwrap 'else'
caendesilva Jun 26, 2024
a62671d
Flatten nested conditions
caendesilva Jun 26, 2024
c2b9f68
Null coalesce to default
caendesilva Jun 26, 2024
dfdceb3
Inline local variable
caendesilva Jun 26, 2024
0e6b17f
Revert "Inline local variable"
caendesilva Jun 26, 2024
456bf34
Refactor other methods to use improved control flow
caendesilva Jun 26, 2024
acbe58b
Extract helper method for shared code
caendesilva Jun 26, 2024
877aa04
Move up method parameter in signature
caendesilva Jun 26, 2024
5fc1c1c
Use callable method parameter
caendesilva Jun 26, 2024
6b8e328
Reorder methods in source
caendesilva Jun 26, 2024
64eb92a
Make extension optional
caendesilva Jun 26, 2024
ea93261
Shift absolute path qualification to retrieval
caendesilva Jun 26, 2024
83b2480
Introduce local variable
caendesilva Jun 26, 2024
76b35dc
Revert "Introduce local variable"
caendesilva Jun 26, 2024
c77d1fc
Shift absolute path qualification to calling method
caendesilva Jun 26, 2024
11a6015
Normalize includes paths using same rules as other helpers
caendesilva Jun 26, 2024
9a07581
Normalize control flow
caendesilva Jun 26, 2024
15472ec
Move extension to last in signature
caendesilva Jun 26, 2024
92fe45d
Make extension optional
caendesilva Jun 26, 2024
1c010cd
Union type null instead of nullable short syntax
caendesilva Jun 26, 2024
22c16d2
Add no-op helper method
caendesilva Jun 26, 2024
daedfdf
Union type string return
caendesilva Jun 26, 2024
7a7d9db
Use the helper method
caendesilva Jun 26, 2024
289ae7f
Annotate callable generics
caendesilva Jun 26, 2024
12d845e
Fix parameter annotation formatting
caendesilva Jun 26, 2024
36e8b5e
Inline no-op helper method into arrow function
caendesilva Jun 26, 2024
cdcf30e
Replace ternary expression with if/else block
caendesilva Jun 26, 2024
f30d834
Unwrap else block
caendesilva Jun 26, 2024
befd302
Remove null union from callable that should not return it
caendesilva Jun 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ This serves two purposes:
- 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
- Minor: Methods in the `Includes` facade now return `HtmlString` objects instead of `string` in https://github.com/hydephp/develop/pull/1738. For more information, see below.
- Minor: `Includes::path()` and `Includes::get()` methods now normalizes paths to be basenames to match the behaviour of the other include methods in https://github.com/hydephp/develop/pull/1738. This means that nested directories are no longer supported, as you should use a data collection for that.
- 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.
- Calling the `Include::path()` method will no longer create the includes directory in https://github.com/hydephp/develop/pull/1707
- Calling the `DataCollection` methods will no longer create the data collections directory in https://github.com/hydephp/develop/pull/1732
- Markdown includes are now converted to HTML using the custom HydePHP Markdown service, meaning they now support full GFM spec and custom Hyde features like colored blockquotes and code block filepath labels in https://github.com/hydephp/develop/pull/1738
- Markdown returned from includes are now trimmed of trailing whitespace and newlines in https://github.com/hydephp/develop/pull/1738

### Deprecated
- for soon-to-be removed features.
Expand Down Expand Up @@ -221,6 +225,25 @@ For example, if you triggered the media transfer with a build service method cal
(new TransferMediaAssets())->run();
```

### Includes facade changes

The following methods in the `Includes` facade now return `HtmlString` objects instead of `string`:

- `Includes::html()`
- `Includes::blade()`
- `Includes::markdown()`

- This means that you no longer need to use `{!! !!}` to render the output of these methods in Blade templates, instead just use `{{ }}`.
- If you have used the return value of these methods in custom code, you may need to adjust your code to work with the new return type.

For more information, see the RFC that proposed this change: https://github.com/hydephp/develop/issues/1734
The RFC was implemented in https://github.com/hydephp/develop/pull/1738

#### Remember to escape output if necessary

**Note:** Remember that this means that includes are **no longer escaped** by default, so make sure to escape the output if necessary, for example if the content is user-generated.
- (Use `{{ e(Includes::html('foo')) }}` instead of `{{ Includes::html('foo') }}` to escape the output, matching the previous behavior.)

### DataCollection API changes

The DataCollection feature has been reworked to improve the developer experience and make it more consistent with the rest of the API.
Expand Down
10 changes: 9 additions & 1 deletion docs/digging-deeper/helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,15 @@ Includes::markdown('example') === Includes::markdown('example.md');

All methods will return `null` if the file does not exist.
However, you can supply a default value as the second argument to be used instead.
Remember that Markdown and Blade defaults will still be rendered to HTML.
Remember that Markdown and Blade defaults will also be rendered to HTML.

Includes are particularly useful in Blade views, as you can echo them directly. You do not need to import the namespace, as it is already aliased.

```blade
<footer>
{{ Includes::markdown('footer.md') }}
</footer>
```

```php
use Hyde\Support\Includes;
Expand Down
2 changes: 2 additions & 0 deletions packages/framework/src/Framework/Services/MarkdownService.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Hyde\Facades\Config;
use Hyde\Facades\Features;
use Hyde\Markdown\Models\MarkdownDocument;
use Hyde\Framework\Concerns\Internal\SetsUpMarkdownConverter;
use Hyde\Pages\DocumentationPage;
use Hyde\Markdown\MarkdownConverter;
Expand Down Expand Up @@ -186,6 +187,7 @@ public function hasFeature(string $feature): bool
protected function determineIfTorchlightAttributionShouldBeInjected(): bool
{
return ! $this->isDocumentationPage()
&& ! (isset($this->pageClass) && $this->pageClass === MarkdownDocument::class)
&& Config::getBool('torchlight.attribution.enabled', true)
&& str_contains($this->html, 'Syntax highlighted by torchlight.dev');
}
Expand Down
100 changes: 61 additions & 39 deletions packages/framework/src/Support/Includes.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
namespace Hyde\Support;

use Hyde\Hyde;
use Hyde\Facades\Filesystem;
use Illuminate\Support\HtmlString;
use Hyde\Markdown\Models\MarkdownDocument;
use Hyde\Markdown\Models\Markdown;
use Illuminate\Support\Facades\Blade;

use function trim;
use function basename;
use function file_exists;
use function file_get_contents;

/**
* The Includes facade provides a simple way to access partials in the includes directory.
Expand All @@ -32,80 +34,100 @@ class Includes
*/
public static function path(?string $filename = null): string
{
return $filename === null
? Hyde::path(static::$includesDirectory)
: Hyde::path(static::$includesDirectory.'/'.$filename);
if ($filename === null) {
return Hyde::path(static::$includesDirectory);
}

return Hyde::path(static::$includesDirectory.'/'.static::normalizePath($filename));
}

/**
* Get the raw contents of a partial file in the includes directory.
*
* @param string $filename The name of the partial file, including the extension.
* @param string|null $default The default value to return if the partial is not found.
* @return string|null The contents of the partial file, or the default value if not found.
* @return string|null The raw contents of the partial file, or the default value if not found.
*/
public static function get(string $filename, ?string $default = null): ?string
{
$path = static::path($filename);

if (! file_exists($path)) {
return $default;
}

return file_get_contents($path);
return static::getInclude(fn (string $contents): string => $contents, $filename, $default);
}

/**
* Get the HTML contents of a partial file in the includes directory.
*
* @param string $filename The name of the partial file, with or without the extension.
* @param string|null $default The default value to return if the partial is not found.
* @return string|null The raw contents of the partial file, or the default value if not found.
* @return HtmlString|null The contents of the partial file, or the default value if not found.
*/
public static function html(string $filename, ?string $default = null): ?string
public static function html(string $filename, ?string $default = null): ?HtmlString
{
$path = static::path(basename($filename, '.html').'.html');

if (! file_exists($path)) {
return $default === null ? null : $default;
}

return file_get_contents($path);
return static::getInclude([static::class, 'renderHtml'], $filename, $default, '.html');
}

/**
* Get the rendered Markdown of a partial file in the includes directory.
* Get the rendered Blade of a partial file in the includes directory.
*
* @param string $filename The name of the partial file, with or without the extension.
* @param string|null $default The default value to return if the partial is not found.
* @return string|null The rendered contents of the partial file, or the default value if not found.
* @return HtmlString|null The rendered contents of the partial file, or the default value if not found.
*/
public static function markdown(string $filename, ?string $default = null): ?string
public static function blade(string $filename, ?string $default = null): ?HtmlString
{
$path = static::path(basename($filename, '.md').'.md');

if (! file_exists($path)) {
return $default === null ? null : Markdown::render($default);
}

return Markdown::render(file_get_contents($path));
return static::getInclude([static::class, 'renderBlade'], $filename, $default, '.blade.php');
}

/**
* Get the rendered Blade of a partial file in the includes directory.
* Get the rendered Markdown of a partial file in the includes directory.
*
* @param string $filename The name of the partial file, with or without the extension.
* @param string|null $default The default value to return if the partial is not found.
* @return string|null The rendered contents of the partial file, or the default value if not found.
* @return HtmlString|null The rendered contents of the partial file, or the default value if not found.
*/
public static function blade(string $filename, ?string $default = null): ?string
public static function markdown(string $filename, ?string $default = null): ?HtmlString
{
$path = static::path(basename($filename, '.blade.php').'.blade.php');
return static::getInclude([static::class, 'renderMarkdown'], $filename, $default, '.md');
}

if (! file_exists($path)) {
return $default === null ? null : Blade::render($default);
/** @param callable(string): (\Illuminate\Support\HtmlString|string) $method */
protected static function getInclude(callable $method, string $filename, ?string $default, string $extension = ''): HtmlString|string|null
{
$path = static::normalizePath($filename, $extension);
$contents = static::getFileContents(static::path($path));

if ($contents === null && $default === null) {
return null;
}

return $method($contents ?? $default);
}

protected static function normalizePath(string $filename, string $extension = ''): string
{
return basename($filename, $extension).$extension;
}

protected static function getFileContents(string $path): ?string
{
if (! Filesystem::exists($path)) {
return null;
}

return Blade::render(file_get_contents($path));
return Filesystem::get($path);
}

protected static function renderHtml(string $html): HtmlString
{
return new HtmlString($html);
}

protected static function renderBlade(string $blade): HtmlString
{
return new HtmlString(Blade::render($blade));
}

protected static function renderMarkdown(string $markdown): HtmlString
{
return new HtmlString(trim(Markdown::render($markdown, MarkdownDocument::class)));
}
}
Loading