Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions docs-builder.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=crosslink/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=docset/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=frontmatter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=linenos/@EntryIndexedValue">True</s:Boolean>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,10 @@ file is null && crossLink is null && folder is null && toc is null &&

if (crossLink is not null)
{
return [new CrossLinkReference(this, crossLink, title, hiddenFile, children ?? [])];
if (Uri.TryCreate(crossLink, UriKind.Absolute, out var crossUri) && CrossLinkValidator.IsCrossLink(crossUri))
return [new CrossLinkReference(this, crossUri, title, hiddenFile, children ?? [])];
else
reader.EmitError($"Cross-link '{crossLink}' is not a valid absolute URI format", tocEntry);
}

if (folder is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface ITocItem
public record FileReference(ITableOfContentsScope TableOfContentsScope, string RelativePath, bool Hidden, IReadOnlyCollection<ITocItem> Children)
: ITocItem;

public record CrossLinkReference(ITableOfContentsScope TableOfContentsScope, string CrossLinkUri, string? Title, bool Hidden, IReadOnlyCollection<ITocItem> Children)
public record CrossLinkReference(ITableOfContentsScope TableOfContentsScope, Uri CrossLinkUri, string? Title, bool Hidden, IReadOnlyCollection<ITocItem> Children)
: ITocItem;

public record FolderReference(ITableOfContentsScope TableOfContentsScope, string RelativePath, IReadOnlyCollection<ITocItem> Children)
Expand Down
7 changes: 2 additions & 5 deletions src/Elastic.Markdown/DocumentationGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class DocumentationGenerator

public DocumentationSet DocumentationSet { get; }
public BuildContext Context { get; }
public ICrossLinkResolver Resolver { get; }
public ICrossLinkResolver CrossLinkResolver { get; }
public IMarkdownStringRenderer MarkdownStringRenderer => HtmlWriter;

public DocumentationGenerator(
Expand All @@ -70,7 +70,7 @@ public DocumentationGenerator(

DocumentationSet = docSet;
Context = docSet.Context;
Resolver = docSet.LinkResolver;
CrossLinkResolver = docSet.CrossLinkResolver;
HtmlWriter = new HtmlWriter(DocumentationSet, _writeFileSystem, new DescriptionGenerator(), navigationHtmlWriter, legacyUrlMapper,
positionalNavigation);
_documentationFileExporter =
Expand Down Expand Up @@ -120,9 +120,6 @@ public async Task<GenerationResult> GenerateAll(Cancel ctx)
if (CompilationNotNeeded(generationState, out var offendingFiles, out var outputSeenChanges))
return result;

_logger.LogInformation($"Fetching external links");
_ = await Resolver.FetchLinks(ctx);

await ResolveDirectoryTree(ctx);

await ProcessDocumentationFiles(offendingFiles, outputSeenChanges, ctx);
Expand Down
41 changes: 13 additions & 28 deletions src/Elastic.Markdown/IO/DocumentationSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using Elastic.Documentation.Configuration;
using Elastic.Documentation.Configuration.Builder;
using Elastic.Documentation.Configuration.TableOfContents;
using Elastic.Documentation.LinkIndex;
using Elastic.Documentation.Links;
using Elastic.Documentation.Site.Navigation;
using Elastic.Markdown.Extensions;
Expand All @@ -28,6 +27,7 @@ public interface INavigationLookups
IReadOnlyCollection<ITocItem> TableOfContents { get; }
IReadOnlyCollection<IDocsBuilderExtension> EnabledExtensions { get; }
FrozenDictionary<string, DocumentationFile[]> FilesGroupedByFolder { get; }
ICrossLinkResolver CrossLinkResolver { get; }
}

public interface IPositionalNavigation
Expand Down Expand Up @@ -94,10 +94,12 @@ public record NavigationLookups : INavigationLookups
public required IReadOnlyCollection<ITocItem> TableOfContents { get; init; }
public required IReadOnlyCollection<IDocsBuilderExtension> EnabledExtensions { get; init; }
public required FrozenDictionary<string, DocumentationFile[]> FilesGroupedByFolder { get; init; }
public required ICrossLinkResolver CrossLinkResolver { get; init; }
}

public class DocumentationSet : INavigationLookups, IPositionalNavigation
{
private readonly ILogger<DocumentationSet> _logger;
public BuildContext Context { get; }
public string Name { get; }
public IFileInfo OutputStateFile { get; }
Expand All @@ -112,7 +114,7 @@ public class DocumentationSet : INavigationLookups, IPositionalNavigation

public MarkdownParser MarkdownParser { get; }

public ICrossLinkResolver LinkResolver { get; }
public ICrossLinkResolver CrossLinkResolver { get; }

public TableOfContentsTree Tree { get; }

Expand All @@ -135,23 +137,23 @@ public class DocumentationSet : INavigationLookups, IPositionalNavigation
public DocumentationSet(
BuildContext context,
ILoggerFactory logFactory,
ICrossLinkResolver? linkResolver = null,
ICrossLinkResolver linkResolver,
TableOfContentsTreeCollector? treeCollector = null
)
{
_logger = logFactory.CreateLogger<DocumentationSet>();
Context = context;
Source = ContentSourceMoniker.Create(context.Git.RepositoryName, null);
SourceDirectory = context.DocumentationSourceDirectory;
OutputDirectory = context.OutputDirectory;
LinkResolver =
linkResolver ?? new CrossLinkResolver(new ConfigurationCrossLinkFetcher(logFactory, context.Configuration, Aws3LinkIndexReader.CreateAnonymous()));
CrossLinkResolver = linkResolver;
Configuration = context.Configuration;
EnabledExtensions = InstantiateExtensions();
treeCollector ??= new TableOfContentsTreeCollector();

var resolver = new ParserResolvers
{
CrossLinkResolver = LinkResolver,
CrossLinkResolver = CrossLinkResolver,
DocumentationFileLookup = DocumentationFileLookup
};
MarkdownParser = new MarkdownParser(context, resolver);
Expand Down Expand Up @@ -184,7 +186,8 @@ public DocumentationSet(
FlatMappedFiles = FlatMappedFiles,
TableOfContents = Configuration.TableOfContents,
EnabledExtensions = EnabledExtensions,
FilesGroupedByFolder = FilesGroupedByFolder
FilesGroupedByFolder = FilesGroupedByFolder,
CrossLinkResolver = CrossLinkResolver
};

Tree = new TableOfContentsTree(Source, Context, lookups, treeCollector, ref fileIndex);
Expand Down Expand Up @@ -232,7 +235,7 @@ private void UpdateNavigationIndex(IReadOnlyCollection<INavigationItem> navigati
UpdateNavigationIndex(documentationGroup.NavigationItems, ref navigationIndex);
break;
default:
Context.EmitError(Context.ConfigurationPath, $"Unhandled navigation item type: {item.GetType()}");
Context.EmitError(Context.ConfigurationPath, $"{nameof(DocumentationSet)}.{nameof(UpdateNavigationIndex)}: Unhandled navigation item type: {item.GetType()}");
break;
}
}
Expand Down Expand Up @@ -374,26 +377,7 @@ void ValidateExists(string from, string to, IReadOnlyDictionary<string, string?>
return FlatMappedFiles.GetValueOrDefault(relativePath);
}

public async Task ResolveDirectoryTree(Cancel ctx)
{
await Tree.Resolve(ctx);

// Validate cross-repo links in navigation
try
{
await NavigationCrossLinkValidator.ValidateNavigationCrossLinksAsync(
Tree,
LinkResolver,
(msg) => Context.EmitError(Context.ConfigurationPath, msg),
ctx
);
}
catch (Exception e)
{
// Log the error but don't fail the build
Context.EmitError(Context.ConfigurationPath, $"Error validating cross-links in navigation: {e.Message}");
}
}
public async Task ResolveDirectoryTree(Cancel ctx) => await Tree.Resolve(ctx);

private DocumentationFile CreateMarkDownFile(IFileInfo file, BuildContext context)
{
Expand Down Expand Up @@ -476,6 +460,7 @@ public RepositoryLinks CreateLinkReference()

public void ClearOutputDirectory()
{
_logger.LogInformation("Clearing output directory {OutputDirectory}", OutputDirectory.Name);
if (OutputDirectory.Exists)
OutputDirectory.Delete(true);
OutputDirectory.Create();
Expand Down
4 changes: 2 additions & 2 deletions src/Elastic.Markdown/IO/MarkdownFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ public string Url
{
if (_url is not null)
return _url;
if (_set.LinkResolver.UriResolver is IsolatedBuildEnvironmentUriResolver)
if (_set.CrossLinkResolver.UriResolver is IsolatedBuildEnvironmentUriResolver)
{
_url = DefaultUrlPath;
return _url;
}
var crossLink = new Uri(CrossLink);
var uri = _set.LinkResolver.UriResolver.Resolve(crossLink, DefaultUrlPathSuffix);
var uri = _set.CrossLinkResolver.UriResolver.Resolve(crossLink, DefaultUrlPathSuffix);
_url = uri.AbsolutePath;
return _url;

Expand Down
17 changes: 6 additions & 11 deletions src/Elastic.Markdown/IO/Navigation/CrossLinkNavigationItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ namespace Elastic.Markdown.IO.Navigation;
[DebuggerDisplay("CrossLink: {Url}")]
public record CrossLinkNavigationItem : ILeafNavigationItem<INavigationModel>
{
// Override Url accessor to use ResolvedUrl if available
string INavigationItem.Url => ResolvedUrl ?? Url;
public CrossLinkNavigationItem(string url, string title, DocumentationGroup group, bool hidden = false)
public CrossLinkNavigationItem(Uri crossLinkUri, Uri resolvedUrl, string title, DocumentationGroup group, bool hidden = false)
{
_url = url;
CrossLink = crossLinkUri;
Url = resolvedUrl.ToString();
NavigationTitle = title;
Parent = group;
NavigationRoot = group.NavigationRoot;
Expand All @@ -24,14 +23,10 @@ public CrossLinkNavigationItem(string url, string title, DocumentationGroup grou

public INodeNavigationItem<INavigationModel, INavigationItem>? Parent { get; set; }
public IRootNavigationItem<INavigationModel, INavigationItem> NavigationRoot { get; }
// Original URL from the cross-link
private readonly string _url;

// Store resolved URL for rendering
public string? ResolvedUrl { get; set; }

// Implement the INavigationItem.Url property to use ResolvedUrl if available
public string Url => ResolvedUrl ?? _url; public string NavigationTitle { get; }
public Uri CrossLink { get; }
public string Url { get; }
public string NavigationTitle { get; }
public int NavigationIndex { get; set; }
public bool Hidden { get; }
public bool IsCrossLink => true; // This is always a cross-link
Expand Down
14 changes: 5 additions & 9 deletions src/Elastic.Markdown/IO/Navigation/DocumentationGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Elastic.Documentation.Configuration;
using Elastic.Documentation.Configuration.TableOfContents;
using Elastic.Documentation.Extensions;
using Elastic.Documentation.Links;
using Elastic.Documentation.Site.Navigation;

namespace Elastic.Markdown.IO.Navigation;
Expand Down Expand Up @@ -124,13 +123,6 @@ void AddToNavigationItems(INavigationItem item, ref int fileIndex)
{
if (tocItem is CrossLinkReference crossLink)
{
// Validate crosslink URI and title
if (!CrossLinkValidator.IsValidCrossLink(crossLink.CrossLinkUri, out var errorMessage))
{
context.EmitError(context.ConfigurationPath, errorMessage!);
continue;
}

// Validate that cross-link has a title
if (string.IsNullOrWhiteSpace(crossLink.Title))
{
Expand All @@ -139,9 +131,13 @@ void AddToNavigationItems(INavigationItem item, ref int fileIndex)
continue;
}

if (!lookups.CrossLinkResolver.TryResolve(msg => context.EmitError(context.ConfigurationPath, msg), crossLink.CrossLinkUri, out var resolvedUrl))
continue; // the crosslink resolver will emit an error already

// Create a special navigation item for cross-repository links
var crossLinkItem = new CrossLinkNavigationItem(crossLink.CrossLinkUri, crossLink.Title, this, crossLink.Hidden);
var crossLinkItem = new CrossLinkNavigationItem(crossLink.CrossLinkUri, resolvedUrl, crossLink.Title, this, crossLink.Hidden);
AddToNavigationItems(crossLinkItem, ref fileIndex);

}
else if (tocItem is FileReference file)
{
Expand Down
86 changes: 0 additions & 86 deletions src/Elastic.Markdown/IO/Navigation/NavigationCrossLinkValidator.cs

This file was deleted.

Loading
Loading