Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
2c197f7
Add CLI command documentation drafts
Mpdreamz Sep 29, 2025
771b84b
organize cli command documentation
Mpdreamz Sep 30, 2025
f72e837
Merge remote-tracking branch 'origin/main' into feature/cli-documenta…
Mpdreamz Sep 30, 2025
335d7a6
stage changes
Mpdreamz Sep 30, 2025
9b65802
Finish documenting cli commands
Mpdreamz Sep 30, 2025
69bf2d2
Cleanup migration
Mpdreamz Sep 30, 2025
1daf6c8
touchups
Mpdreamz Sep 30, 2025
a030f85
stage
Mpdreamz Sep 30, 2025
8ed7a3b
stage
Mpdreamz Sep 30, 2025
0bfe134
First pass of claude over building blocks
Mpdreamz Sep 30, 2025
7887ee2
Expanded building blocks
Mpdreamz Oct 1, 2025
6d7f430
update building blocks
Mpdreamz Oct 1, 2025
9a9f20b
update building blocks
Mpdreamz Oct 1, 2025
0c617c5
Include navigation building blocks
Mpdreamz Oct 1, 2025
e593ff0
Small touchups
Mpdreamz Oct 1, 2025
2e0cfd4
update redirects
Mpdreamz Oct 1, 2025
ea39746
Apply suggestions from code review
Mpdreamz Oct 1, 2025
b3b5309
Sentence case
Mpdreamz Oct 1, 2025
eb5f50d
fix links
Mpdreamz Oct 1, 2025
73d827f
List items full stop
Mpdreamz Oct 1, 2025
9a0d94d
colons in list not dashes
Mpdreamz Oct 1, 2025
d552c73
fix test
Mpdreamz Oct 1, 2025
62e97f7
Start on isolated docset config reader
Mpdreamz Oct 1, 2025
855b2b6
Stage before invoking AI
Mpdreamz Oct 1, 2025
1b3aac0
Start on new isolated navigation building
Mpdreamz Oct 2, 2025
b55e06b
toc nesting restrictions
Mpdreamz Oct 2, 2025
011584c
Continue with documentation set navigation, generated a lot of tests,…
Mpdreamz Oct 2, 2025
0384945
Reorg tests
Mpdreamz Oct 2, 2025
c2d9780
chop of /index
Mpdreamz Oct 2, 2025
8a73ddb
encapsulate more rules
Mpdreamz Oct 2, 2025
273908b
explicitly return IndexFileRef when deserialzing the configuration fo…
Mpdreamz Oct 2, 2025
9a103cc
Read through nested toc.yml file definitions
Mpdreamz Oct 2, 2025
295c823
Brand new navigation.yml deserialization
Mpdreamz Oct 2, 2025
0b7a9b4
Initial version of SiteNavigation
Mpdreamz Oct 2, 2025
1fae41b
Post claude test fixes
Mpdreamz Oct 2, 2025
faf31ea
Start on SiteDocumentationNavigation tests
Mpdreamz Oct 2, 2025
3853d09
stage broken state
Mpdreamz Oct 2, 2025
26daf61
stage
Mpdreamz Oct 2, 2025
5835e21
stage
Mpdreamz Oct 2, 2025
14bcfeb
fix tests manually
Mpdreamz Oct 2, 2025
c23eb1b
path_prefix is required now and added more tests
Mpdreamz Oct 2, 2025
763121e
Stage work on path prefix providers
Mpdreamz Oct 2, 2025
ac68fe5
Stage work tests passing again
Mpdreamz Oct 2, 2025
f079581
Add failing test
Mpdreamz Oct 3, 2025
9345203
Refactor TOC navigation handling to simplify nested validation logic …
Mpdreamz Oct 3, 2025
3358f74
fix tests
Mpdreamz Oct 3, 2025
1f4ffc6
Merge remote-tracking branch 'origin/main' into refactor/navigation
Mpdreamz Oct 3, 2025
0cb96f1
dotnet format namespaces
Mpdreamz Oct 3, 2025
12ce150
dotnet format namespaces
Mpdreamz Oct 3, 2025
754b191
Merge remote-tracking branch 'origin/main' into refactor/navigation
Mpdreamz Oct 3, 2025
baeba6e
Add explicit YamlMember
Mpdreamz Oct 3, 2025
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# docs-builder
#ocs-builder

