diff --git a/src/Elastic.Markdown/IO/Paths.cs b/src/Elastic.Markdown/IO/Paths.cs index 8b63080..3075ef0 100644 --- a/src/Elastic.Markdown/IO/Paths.cs +++ b/src/Elastic.Markdown/IO/Paths.cs @@ -15,4 +15,13 @@ private static DirectoryInfo RootDirectoryInfo() } public static readonly DirectoryInfo Root = RootDirectoryInfo(); + + /// Used in debug to locate static folder so we can change js/css files while the server is running + public static DirectoryInfo? GetSolutionDirectory() + { + var directory = new DirectoryInfo(Directory.GetCurrentDirectory()); + while (directory != null && directory.GetFiles("*.sln").Length == 0) + directory = directory.Parent; + return directory; + } } diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 524cfa8..d4ce66d 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -108,6 +108,10 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) if (url.EndsWith(".md")) link.Url = Path.ChangeExtension(url, ".html"); + // rooted links might need the configured path prefix to properly link + var prefix = processor.GetBuildContext().UrlPathPrefix; + if (url.StartsWith("/") && !string.IsNullOrWhiteSpace(prefix)) + link.Url = $"{prefix}/{link.Url}"; if (!string.IsNullOrEmpty(anchor)) link.Url += $"#{anchor}"; diff --git a/src/docs-builder/Http/DocumentationWebHost.cs b/src/docs-builder/Http/DocumentationWebHost.cs index c9537ea..656e9bb 100644 --- a/src/docs-builder/Http/DocumentationWebHost.cs +++ b/src/docs-builder/Http/DocumentationWebHost.cs @@ -10,9 +10,12 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.FileProviders.Physical; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; using Westwind.AspNetCore.LiveReload; +using IFileInfo = Microsoft.Extensions.FileProviders.IFileInfo; namespace Documentation.Builder.Http; @@ -20,8 +23,7 @@ public class DocumentationWebHost { private readonly WebApplication _webApplication; - private readonly string _staticFilesDirectory = - Path.Combine(Paths.Root.FullName, "docs", "source", "_static"); + private readonly string _staticFilesDirectory; public DocumentationWebHost(string? path, ILoggerFactory logger, IFileSystem fileSystem) { @@ -40,6 +42,15 @@ public DocumentationWebHost(string? path, ILoggerFactory logger, IFileSystem fil builder.Services.AddSingleton(logger); builder.Logging.SetMinimumLevel(LogLevel.Warning); + _staticFilesDirectory = Path.Combine(context.SourcePath.FullName, "_static"); + #if DEBUG + // this attempts to serve files directly from their source rather than the embedded resourses during development. + // this allows us to change js/css files without restarting the webserver + var solutionRoot = Paths.GetSolutionDirectory(); + if (solutionRoot != null) + _staticFilesDirectory = Path.Combine(solutionRoot.FullName, "src", "Elastic.Markdown", "_static"); + #endif + _webApplication = builder.Build(); SetUpRoutes(); } @@ -52,7 +63,7 @@ private void SetUpRoutes() _webApplication.UseLiveReload(); _webApplication.UseStaticFiles(new StaticFileOptions { - FileProvider = new PhysicalFileProvider(_staticFilesDirectory), + FileProvider = new EmbeddedOrPhysicalFileProvider(_staticFilesDirectory), RequestPath = "/_static" }); _webApplication.UseRouting(); @@ -86,3 +97,40 @@ private static async Task ServeDocumentationFile(ReloadableGeneratorSta } } } + + +public class EmbeddedOrPhysicalFileProvider : IFileProvider +{ + private readonly EmbeddedFileProvider _embeddedProvider; + private readonly PhysicalFileProvider _fileProvider; + + public EmbeddedOrPhysicalFileProvider(string root) + { + _embeddedProvider = new EmbeddedFileProvider(typeof(BuildContext).Assembly, "Elastic.Markdown._static"); + _fileProvider = new PhysicalFileProvider(root); + } + + public IDirectoryContents GetDirectoryContents(string subpath) + { + var contents = _fileProvider.GetDirectoryContents(subpath); + if (!contents.Exists) + contents = _embeddedProvider.GetDirectoryContents(subpath); + return contents; + } + + public IFileInfo GetFileInfo(string subpath) + { + var fileInfo = _fileProvider.GetFileInfo(subpath.Replace("/_static", "")); + if (!fileInfo.Exists) + fileInfo = _embeddedProvider.GetFileInfo(subpath); + return fileInfo; + } + + public IChangeToken Watch(string filter) + { + var changeToken = _fileProvider.Watch(filter); + if (changeToken is NullChangeToken) + changeToken = _embeddedProvider.Watch(filter); + return changeToken; + } +} diff --git a/src/docs-generator/Cli/Commands.cs b/src/docs-generator/Cli/Commands.cs index 924d1a4..3f435da 100644 --- a/src/docs-generator/Cli/Commands.cs +++ b/src/docs-generator/Cli/Commands.cs @@ -95,7 +95,7 @@ public async Task Generate( WriteMarkdownFile(outputFolder, file); } - var name = $"random-docset-{seedContent}-{seedFileSystem}"; + var name = $"random-docset-{Determinism.Random.SeedFileSystem}-{Determinism.Random.SeedFileSystem}"; WriteIndexMarkdownFile(name, outputFolder); var docset = Path.Combine(outputFolder.FullName, "docset.yml"); diff --git a/src/docs-generator/Domain/Generators.cs b/src/docs-generator/Domain/Generators.cs index feef603..dad7b90 100644 --- a/src/docs-generator/Domain/Generators.cs +++ b/src/docs-generator/Domain/Generators.cs @@ -2,7 +2,6 @@ // 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 Slugify; using Soenneker.Utils.AutoBogus; namespace Documentation.Generator.Domain; @@ -12,7 +11,6 @@ public static class Generators public static AutoFaker FolderName { get; } = new(); public static AutoFaker
Section { get; } = new(); public static AutoFaker File { get; } = new(); - public static SlugHelper Slug { get; } = new(); static Generators() {