Skip to content

Commit

Permalink
Merge pull request #1590 from hydephp/dynamic-markdown-links
Browse files Browse the repository at this point in the history
[2.x] Dynamic Markdown links
  • Loading branch information
caendesilva authored Jul 25, 2024
2 parents 2df0527 + 2ae3ef9 commit 158a7b1
Show file tree
Hide file tree
Showing 7 changed files with 682 additions and 0 deletions.
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

0 comments on commit 158a7b1

Please sign in to comment.