[![ci](https://github.com/elastic/docs-builder/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/elastic/docs-builder/actions/workflows/ci.yml)

Expand Down
304 changes: 298 additions & 6 deletions docs-builder.sln

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Elastic.ApiExplorer/ApiRenderContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using Elastic.Documentation;
using Elastic.Documentation.Configuration;
using Elastic.Documentation.Navigation;
using Elastic.Documentation.Site.FileProviders;
using Elastic.Documentation.Site.Navigation;
using Microsoft.OpenApi.Models;
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.ApiExplorer/ApiViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
using Elastic.Documentation.Configuration.Assembler;
using Elastic.Documentation.Configuration.Builder;
using Elastic.Documentation.Extensions;
using Elastic.Documentation.Navigation;
using Elastic.Documentation.Site;
using Elastic.Documentation.Site.FileProviders;
using Elastic.Documentation.Site.Navigation;
using Microsoft.AspNetCore.Html;

namespace Elastic.ApiExplorer;
Expand Down
1 change: 1 addition & 0 deletions src/Elastic.ApiExplorer/Endpoints/ApiEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information

using System.IO.Abstractions;
using Elastic.Documentation.Navigation;
using Elastic.Documentation.Site.Navigation;
using Microsoft.OpenApi.Models.Interfaces;
using RazorSlices;
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.ApiExplorer/Landing/LandingNavigationItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.IO.Abstractions;
using Elastic.ApiExplorer.Operations;
using Elastic.Documentation.Extensions;
using Elastic.Documentation.Site.Navigation;
using Elastic.Documentation.Navigation;
using RazorSlices;

namespace Elastic.ApiExplorer.Landing;
Expand Down
1 change: 1 addition & 0 deletions src/Elastic.ApiExplorer/Landing/LandingView.cshtml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@inherits RazorSliceHttpResult<Elastic.ApiExplorer.Landing.LandingViewModel>
@using Elastic.ApiExplorer.Landing
@using Elastic.ApiExplorer.Operations
@using Elastic.Documentation.Navigation
@using Elastic.Documentation.Site.Navigation
@implements IUsesLayout<Elastic.ApiExplorer._Layout, GlobalLayoutViewModel>
@functions {
Expand Down
1 change: 1 addition & 0 deletions src/Elastic.ApiExplorer/OpenApiGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Elastic.ApiExplorer.Operations;
using Elastic.Documentation;
using Elastic.Documentation.Configuration;
using Elastic.Documentation.Navigation;
using Elastic.Documentation.Site.FileProviders;
using Elastic.Documentation.Site.Navigation;
using Microsoft.Extensions.Logging;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.IO.Abstractions;
using Elastic.ApiExplorer.Landing;
using Elastic.Documentation.Extensions;
using Elastic.Documentation.Site.Navigation;
using Elastic.Documentation.Navigation;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Models.Interfaces;
using RazorSlices;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Text.RegularExpressions;
using Elastic.Documentation.Extensions;
using YamlDotNet.Serialization;
using YamlStaticContext = Elastic.Documentation.Configuration.Serialization.YamlStaticContext;

namespace Elastic.Documentation.Configuration.Assembler;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO.Abstractions;
using System.Text.RegularExpressions;
using Elastic.Documentation.Configuration.Assembler;
using Elastic.Documentation.Configuration.DocSet;
using Elastic.Documentation.Configuration.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
Expand All @@ -20,7 +21,10 @@ public partial class ConfigurationFileProvider
private readonly ILogger<ConfigurationFileProvider> _logger;

internal static IDeserializer Deserializer { get; } = new StaticDeserializerBuilder(new YamlStaticContext())
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithTypeConverter(new TocItemCollectionYamlConverter())
.WithTypeConverter(new TocItemYamlConverter())
.WithTypeConverter(new SiteTableOfContentsCollectionYamlConverter())
.WithTypeConverter(new SiteTableOfContentsRefYamlConverter())
.Build();

public ConfigurationSource ConfigurationSource { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

namespace Elastic.Documentation.Configuration.DocSet;

[YamlSerializable]
public class TableOfContentsFile
{
[YamlMember(Alias = "toc")]
public TableOfContents Toc { get; set; } = [];

public static TableOfContentsFile Deserialize(string json) =>
ConfigurationFileProvider.Deserializer.Deserialize<TableOfContentsFile>(json);
}

[YamlSerializable]
public class DocumentationSetFile : TableOfContentsFile
{
[YamlMember(Alias = "project")]
public string? Project { get; set; }

[YamlMember(Alias = "max_toc_depth")]
public int MaxTocDepth { get; set; } = 2;

[YamlMember(Alias = "dev_docs")]
public bool DevDocs { get; set; }

[YamlMember(Alias = "cross_links")]
public List<string> CrossLinks { get; set; } = [];

[YamlMember(Alias = "exclude")]
public List<string> Exclude { get; set; } = [];

[YamlMember(Alias = "subs")]
public Dictionary<string, string> Subs { get; set; } = [];

[YamlMember(Alias = "features")]
public DocumentationSetFeatures Features { get; set; } = new();

[YamlMember(Alias = "api")]
public Dictionary<string, string> Api { get; set; } = [];

[YamlMember(Alias = "toc")]
public new TableOfContents Toc { get; set; } = [];

public static new DocumentationSetFile Deserialize(string json) =>
ConfigurationFileProvider.Deserializer.Deserialize<DocumentationSetFile>(json);
}

[YamlSerializable]
public class DocumentationSetFeatures
{
[YamlMember(Alias = "primary-nav")]
public bool? PrimaryNav { get; set; }
}

public class TableOfContents : List<ITableOfContentsItem>
{
public TableOfContents() { }

public TableOfContents(IEnumerable<ITableOfContentsItem> items) : base(items) { }
}


public interface ITableOfContentsItem;

public record FileRef(string RelativePath, bool Hidden, IReadOnlyCollection<ITableOfContentsItem> Children)
: ITableOfContentsItem;

public record IndexFileRef(string RelativePath, bool Hidden, IReadOnlyCollection<ITableOfContentsItem> Children)
: FileRef(RelativePath, Hidden, Children);

public record CrossLinkRef(Uri CrossLinkUri, string? Title, bool Hidden, IReadOnlyCollection<ITableOfContentsItem> Children)
: ITableOfContentsItem;

public record FolderRef(string RelativePath, IReadOnlyCollection<ITableOfContentsItem> Children)
: ITableOfContentsItem;

public record IsolatedTableOfContentsRef(string Source, IReadOnlyCollection<ITableOfContentsItem> Children)
: ITableOfContentsItem;


public class TocItemCollectionYamlConverter : IYamlTypeConverter
{
public bool Accepts(Type type) => type == typeof(TableOfContents);

public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
var collection = new TableOfContents();

if (!parser.TryConsume<SequenceStart>(out _))
return collection;

while (!parser.TryConsume<SequenceEnd>(out _))
{
var item = rootDeserializer(typeof(ITableOfContentsItem));
if (item is ITableOfContentsItem tocItem)
collection.Add(tocItem);
}

return collection;
}

public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) =>
serializer.Invoke(value, type);
}

public class TocItemYamlConverter : IYamlTypeConverter
{
public bool Accepts(Type type) => type == typeof(ITableOfContentsItem);

public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
if (!parser.TryConsume<MappingStart>(out _))
return null;

var dictionary = new Dictionary<string, object?>();

while (!parser.TryConsume<MappingEnd>(out _))
{
var key = parser.Consume<Scalar>();

// Parse the value based on what type it is
object? value = null;
if (parser.Accept<Scalar>(out var scalarValue))
{
value = scalarValue.Value;
_ = parser.MoveNext();
}
else if (parser.Accept<SequenceStart>(out _))
{
// This is a list - parse it manually for "children"
if (key.Value == "children")
{
// Parse the children list manually
var childrenList = new List<ITableOfContentsItem>();
_ = parser.Consume<SequenceStart>();
while (!parser.TryConsume<SequenceEnd>(out _))
{
var child = rootDeserializer(typeof(ITableOfContentsItem));
if (child is ITableOfContentsItem tocItem)
childrenList.Add(tocItem);
}
value = childrenList;
}
else
{
// For other lists, just skip them
parser.SkipThisAndNestedEvents();
}
}
else if (parser.Accept<MappingStart>(out _))
{
// This is a nested mapping - skip it
parser.SkipThisAndNestedEvents();
}

dictionary[key.Value] = value;
}

var children = GetChildren(dictionary);

// Check for file reference (file: or hidden:)
if (dictionary.TryGetValue("file", out var filePath) && filePath is string file)
return file == "index.md" ? new IndexFileRef(file, false, children) : new FileRef(file, false, children);

if (dictionary.TryGetValue("hidden", out var hiddenPath) && hiddenPath is string p)
return p == "index.md" ? new IndexFileRef(p, true, children) : new FileRef(p, true, children);

// Check for crosslink reference
if (dictionary.TryGetValue("crosslink", out var crosslink) && crosslink is string crosslinkStr)
{
var title = dictionary.TryGetValue("title", out var t) && t is string titleStr ? titleStr : null;
var isHidden = dictionary.TryGetValue("hidden", out var h) && h is bool hiddenBool && hiddenBool;
return new CrossLinkRef(new Uri(crosslinkStr), title, isHidden, children);
}

// Check for folder reference
if (dictionary.TryGetValue("folder", out var folderPath) && folderPath is string folder)
return new FolderRef(folder, children);

// Check for toc reference
if (dictionary.TryGetValue("toc", out var tocPath) && tocPath is string source)
return new IsolatedTableOfContentsRef(source, children);

return null;
}

private IReadOnlyCollection<ITableOfContentsItem> GetChildren(Dictionary<string, object?> dictionary)
{
if (!dictionary.TryGetValue("children", out var childrenObj))
return [];

// Children have already been deserialized as List<ITableOfContentsItem>
if (childrenObj is List<ITableOfContentsItem> tocItems)
return tocItems;

return [];
}

public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) =>
serializer.Invoke(value, type);
}
Loading
Loading