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] Dynamic Markdown links #1590

Merged
merged 92 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
37e5921
Initial feature abstract
caendesilva Feb 25, 2024
1f99128
Add examples
caendesilva Feb 25, 2024
04dcd92
Streamline examples
caendesilva Feb 25, 2024
eec5887
Match the Hyde facade methods
caendesilva Jul 24, 2024
397da8e
Create DynamicMarkdownLinksTest.php
caendesilva Jul 24, 2024
63bab3b
Create DynamicMarkdownLinkProcessor.php
caendesilva Jul 24, 2024
d1ee654
Implements MarkdownPostProcessorContract
caendesilva Jul 24, 2024
a2e3052
Draft postprocess method
caendesilva Jul 24, 2024
59b0618
Revert "Create DynamicMarkdownLinksTest.php"
caendesilva Jul 24, 2024
47bfef5
Create DynamicMarkdownLinkProcessorTest.php
caendesilva Jul 24, 2024
9a08865
Implement the initial dynamic markdown link feature
caendesilva Jul 24, 2024
6ec38dd
Refactor to use strict types
caendesilva Jul 24, 2024
eabee96
Extract helper method
caendesilva Jul 24, 2024
3c91dd7
Supress faulty inspection
caendesilva Jul 24, 2024
a2c7879
Use more readable formatting for complex test fixture
caendesilva Jul 24, 2024
33ba914
Use assert same instead of assert equals
caendesilva Jul 24, 2024
15ecda8
Update Markdown link support to work without single quotes
caendesilva Jul 24, 2024
048910d
Update Markdown link support to work with double quotes
caendesilva Jul 24, 2024
017355f
Split out multiple assertions to dedicated unit tests
caendesilva Jul 24, 2024
df09449
Revert "Update Markdown link support to work with double quotes"
caendesilva Jul 24, 2024
55df5a3
Register the dynamic Markdown link processor
caendesilva Jul 24, 2024
16051f7
Reapply "Create DynamicMarkdownLinksTest.php"
caendesilva Jul 24, 2024
b0fe2bb
Rename test to DynamicMarkdownLinksFeatureTest
caendesilva Jul 24, 2024
69a5277
Set up coverage
caendesilva Jul 24, 2024
6a7f46a
Add test crosslinks
caendesilva Jul 24, 2024
8586016
Match heredoc name to type
caendesilva Jul 24, 2024
0813aa1
Merge branch '2.x-dev' into dynamic-markdown-links
caendesilva Jul 24, 2024
e943c19
Implement the basic test
caendesilva Jul 24, 2024
d489d90
Generate more tests with Claude
caendesilva Jul 24, 2024
a8aeb8b
Skip unsupported test
caendesilva Jul 24, 2024
b10b55a
Add fault tolerance testing
caendesilva Jul 24, 2024
d105152
Section test code
caendesilva Jul 24, 2024
f2d02fb
Add more fault tolerance tests
caendesilva Jul 24, 2024
ad20f39
Throw an exception for non existent routes
caendesilva Jul 24, 2024
61a552b
Extract helper method
caendesilva Jul 24, 2024
ee000ce
Add newline
caendesilva Jul 24, 2024
80edd88
Add newline
caendesilva Jul 24, 2024
8e1e516
Try to find the faulty line
caendesilva Jul 24, 2024
1238315
Try to find the line number of the error
caendesilva Jul 24, 2024
5fa5791
Extract method
caendesilva Jul 24, 2024
475c8ef
Test the line finding
caendesilva Jul 24, 2024
5da5b2f
Revert out of scope line finder
caendesilva Jul 24, 2024
2bf321c
Generate new documentation with Claude
caendesilva Jul 24, 2024
6d43f73
Proof generated documentation
caendesilva Jul 24, 2024
5e96cda
Streamline documentation
caendesilva Jul 24, 2024
a2b256b
Fix documentation location
caendesilva Jul 24, 2024
9589bc1
Remove dynamic relative link Markdown feature offering little value
caendesilva Jul 24, 2024
d4d8762
New proof of concept
caendesilva Jul 24, 2024
9e613da
Improve proof of concept
caendesilva Jul 24, 2024
6a5f159
Prepare to refactor proof of concept
caendesilva Jul 25, 2024
9c19e50
Document new dynamic Markdown link feature spec
caendesilva Jul 25, 2024
70deb8c
Remove the patterns method
caendesilva Jul 25, 2024
780b871
Remove the patterns loop
caendesilva Jul 25, 2024
dee2c96
Remove unused import
caendesilva Jul 25, 2024
642ec73
Draft new processing loop
caendesilva Jul 25, 2024
2b35be2
Convert concatenation to 'sprintf()' calls
caendesilva Jul 25, 2024
2afc335
Implement the route map method
caendesilva Jul 25, 2024
04a0a7c
Implement the asset map method
caendesilva Jul 25, 2024
48efa5e
Implement the asset path method
caendesilva Jul 25, 2024
f8c2c22
Add todo
caendesilva Jul 25, 2024
b5d8f71
Update imports
caendesilva Jul 25, 2024
37b8f84
Add import
caendesilva Jul 25, 2024
a44357d
Update route mocks
caendesilva Jul 25, 2024
c175cb5
Remove old exception test
caendesilva Jul 25, 2024
a8be72d
Remove old fault tolerance tests
caendesilva Jul 25, 2024
77af426
Remove old fault tolerance tests
caendesilva Jul 25, 2024
c3a46c9
Add new fault tolerance tests
caendesilva Jul 25, 2024
b79d983
Update test for the new dynamic Markdown link feature
caendesilva Jul 25, 2024
c644501
Use existing file pending mock support
caendesilva Jul 25, 2024
6e50913
Replace feature test setup with lower level filesystem configuration
caendesilva Jul 25, 2024
6d6d753
Tear down filesystem after class
caendesilva Jul 25, 2024
48558fa
Update test for the new dynamic Markdown link feature
caendesilva Jul 25, 2024
60786a5
Add more high level feature test cases
caendesilva Jul 25, 2024
75fc296
Update unit test to mock the kernel routes
caendesilva Jul 25, 2024
f0e0064
Add static variable to cache the asset map in memory
caendesilva Jul 25, 2024
25a7328
Refactor static variable to a static property
caendesilva Jul 25, 2024
ae46463
Add internal testing helper to reset the asset map cache
caendesilva Jul 25, 2024
8755f49
Reset the asset map cache
caendesilva Jul 25, 2024
1b7bfcb
Ignore coverage
caendesilva Jul 25, 2024
7e7927b
Revert "Ignore coverage"
caendesilva Jul 25, 2024
76672b2
Test the method
caendesilva Jul 25, 2024
7ed5a85
Compress PHPDoc annotations
caendesilva Jul 25, 2024
376d3ec
Normalize the signatures to be looser and match images
caendesilva Jul 25, 2024
831c83c
Normalize the loop variable name
caendesilva Jul 25, 2024
851b17a
Refactor to extract methods for common logic
caendesilva Jul 25, 2024
ede0cdf
Revert "Refactor to extract methods for common logic"
caendesilva Jul 25, 2024
4a46a58
Document that relative links is not supported here
caendesilva Jul 25, 2024
f4c9984
Merge pull request #1901 from hydephp/improved-dynamic-markdown-links
caendesilva Jul 25, 2024
5cbde9c
Add more feature tests
caendesilva Jul 25, 2024
a2bd446
Clean up the documentation
caendesilva Jul 25, 2024
a307b88
Proof the documentation
caendesilva Jul 25, 2024
2ae3ef9
Update RELEASE_NOTES.md
caendesilva Jul 25, 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
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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 support for resolving dynamic links to source files in Markdown documents in https://github.com/hydephp/develop/pull/1590
- 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
- Added a new `\Hyde\Framework\Exceptions\InvalidConfigurationException` exception class to handle invalid configuration exceptions in https://github.com/hydephp/develop/pull/1799
Expand Down
47 changes: 47 additions & 0 deletions docs/digging-deeper/advanced-markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,53 @@ anything within the path label will be rendered as HTML. This means you can add

