antonioprimera/md-parser

自定义Markdown处理器

v1.3 2024-08-28 22:42 UTC

This package is auto-updated.

Last update: 2024-08-28 22:43:27 UTC


README

这是一个高度可定制的Markdown解析器,它包含一个预定义的Markdown风味,但允许您轻松创建任何其他Markdown风味,无论是标准的还是您刚刚发明的。

例如,您可以创建一个Markdown风味,允许您通过ID(存储在数据库中的对象的ID)或slug嵌入图像,或者可能是一个允许您通过名称嵌入hero-icon的自定义标签。您甚至可以决定段落应该由一个|字符而不是换行符来分隔。

以下是一些您可以使用此解析器轻松完成的示例

An image:
![This is the alt text for an image for an image determined by a slug](welcome-image)
![This is the alt text for an image with an id, as stored in the DB](840)

A list of items with hero-icons:
## Contact
[heroicon:phone] [+10 (1234) 567 890](tel:+10 1234.567.890)
[heroicon:envelope] [Email me](abc@def.test)

A gallery:
!!![This is the title of the gallery with 3 images](/img/gallery/1.jpg | /img/gallery/2.jpg | /img/gallery/3.jpg)

Or even a special internal link to an article with a slug:
[Read more about this](article:my-article-slug)

基本用法(框架无关)

主解析器是一个类,它通过一组内联解析器、一组块解析器、可选的块分割器和可选的配置来实例化。实例化后,您可以调用parse方法,传入Markdown字符串,它将返回解析结果。

首先,通过composer导入包

composer require antonio-primera/md-parser

然后,您可以使用默认风味,或创建自己的。

如果您想使用默认风味,可以这样做

$parser = \AntonioPrimera\Md\MarkdownFlavors\BasicMarkdownParser::create();
$html = $parser->parse($markdownString);

如果您想创建自己的解析器,或只是更改默认解析器的行为,请查看下面的高级用法部分。

在Laravel项目中使用

如果您在Laravel项目中使用此包,所有设置都已为您完成,因此您可以像这样使用Md外观

use AntonioPrimera\Md\Facades\Md;

$html = Md::parse($markdownString);

默认情况下,此包的服务提供者将使用config/md-config.php文件中的配置来创建解析器实例。如果您想更改配置,可以通过运行以下命令发布配置文件

php artisan vendor:publish --tag=md-config

您有两个主要选项使用配置来构建解析器

  1. 在配置文件中提供一个flavor.factory条目,其中包含您的工厂类的类,该工厂将返回一个解析器实例,并可选地提供一个flavor.config条目,其中包含传递给工厂的配置。
  2. 为解析器提供必要的构建块:flavor.inlineParsersflavor.blockParsersflavor.splitter(这是可选的)和flavor.config(这也是可选的)。

高级用法

如果您想创建自己的风味,可以通过创建一个返回AntonioPrimera\Md\Parser实例的工厂类来实现,该实例加载了您的自定义块和内联解析器,或者混合和匹配现有的。

以下是默认风味是如何创建的

class BasicMarkdownParser
{
	
	public static function create(array $config = []): Parser
	{
		return new Parser(
			inlineParsers: [
				new ImageParser(),	//this has to go before the LinkParser, because it uses a similar syntax
				new LinkParser(),
				new BoldParser(),
				new ItalicParser(),
				new LineBreakParser(),	//this has to go last, because other parsers may use "|" in their syntax
			],
			blockParsers: [
				new HeadingParser(['baseHeadingLevel' => $config['baseHeadingLevel'] ?? 2]),
				new UnorderedListItemParser(),
				new BlockQuoteParser(),
				new ParagraphParser(),
			],
			config: $config
		);
	}
}

块解析器

块解析器是一个继承自AntonioPrimera\Md\BlockParser类的类。它有两个方法:matches用于检查块是否匹配此解析器并应由它处理,以及parse用于解析块。一旦块被块解析器匹配和解析,结果将被添加到解析结果中,并停止其解析。

一个简单的块解析器,它会解析空白行(只包含空白字符的块)将看起来像这样

class EmptyBlockParser extends BlockParser
{
	public function matches(string $text, array $parsedBlocks = []): bool
	{
	    //this parser will match empty blocks (blocks that contain only whitespace)
		return empty(trim($text));
	}
	
	public function parse(string $text, array &$parsedBlocks = []): string|MarkdownBlock|null
	{
	    //instead of returning an empty string, we return a special class that will be styled with CSS
		return '<p class="empty-block"></p>';
	}
}

内联解析器

内联解析器是一个继承自 AntonioPrimera\Md\InlineParser 类的类。它有一个单独的 parse 方法,用于在给定文本中查找和替换内联元素。解析器首先在文本上运行所有内联解析器,然后将文本分割成块,并对每个块运行块解析器。

一个简单的内联解析器,用于解析类似 [heroicon:icon-name] 的英雄图标语法,可能看起来像这样

class HeroIconParser extends InlineParser
{
	public function parse(string $text): string
    {
        $pattern = '/\[heroicon:(.+)\]/';
        return preg_replace_callback($pattern, fn($matches) => "<i class='heroicon heroicon-$matches[1]'></i>", $text);
    }
}

分割器

Markdown 分割器是一个扩展自 AntonioPrimera\Md\Splitters\MarkdownSplitter 类的类。它有一个单独的 split 方法,接收一个 Markdown 字符串并返回一个块数组。然后这些块被块解析器解析。

在实例化解析器时,你可以传递一个分割器实例(如果你自己实现的),或者你可以传递一个字符串,该字符串将被用作分割 Markdown 字符串为块的分隔符。默认情况下,使用换行符 \n

配置

在实例化解析器时,你可以为每个内联解析器和块解析器实例传递一个配置,或者你可以只传递一个配置数组,该数组将被部分转发到所有解析器,基于它们的别名。

这样做是为了将配置保存在一个地方,或者允许动态配置(例如,基于存储在数据库中的某些用户输入)。

每个内联解析器和块解析器都有一个别名,默认情况下是去掉 parser 后缀的 kebab-case 类基名(例如,HeroIconParser 的别名为 hero-icon)。如果你想在解析器类中使用不同的别名,可以覆盖别名方法。然后你可以使用别名作为配置数组中的键来传递该解析器的配置。

$parser = new Parser(
    inlineParsers: [new HeroIconParser()],  //and other inline parsers
    blockParsers: [new HeadingParser()],    //and other block parsers
    config: [
        'hero-icon' => ['iconClass' => 'heroicon', 'iconSubset' => 'heroicon-solid'],
        'heading' => ['baseHeadingLevel' => 3]
    ]
);

这与直接传递配置到解析器实例相同

$parser = new Parser(
    inlineParsers: [new HeroIconParser(['iconClass' => 'heroicon', 'iconSubset' => 'heroicon-solid'])],
    blockParsers: [new HeadingParser(['baseHeadingLevel' => 3])],
);

贡献

还需要完成什么

  • 优化解析器(例如,进行基准测试和找到瓶颈)
  • 添加更多测试
  • 添加更多节点解析器(如代码块解析器、有序列表解析器等)
  • 添加更多内联解析器(如英雄图标解析器等)
  • 使这份文档更好

如果你想贡献,请随意fork仓库并提交一个pull request。如果你有任何问题,请随时提问。

Markdown解析器的多数规范基于以下资源