DocPage objects." /> DocPage objects." /> DocPage objects." />
Class Sealed
public sealed class MarkdownParsePhase : IBuildPhase

Namespace: Moka.Docs.Engine.Phases

Parses all discovered Markdown files into DocPage objects.

Inheritance

Inherits from: IBuildPhase

Constructors

NameDescription
MarkdownParsePhase(MarkdownParser markdownParser, MarkdownParsePhase> logger) Parses all discovered Markdown files into DocPage objects.

MarkdownParsePhase(MarkdownParser markdownParser, MarkdownParsePhase> logger)

MarkdownParsePhase.MarkdownParsePhase(MarkdownParser markdownParser, ILogger<MarkdownParsePhase> logger)

Parses all discovered Markdown files into DocPage objects.

Properties

NameDescription
Name
Order

Methods

Type Relationships
classDiagram
                    style MarkdownParsePhase fill:#f9f,stroke:#333,stroke-width:2px
                    MarkdownParsePhase --|> IBuildPhase : inherits
                
View Source
/// <summary>
///     Parses all discovered Markdown files into <see cref = "DocPage"/> objects.
/// </summary>
public sealed class MarkdownParsePhase(MarkdownParser markdownParser, ILogger<MarkdownParsePhase> logger) : IBuildPhase
{
    /// <inheritdoc/>
    public string Name => "MarkdownParse";
    /// <inheritdoc/>
    public int Order => 400;

    /// <inheritdoc/>
    public Task ExecuteAsync(BuildContext context, CancellationToken ct = default)
    {
        string docsPath = context.FileSystem.Path.GetFullPath(context.FileSystem.Path.Combine(context.RootDirectory, context.Config.Content.Docs));
        foreach (string relativePath in context.DiscoveredMarkdownFiles)
        {
            ct.ThrowIfCancellationRequested();
            string fullPath = context.FileSystem.Path.Combine(docsPath, relativePath);
            try
            {
                string markdown = context.FileSystem.File.ReadAllText(fullPath);
                MarkdownParseResult result = markdownParser.Parse(markdown);
                string route = BuildRoute(relativePath, result.FrontMatter);
                var page = new DocPage
                {
                    FrontMatter = result.FrontMatter,
                    Content = new PageContent
                    {
                        Html = result.Html,
                        PlainText = result.PlainText
                    },
                    TableOfContents = result.TableOfContents,
                    SourcePath = relativePath,
                    Route = route,
                    Origin = PageOrigin.Markdown,
                    LastModified = GetLastModified(context, fullPath)
                };
                context.Pages.Add(page);
            }
            catch (Exception ex)
            {
                context.Diagnostics.Warning($"Failed to parse {relativePath}: {ex.Message}", Name);
                logger.LogWarning(ex, "Failed to parse Markdown file: {Path}", relativePath);
            }
        }

        logger.LogInformation("Parsed {Count} Markdown pages", context.Pages.Count);
        return Task.CompletedTask;
    }

    private static string BuildRoute(string relativePath, FrontMatter frontMatter)
    {
        // Use custom route from front matter if specified
        if (!string.IsNullOrEmpty(frontMatter.Route))
        {
            return frontMatter.Route;
        }

        // Convert file path to URL route
        string route = "/" + relativePath.Replace('\\', '/').Replace(".md", "", StringComparison.OrdinalIgnoreCase);
        // index.md → parent directory route
        if (route.EndsWith("/index", StringComparison.OrdinalIgnoreCase))
        {
            route = route[..^6];
        }

        if (string.IsNullOrEmpty(route))
        {
            route = "/";
        }

        return route;
    }

    private static DateTimeOffset? GetLastModified(BuildContext context, string fullPath)
    {
        try
        {
            IFileInfo info = context.FileSystem.FileInfo.New(fullPath);
            return info.Exists ? new DateTimeOffset(info.LastWriteTimeUtc, TimeSpan.Zero) : null;
        }
        catch
        {
            return null;
        }
    }
}
Was this page helpful?