Skip to content

Commit 2ee14d0

Browse files
authored
docs: improve markdown docs (#2146)
1 parent 0d77265 commit 2ee14d0

1 file changed

Lines changed: 227 additions & 5 deletions

File tree

docs/3-packages/04-markdown.md

Lines changed: 227 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Markdown
33
description: "Fast and extensible Markdown in PHP"
44
---
55

6-
`tempest/markdown` is a Markdown parser written in PHP. It's designed to be fast and extensible and has a bunch of additional features built-in like code highlighting, responsive images, tables, and frontmatter support.
6+
`tempest/markdown` is a Markdown parser for server-side Markdown parsing with PHP. It's designed to be fast and extensible, and has a bunch of extended Markdown features built-in like code highlighting, table and div support, responsive images, and frontmatter support.
77

88
## Quickstart
99

@@ -24,7 +24,9 @@ echo $parsed->frontMatter['title'];
2424
echo $parsed->html;
2525
```
2626

27-
## Integrations
27+
## Extended features
28+
29+
One thing that sets `tempest/markdown` apart from other Markdown implementations is that it comes with a bunch of extended features built-in.
2830

2931
### Code highlighting
3032

@@ -41,7 +43,7 @@ $markdown = new Markdown(
4143
);
4244
```
4345

44-
Language definitions work in both inline and pre code blocks:
46+
Language definitions work for both inline and multiline code blocks:
4547

4648
<pre>
4749
This is an inline PHP codeblock: `{php}echo "Hello";`
@@ -63,9 +65,28 @@ $markdown = new Markdown(
6365
);
6466
```
6567

68+
### Frontmatter
69+
70+
Add frontmatter like you're used to with other Markdown parsers
71+
72+
```md
73+
---
74+
title: Markdown
75+
description: "Fast and extensible Markdown in PHP"
76+
---
77+
78+
`tempest/markdown` is a Markdown parser for server-side Markdown parsing with PHP. It's designed to be fast and extensible, and has a bunch of extended Markdown features built-in like code highlighting, table and div support, responsive images, and frontmatter support.
79+
```
80+
81+
```php
82+
$parsed = $markdown->parse($contents);
83+
84+
echo $parsed->frontmatter['title'];
85+
```
86+
6687
### Responsive images
6788

68-
`tempest/markdown` has support for responsive images powered by [`tempest/responsive-image`](/docs/packages/responsive-image). You'll need to configure the responsive image factory before being able to use it.
89+
`tempest/markdown` has support for responsive images powered by [`tempest/responsive-image`](/docs/packages/responsive-image). You'll need to configure the responsive image factory to enable this feature.
6990

7091
```php
7192
use Tempest\Markdown\Markdown;
@@ -82,6 +103,207 @@ $markdown = new Markdown(
82103
);
83104
```
84105

106+
When enabled, `tempest/markdown` will generate different versions of the same image, and add them as `srcset` entries in the generate HTML.
107+
108+
```md
109+
![A parrot](/parrot.jpg)
110+
```
111+
112+
```html
113+
<img
114+
src="/parrot.jpg"
115+
alt="A parrot"
116+
srcset="/parrot-1920-1280.jpg 1920w, /parrot-1606-1070.jpg 1606w, /parrot-1214-809.jpg 1214w, /parrot-607-404.jpg 607w"
117+
>
118+
```
119+
120+
Read more about responsive images [here](/docs/packages/responsive-image).
121+
122+
### Tables
123+
124+
Add tables like you're used to with other Markdown parsers.
125+
126+
```
127+
| Package | Memory | Time to parse |
128+
|------------------------|----------|---------------|
129+
| tempest/markdown | 6.826mb | 13.273ms |
130+
| league/commonmark | 21.114mb | 56.993ms |
131+
| michelf/php-markdown | 7.343mb | 23.215ms |
132+
| erusev/parsedown-extra | 8.485mb | 15.163ms |
133+
```
134+
135+
```html
136+
137+
<table>
138+
<thead>
139+
<tr>
140+
<th>Package</th>
141+
<th>Memory</th>
142+
<th>Time to parse</th>
143+
</tr>
144+
</thead>
145+
<tbody>
146+
<tr>
147+
<td>tempest/markdown</td>
148+
<td>6.826mb</td>
149+
<td>13.273ms</td>
150+
</tr>
151+
152+
<!---->
153+
154+
</tbody>
155+
</table>
156+
```
157+
158+
### Divs
159+
160+
Use `:::` to create divs with optional classes:
161+
162+
```
163+
:::alert
164+
This is an important message!
165+
:::
166+
```
167+
168+
```html
169+
<div class="alert">
170+
This is an important message!
171+
</div>
172+
```
173+
174+
### Strikethrough
175+
176+
Use `~~` to strikethrough text:
177+
178+
```
179+
~~This was wrong~~
180+
```
181+
182+
```html
183+
<s>This was wrong</s>
184+
```
185+
186+
### Target blank links
187+
188+
Prepend `*` to a link's URI to open the link in a new tab:
189+
190+
```
191+
[Click me](*https://stitcher.io)
192+
```
193+
194+
```html
195+
<a href="https://stitcher.io" target="_blank" rel="noopener noreferrer">Click me</a>
196+
```
197+
198+
## Adding custom features
199+
200+
`tempest/markdown` is meant to be extended. Adding custom parser rules is done in two steps: first you provide a `LexerRule`, this is a class that determines when your custom parsing logic should be triggered. Next you'll use a `Token` to render your selected Markdown code in any way you'd like.
201+
202+
Let's work with an example. Say you want to add support for including custom HTML snippets. It could look something like this:
203+
204+
```
205+
Hello world
206+
207+
{{ snippets/call-to-action.html }}
208+
```
209+
210+
The first step to adding this new feature is detecting when we run into our custom `{{ }}` syntax. This is done with a `LexerRule`. Let's call our implementation `SnippetRule`:
211+
212+
```php
213+
use Tempest\Markdown\Lexer;
214+
use Tempest\Markdown\Rule;
215+
use Tempest\Markdown\Token;
216+
217+
final readonly class SnippetRule implements Rule
218+
{
219+
public function shouldLex(Lexer $lexer): bool
220+
{
221+
// Our rule takes effect as soon as we run into `{{`
222+
223+
return $lexer->comesNext('{{', length: 2);
224+
}
225+
226+
public function lex(Lexer $lexer): ?Token
227+
{
228+
// We'll consume all { characters
229+
$lexer->consumeWhile('{');
230+
231+
// Then we'll consume and store the snippet path itself until we encounter the closing } characters
232+
$snippet = $lexer->consumeUntil('}');
233+
234+
// Then we'll consume the closing } characters
235+
$lexer->consumeWhile('}');
236+
237+
// Finally, we return a token with the snippet
238+
return new SnippetToken(trim($snippet));
239+
}
240+
}
241+
```
242+
243+
:::info
244+
For performance reasons, it's best to explcitly add the `{:hl-property:length:}` parameter to the `{:hl-property:comesNext:}` call. It's not required, but it will make parsing faster. If possible, also try not to rely on regex within your lexer rules, as it may become a performance bottleneck.
245+
:::
246+
247+
So that's our rule implementation: we consumed our custom `{{ path }}` syntax, and created a token with that path. Let's take a look at the token implementation next.
248+
249+
The token's responsibility is to parse the content into HTML.
250+
251+
```php
252+
use Tempest\Markdown\Parser;
253+
use Tempest\Markdown\Token;
254+
use Tempest\View\Exceptions\ViewNotFound;
255+
use Tempest\View\ViewRenderer;
256+
use function Tempest\Container\get;
257+
258+
final readonly class SnippetToken implements Token
259+
{
260+
public function __construct(
261+
public string $path,
262+
) {}
263+
264+
public function parse(Parser $parser): string
265+
{
266+
// Of course, you should add validation here depending on your use case
267+
268+
return file_get_contents($this->path);
269+
}
270+
}
271+
```
272+
273+
Finally, you should add your custom rule to the Markdown parser:
274+
275+
```php
276+
use Tempest\Markdown\Markdown;
277+
278+
$markdown = new Markdown();
279+
280+
$markdown->prependRules(
281+
new SnippetRule(),
282+
);
283+
```
284+
285+
If needed, you can fully customize the Markdown parser by overwriting all rules:
286+
287+
```php
288+
$markdown = new Markdown()->withRules(
289+
new NewLineRule(),
290+
new FrontMatterRule(),
291+
new HeadingRule(),
292+
new QuoteRule(),
293+
new PreRule(),
294+
new DivRule(),
295+
new ThinRulerRule(),
296+
new ThickRulerRule(),
297+
new ListRule(),
298+
new OrderedListRule(),
299+
new TableRule(),
300+
new HtmlRule(),
301+
new ParagraphRule(),
302+
);
303+
```
304+
305+
This way you have full control over how the parser works. For more inspiration, you can look at the [rules that come built-in with the package](https://github.com/tempestphp/markdown/tree/main/src/LexerRules).
306+
85307
## Performance
86308

87309
This package began as a challenge to make a more performant Markdown parser in pure PHP. The primary performance gain is from not relying on regex but instead using a simple lexer to tokenize Markdown files and convert them to HTML.
@@ -90,7 +312,7 @@ Benchmarks are included in this repo and can be run with `composer bench` after
90312

91313
| Package | Memory | Time to parse |
92314
|------------------------|----------|---------------|
93-
| tempest/markdown | 5.944mb | 6.281ms |
315+
| tempest/markdown | 6.826mb | 13.273ms |
94316
| league/commonmark | 21.114mb | 56.993ms |
95317
| michelf/php-markdown | 7.343mb | 23.215ms |
96318
| erusev/parsedown-extra | 8.485mb | 15.163ms |

0 commit comments

Comments
 (0)