The filepaths are hidden on mobile devices using CSS to prevent them from overlapping with the code block.


## Dynamic Markdown Links

HydePHP provides a powerful feature for automatically converting Markdown links to source files to the corresponding routes in the built site.

This allows for a much better writing experience when using an IDE, as you can easily navigate to the source file by clicking on the link. Hyde will then automatically resolve the link to the correct route when building the site, formatting the links properly using dynamic relative paths and your configured `pretty_urls` setting.

## Usage

Using the feature is simple: Just use the source file path when linking to the page you want to resolve:

```markdown
[Home](/_pages/index.blade.php)
[Docs](/_docs/index.md)
[Featured Post](/_posts/hello-world.md)
![Logo](/_media/logo.svg)
```

As you can see, it works for both pages and media assets. The leading slash is optional and will be ignored by Hyde, but including it often gives better IDE support.

### Behind the Scenes

During the build process, HydePHP converts source paths to their corresponding routes and evaluates them depending on the page being rendered.

If your page is in the site root then:

- `/_pages/index.blade.php` becomes `index.html`
- `/_media/logo.svg` becomes `media/logo.svg`

If your page is in a subdirectory then:

- `/_pages/index.blade.php` becomes `../index.html`
- `/_media/logo.svg` becomes `../media/logo.svg`

