Skip to content

Commit

Permalink
Added redirect support
Browse files Browse the repository at this point in the history
  • Loading branch information
daveaglick committed May 20, 2020
1 parent 4172905 commit 18f1195
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<!-- Controls whether references are local projects or NuGet packages -->
<LocalReferences>false</LocalReferences>
<!-- The NuGet version of Statiq that should be referenced if LocalReferences is false -->
<StatiqFrameworkVersion>1.0.0-beta.11</StatiqFrameworkVersion>
<StatiqFrameworkVersion>1.0.0-beta.12</StatiqFrameworkVersion>
</PropertyGroup>

<PropertyGroup>
Expand Down
1 change: 1 addition & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# 1.0.0-alpha.15

- Added redirect support.
- Added deployment support for Azure App Service.
- Added deployment support for Netlify.
- Added deployment support for GitHub Pages.
Expand Down
39 changes: 39 additions & 0 deletions src/Statiq.Web/Pipelines/Redirects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Linq;
using Statiq.Common;
using Statiq.Core;
using Statiq.Html;

namespace Statiq.Web.Pipelines
{
public class Redirects : Pipeline
{
public Redirects()
{
Dependencies.Add(nameof(Content));

ProcessModules = new ModuleList
{
new ReplaceDocuments(nameof(Content)),
new FlattenTree(),
new ExecuteConfig(Config.FromSettings(settings =>
{
GenerateRedirects generateRedirects = new GenerateRedirects()
.WithMetaRefreshPages(settings.GetBool(WebKeys.MetaRefreshRedirects, true));
if (settings.GetBool(WebKeys.NetlifyRedirects, false))
{
generateRedirects = generateRedirects.WithAdditionalOutput(
"_redirects",
redirects => string.Join(Environment.NewLine, redirects.Select(r => $"/{r.Key} {r.Value}")));
}
return generateRedirects;
}))
};

OutputModules = new ModuleList
{
new WriteFiles()
};
}
}
}
88 changes: 51 additions & 37 deletions src/Statiq.Web/WebKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Statiq.Web
{
public static class WebKeys
{
// Global
////////// Global

/// <summary>
/// The globbing pattern(s) that will be used to read content files.
Expand Down Expand Up @@ -55,7 +55,17 @@ public static class WebKeys
/// </summary>
public const string ValidateRelativeLinks = nameof(ValidateRelativeLinks);

// Document
/// <summary>
/// Generates META-REFRESH redirect pages (the default value is <c>true</c>).
/// </summary>
public const string MetaRefreshRedirects = nameof(MetaRefreshRedirects);

/// <summary>
/// Generates a Netlify redirects file.
/// </summary>
public const string NetlifyRedirects = nameof(NetlifyRedirects);

////////// Document

public const string Title = nameof(Title);

Expand Down Expand Up @@ -87,6 +97,42 @@ public static class WebKeys
/// </summary>
public const string Excluded = nameof(Excluded);

/// <summary>
/// Indicates that the content or data file should be output.
/// By default content files are output and data files are not.
/// </summary>
public const string ShouldOutput = nameof(ShouldOutput);

/// <summary>
/// Indicates the layout file that should be used for this document.
/// </summary>
public const string Layout = nameof(Layout);

/// <summary>
/// Specifies the cross-reference ID of the current document. If not explicitly provided, it will default
/// to the title of the document with spaces replaced by underscores (which is derived from the source file name
/// if no <see cref="Title"/> metadata is defined for the document).
/// </summary>
public const string Xref = nameof(Xref);

/// <summary>
/// Used with directory metadata to indicate if the metadata should be applied
/// recursively to files in child directories (the default is <c>true</c>).
/// </summary>
public const string Recursive = nameof(Recursive);

/// <summary>
/// Indicates that post-process templates should be rendered (the default is <c>true</c>).
/// </summary>
/// <remarks>
/// Set this to <c>false</c> for a document to prevent rendering post-process templates such as Razor.
/// This can be helpful when you have small bits of content like Markdown that you want to render
/// as HTML but not as an entire page so that it can be included in other pages.
/// </remarks>
public const string RenderPostProcessTemplates = nameof(RenderPostProcessTemplates);

////////// Archive

/// <summary>
/// The pipeline(s) to get documents for the archive from.
/// Defaults to the <c>Content</c> pipeline if not defined.
Expand Down Expand Up @@ -158,6 +204,8 @@ public static class WebKeys
/// </summary>
public const string ArchiveOrderDescending = nameof(ArchiveOrderDescending);

////////// Feed

public const string FeedPipelines = nameof(FeedPipelines);

public const string FeedSources = nameof(FeedSources);
Expand Down Expand Up @@ -216,41 +264,7 @@ public static class WebKeys

public const string FeedItemThreadUpdated = nameof(FeedItemThreadUpdated);

/// <summary>
/// Indicates that the content or data file should be output.
/// By default content files are output and data files are not.
/// </summary>
public const string ShouldOutput = nameof(ShouldOutput);

/// <summary>
/// Indicates the layout file that should be used for this document.
/// </summary>
public const string Layout = nameof(Layout);

/// <summary>
/// Specifies the cross-reference ID of the current document. If not explicitly provided, it will default
/// to the title of the document with spaces replaced by underscores (which is derived from the source file name
/// if no <see cref="Title"/> metadata is defined for the document).
/// </summary>
public const string Xref = nameof(Xref);

/// <summary>
/// Used with directory metadata to indicate if the metadata should be applied
/// recursively to files in child directories (the default is <c>true</c>).
/// </summary>
public const string Recursive = nameof(Recursive);

/// <summary>
/// Indicates that post-process templates should be rendered (the default is <c>true</c>).
/// </summary>
/// <remarks>
/// Set this to <c>false</c> for a document to prevent rendering post-process templates such as Razor.
/// This can be helpful when you have small bits of content like Markdown that you want to render
/// as HTML but not as an entire page so that it can be included in other pages.
/// </remarks>
public const string RenderPostProcessTemplates = nameof(RenderPostProcessTemplates);

// Deployment
////////// Deployment

public const string GitHubOwner = nameof(GitHubOwner);

Expand Down
82 changes: 82 additions & 0 deletions tests/Statiq.Web.Tests/Pipelines/RedirectsFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Shouldly;
using Statiq.App;
using Statiq.Common;
using Statiq.Testing;
using Statiq.Web.Pipelines;

namespace Statiq.Web.Tests.Pipelines
{
[TestFixture]
[NonParallelizable]
public class RedirectsFixture : BaseFixture
{
public class ExecuteTests : RedirectsFixture
{
[Test]
public async Task GeneratesClientRedirects()
{
// Given
Bootstrapper bootstrapper = Bootstrapper.Factory.CreateWeb(Array.Empty<string>());
TestFileProvider fileProvider = new TestFileProvider
{
{
"/input/a/b/c.md",
@"RedirectFrom: x/y
---
Foo"
},
{
"/input/d/e.md",
"Bar"
}
};

// When
BootstrapperTestResult result = await bootstrapper.RunTestAsync(fileProvider);

// Then
result.ExitCode.ShouldBe((int)ExitCode.Normal);
IDocument document = result.Outputs[nameof(Redirects)][Phase.Process].ShouldHaveSingleItem();
document.Destination.ShouldBe("x/y.html");
(await document.GetContentStringAsync()).ShouldContain(@"<meta http-equiv=""refresh"" content=""0;url='/a/b/c'"" />");
}

[Test]
public async Task GeneratesNetlifyRedirects()
{
// Given
Bootstrapper bootstrapper = Bootstrapper.Factory
.CreateWeb(Array.Empty<string>())
.AddSetting(WebKeys.NetlifyRedirects, true)
.AddSetting(WebKeys.MetaRefreshRedirects, false);
TestFileProvider fileProvider = new TestFileProvider
{
{
"/input/a/b/c.md",
@"RedirectFrom: x/y
---
Foo"
},
{
"/input/d/e.md",
"Bar"
}
};

// When
BootstrapperTestResult result = await bootstrapper.RunTestAsync(fileProvider);

// Then
result.ExitCode.ShouldBe((int)ExitCode.Normal);
IDocument document = result.Outputs[nameof(Redirects)][Phase.Process].ShouldHaveSingleItem();
document.Destination.ShouldBe("_redirects");
(await document.GetContentStringAsync()).ShouldBe("/x/y /a/b/c");
}
}
}
}

0 comments on commit 18f1195

Please sign in to comment.