diff --git a/RELEASE.md b/RELEASE.md index c4e36fa2..a51f1543 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,3 +1,7 @@ +# 1.0.0-beta.65 + +- Added a `GenerateRedirects.AlwaysCreateAdditionalOutput()` method to allow creating additional redirect files even if no redirects are specified (I.e. if redirects are also being generated from another source). + # 1.0.0-beta.64 - Fixed `ElseIf` when `Predicate.RequiresDocument` is false in `ExecuteIf` module (#255, #256, thanks @nils-a). diff --git a/src/core/Statiq.Common/IO/NormalizedPath.cs b/src/core/Statiq.Common/IO/NormalizedPath.cs index a58f4fe8..8aec975c 100644 --- a/src/core/Statiq.Common/IO/NormalizedPath.cs +++ b/src/core/Statiq.Common/IO/NormalizedPath.cs @@ -442,6 +442,11 @@ private NormalizedPath(string fullPath, ReadOnlyMemory[] segments, bool is /// public string ToDisplayString() => IsNull ? string.Empty : FullPath; + /// + /// A display string that can be used for tracing that displays a custom message if the path is null. + /// + public string ToDisplayString(string nullText) => IsNull ? nullText : FullPath; + /// /// Returns a that represents this path. /// diff --git a/src/core/Statiq.Core/Modules/Content/GenerateRedirects.cs b/src/core/Statiq.Core/Modules/Content/GenerateRedirects.cs index fe6d834a..9014bedf 100644 --- a/src/core/Statiq.Core/Modules/Content/GenerateRedirects.cs +++ b/src/core/Statiq.Core/Modules/Content/GenerateRedirects.cs @@ -37,6 +37,7 @@ public class GenerateRedirects : Module private Config> _paths = Config.FromDocument>(Keys.RedirectFrom); private bool _metaRefreshPages = true; private bool _includeHost = false; + private bool _alwaysCreateAdditionalOutput = false; /// /// Controls where the redirected paths come from. By default, values from the metadata @@ -92,6 +93,17 @@ public GenerateRedirects WithAdditionalOutput(in NormalizedPath path, Func + /// Always creates any additional output documents even if there are no redirected paths. + /// + /// true to always create additional output documents. + /// The current module instance. + public GenerateRedirects AlwaysCreateAdditionalOutput(bool alwaysCreateAdditionalOutput = true) + { + _alwaysCreateAdditionalOutput = alwaysCreateAdditionalOutput; + return this; + } + /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { @@ -109,7 +121,7 @@ protected override async Task> ExecuteContextAsync(IExecu } // Generate other output documents if requested - if (redirects.Count > 0) + if (redirects.Count > 0 || _alwaysCreateAdditionalOutput) { foreach (KeyValuePair, string>> additionalOutput in _additionalOutputs) { diff --git a/src/core/Statiq.Core/Modules/Control/ProcessSidecarFile.cs b/src/core/Statiq.Core/Modules/Control/ProcessSidecarFile.cs index a71e0f8e..d1a26fe7 100644 --- a/src/core/Statiq.Core/Modules/Control/ProcessSidecarFile.cs +++ b/src/core/Statiq.Core/Modules/Control/ProcessSidecarFile.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Statiq.Common; namespace Statiq.Core @@ -69,6 +70,7 @@ protected override async Task> ExecuteInputAsync(IDocumen IFile sidecarFile = context.FileSystem.GetInputFile(sidecarPath); if (sidecarFile.Exists) { + context.LogDebug($"Processing sidecar file {sidecarPath} for {input.Source.ToDisplayString("unknown")}"); IContentProvider sidecarContent = sidecarFile.GetContentProvider(); foreach (IDocument result in await context.ExecuteModulesAsync(Children, input.Clone(sidecarContent).Yield())) { diff --git a/src/core/Statiq.Core/Modules/IO/SetDestination.cs b/src/core/Statiq.Core/Modules/IO/SetDestination.cs index 5f10c7f5..7264e47f 100644 --- a/src/core/Statiq.Core/Modules/IO/SetDestination.cs +++ b/src/core/Statiq.Core/Modules/IO/SetDestination.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Statiq.Common; namespace Statiq.Core @@ -141,7 +142,19 @@ public static NormalizedPath GetCurrentDestinationFromMetadata(IDocument doc) return null; } - protected override Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, NormalizedPath value) => - Task.FromResult(value.IsNull ? input.Yield() : input.Clone(value).Yield()); + protected override Task> ExecuteConfigAsync( + IDocument input, + IExecutionContext context, + NormalizedPath value) + { + if (value.IsNull) + { + return Task.FromResult(input.Yield()); + } + + context.LogDebug( + $"Setting destination of {input.Source.ToDisplayString("null")} to {value.ToDisplayString("null")}"); + return Task.FromResult(input.Clone(value).Yield()); + } } } \ No newline at end of file diff --git a/tests/core/Statiq.Core.Tests/Modules/Content/GenerateRedirectsFixture.cs b/tests/core/Statiq.Core.Tests/Modules/Content/GenerateRedirectsFixture.cs index 2a90a182..97ce7700 100644 --- a/tests/core/Statiq.Core.Tests/Modules/Content/GenerateRedirectsFixture.cs +++ b/tests/core/Statiq.Core.Tests/Modules/Content/GenerateRedirectsFixture.cs @@ -179,7 +179,8 @@ public async Task WithAdditionalOutput() { { Keys.RedirectFrom, new List { new NormalizedPath("bar/baz.html") } } }); - GenerateRedirects redirect = new GenerateRedirects().WithAdditionalOutput(new NormalizedPath("a/b"), x => string.Join("|", x.Select(y => $"{y.Key} {y.Value}"))); + GenerateRedirects redirect = new GenerateRedirects() + .WithAdditionalOutput(new NormalizedPath("a/b"), x => string.Join("|", x.Select(y => $"{y.Key} {y.Value}"))); // When IReadOnlyList results = await ExecuteAsync(new[] { redirected1, redirected2 }, redirect); @@ -216,6 +217,40 @@ public async Task WithAdditionalOutputWithoutMetaRefresh() results.Select(x => x.Destination.FullPath).ShouldBe(new[] { "a/b" }); } + [Test] + public async Task ShouldNotGenerateAdditionalOutputIfNoRedirects() + { + // Given + TestDocument redirected1 = new TestDocument(new NormalizedPath("foo2.html")); + TestDocument redirected2 = new TestDocument(new NormalizedPath("fizz.html")); + GenerateRedirects redirect = new GenerateRedirects() + .WithAdditionalOutput(new NormalizedPath("a/b"), x => "foobar"); + + // When + IReadOnlyList results = await ExecuteAsync(new[] { redirected1, redirected2 }, redirect); + + // Then + results.Select(x => x.Destination.FullPath).ShouldBeEmpty(); + } + + [Test] + public async Task ShouldGenerateAdditionalOutputIfNoRedirects() + { + // Given + TestDocument redirected1 = new TestDocument(new NormalizedPath("foo2.html")); + TestDocument redirected2 = new TestDocument(new NormalizedPath("fizz.html")); + GenerateRedirects redirect = new GenerateRedirects() + .WithAdditionalOutput(new NormalizedPath("a/b"), x => "foobar") + .AlwaysCreateAdditionalOutput(); + + // When + IReadOnlyList results = await ExecuteAsync(new[] { redirected1, redirected2 }, redirect); + + // Then + results.Select(x => x.Destination.FullPath).ShouldBe(new[] { "a/b" }); + results.Single(x => x.Destination.FullPath == "a/b").Content.ShouldBe("foobar"); + } + [Test] public async Task DifferentBody() { @@ -236,4 +271,4 @@ public async Task DifferentBody() results.Single().Content.ShouldContain("

FOOBAR

"); } } -} +} \ No newline at end of file