Of course, if your page is in a more deeply nested directory, the number of `../` will increase accordingly.

We will also match your configured preference for `pretty_urls` and only include the `.html` extension when desired.

### Limitations

There are some limitations and considerations to keep in mind when using this feature:

- This feature will not work for dynamic routes (not backed by a file)
- If you rename a file, links will break. Your IDE may warn about this.
- If a file is not found, we will not be able to see it when evaluating links.
- Relative links are not supported (so `../_pages/index.blade.php` will not work)

## Configuration

### Full configuration reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Hyde\Markdown\Processing\BladeDownProcessor;
use Hyde\Markdown\Processing\ShortcodeProcessor;
use Hyde\Markdown\Processing\CodeblockFilepathProcessor;
use Hyde\Markdown\Processing\DynamicMarkdownLinkProcessor;
use Torchlight\Commonmark\V2\TorchlightExtension;

use function array_merge;
Expand Down Expand Up @@ -73,6 +74,8 @@ protected function registerPostProcessors(): void
CodeblockFilepathProcessor::class,
Config::getBool('markdown.features.codeblock_filepaths', true)
);

$this->registerPostProcessor(DynamicMarkdownLinkProcessor::class);
}

protected function registerPreProcessor(string $class, bool $when = true): void
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

namespace Hyde\Markdown\Processing;

use Hyde\Hyde;
use Illuminate\Support\Str;
use Hyde\Support\Filesystem\MediaFile;
use Hyde\Markdown\Contracts\MarkdownPostProcessorContract;

class DynamicMarkdownLinkProcessor implements MarkdownPostProcessorContract
{
/** @var array<string, \Hyde\Support\Filesystem\MediaFile>|null */
protected static ?array $assetMapCache = null;

public static function postprocess(string $html): string
{
foreach (static::routeMap() as $sourcePath => $route) {
$patterns = [
sprintf('<a href="%s"', $sourcePath),
sprintf('<a href="/%s"', $sourcePath),
];

$html = str_replace($patterns, sprintf('<a href="%s"', $route->getLink()), $html);
}

foreach (static::assetMap() as $sourcePath => $mediaFile) {
$patterns = [
sprintf('<img src="%s"', $sourcePath),
sprintf('<img src="/%s"', $sourcePath),
];

$html = str_replace($patterns, sprintf('<img src="%s"', static::assetPath($mediaFile)), $html);
}

return $html;
}

/** @return array<string, \Hyde\Support\Models\Route> */
protected static function routeMap(): array
{
$map = [];

/** @var \Hyde\Support\Models\Route $route */
foreach (Hyde::routes() as $route) {
$map[$route->getSourcePath()] = $route;
}

return $map;
}

/** @return array<string, \Hyde\Support\Filesystem\MediaFile> */
protected static function assetMap(): array
{
if (static::$assetMapCache === null) {
static::$assetMapCache = [];

foreach (MediaFile::all() as $mediaFile) {
static::$assetMapCache[$mediaFile->getPath()] = $mediaFile;
}
}

return static::$assetMapCache;
}

protected static function assetPath(MediaFile $mediaFile): string
{
return Hyde::asset(Str::after($mediaFile->getPath(), '_media/'));
}

/** @internal Testing helper to reset the asset map cache. */
public static function resetAssetMapCache(): void
{
static::$assetMapCache = null;
}
}
Loading