Skip to content

Commit 26d948e

Browse files
committed
Added several output and processing control keys
1 parent d207ec2 commit 26d948e

File tree

9 files changed

+172
-31
lines changed

9 files changed

+172
-31
lines changed

RELEASE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# 1.0.0-alpha.10
22

3+
- Added a new `RenderPostProcessTemplates` key that prevents running post-processing templates like Razor.
4+
- Added a new `ShouldOutput` key that controls outputting a particular document to disk (but doesn't remove it from the pipeline like `Excluded` does).
5+
- Added support for directory metadata (by default as `_directory.yaml` files).
36
- Added new `ContentFiles` and `DataFiles` settings to control the file globbing patterns.
47
- Added a new `GenerateSitemap` setting and `Sitemap` pipeline to generate sitemap files by default.
58
- Added a new `Excluded` key that indicates a document should be filtered out of the content or data pipeline.

src/Statiq.Web/BootstrapperFactoryExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public static Bootstrapper CreateWeb(this BootstrapperFactory factory, string[]
2525
{
2626
{ WebKeys.ContentFiles, "**/{!_,}*.{html,cshtml,md}" },
2727
{ WebKeys.DataFiles, "**/{!_,}*.{json,yaml,yml}" },
28+
{ WebKeys.DirectoryMetadataFiles, "**/_{d,D}irectory.{json,yaml,yml}" },
2829
{ WebKeys.MirrorResources, true },
2930
{ WebKeys.ValidateRelativeLinks, true },
3031
{ WebKeys.GenerateSitemap, true },
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.Immutable;
4+
using System.Linq;
5+
using Statiq.Common;
6+
using Statiq.Web.Pipelines;
7+
8+
namespace Statiq.Web.Modules
9+
{
10+
/// <summary>
11+
/// Applies directory metadata documents to the input documents. Make sure this module
12+
/// is executed before applying local metadata like front matter or it will get overwritten
13+
/// by the directory metadata.
14+
/// </summary>
15+
public class ApplyDirectoryMetadata : SyncModule
16+
{
17+
protected override IEnumerable<IDocument> ExecuteContext(IExecutionContext context)
18+
{
19+
// Figure out relative input paths for all metadata documents
20+
ImmutableDictionary<string, ImmutableArray<DirectoryMetadataData>> directoryMetadata = context
21+
.Outputs[nameof(DirectoryMetadata)]
22+
.GroupBy(x => x.Source.Parent.GetRelativeInputPath().FullPath)
23+
.ToImmutableDictionary(
24+
x => x.Key,
25+
x => x
26+
.Select(doc => new DirectoryMetadataData
27+
{
28+
Recursive = doc.GetBool(WebKeys.Recursive, true),
29+
Metadata = doc.GetRawEnumerable().ToImmutableArray()
30+
})
31+
.ToImmutableArray());
32+
33+
// Iterate through all input documents in parallel
34+
return context.Inputs.Select(input =>
35+
{
36+
IDocument merged = input;
37+
38+
// Get all matching directory metadata documents
39+
NormalizedPath path = input.Source.Parent.GetRelativeInputPath();
40+
Stack<DirectoryMetadataData> matchStack = new Stack<DirectoryMetadataData>();
41+
bool local = true;
42+
while (!path.IsNull)
43+
{
44+
if (directoryMetadata.TryGetValue(path.FullPath, out ImmutableArray<DirectoryMetadataData> matches))
45+
{
46+
foreach (DirectoryMetadataData match in matches.Where(x => local || x.Recursive))
47+
{
48+
matchStack.Push(match);
49+
}
50+
}
51+
if (path.IsNullOrEmpty)
52+
{
53+
break;
54+
}
55+
local = false;
56+
path = path.Parent;
57+
}
58+
59+
// Apply matches if there are any
60+
while (matchStack.Count > 0)
61+
{
62+
DirectoryMetadataData match = matchStack.Pop();
63+
merged = merged.Clone(match.Metadata);
64+
}
65+
66+
return merged;
67+
});
68+
}
69+
70+
private class DirectoryMetadataData
71+
{
72+
public bool Recursive { get; set; }
73+
public ImmutableArray<KeyValuePair<string, object>> Metadata { get; set; }
74+
}
75+
}
76+
}

src/Statiq.Web/Modules/RenderPostProcessTemplates.cs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,22 @@ public class RenderPostProcessTemplates : ForAllDocuments
1212
{
1313
public RenderPostProcessTemplates(Templates templates)
1414
: base(
15-
templates.GetModules(Phase.PostProcess)
16-
.Concat(new IModule[]
15+
new IModule[]
16+
{
17+
new ExecuteIf(
18+
Config.FromDocument(WebKeys.RenderPostProcessTemplates, true),
19+
templates.GetModules(Phase.PostProcess))
20+
}
21+
.Concat(new IModule[]
22+
{
23+
new ProcessShortcodes(),
24+
new ExecuteIf(Config.FromSetting<bool>(WebKeys.MirrorResources))
1725
{
18-
new ProcessShortcodes(),
19-
new ExecuteIf(Config.FromSetting<bool>(WebKeys.MirrorResources))
20-
{
21-
new MirrorResources()
22-
},
23-
new ResolveXrefs()
24-
})
25-
.ToArray())
26+
new MirrorResources()
27+
},
28+
new ResolveXrefs()
29+
})
30+
.ToArray())
2631
{
2732
}
2833
}

src/Statiq.Web/Pipelines/Content.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Linq;
4-
using System.Text;
5-
using Microsoft.AspNetCore.Mvc.Formatters;
6-
using Statiq.App;
73
using Statiq.Common;
84
using Statiq.Core;
95
using Statiq.Html;
10-
using Statiq.Markdown;
11-
using Statiq.Razor;
126
using Statiq.Web.Modules;
13-
using Statiq.Yaml;
147

158
namespace Statiq.Web.Pipelines
169
{
1710
public class Content : Pipeline
1811
{
1912
public Content(Templates templates)
2013
{
21-
Dependencies.Add(nameof(Data));
14+
Dependencies.AddRange(nameof(Data), nameof(DirectoryMetadata));
2215

2316
InputModules = new ModuleList
2417
{
@@ -30,6 +23,9 @@ public Content(Templates templates)
3023
// Concat all documents from externally declared dependencies (exclude explicit dependencies above like "Data")
3124
new ConcatDocuments(Config.FromContext<IEnumerable<IDocument>>(ctx => ctx.Outputs.FromPipelines(ctx.Pipeline.GetAllDependencies(ctx).Except(Dependencies).ToArray()))),
3225

26+
// Apply directory metadata
27+
new ApplyDirectoryMetadata(),
28+
3329
// Process front matter and sidecar files
3430
new ProcessMetadata(),
3531

@@ -62,6 +58,7 @@ public Content(Templates templates)
6258

6359
OutputModules = new ModuleList
6460
{
61+
new FilterDocuments(Config.FromDocument(WebKeys.ShouldOutput, true)),
6562
new WriteFiles()
6663
};
6764
}

src/Statiq.Web/Pipelines/Data.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Linq;
4-
using System.Text;
5-
using Statiq.App;
63
using Statiq.Common;
74
using Statiq.Core;
8-
using Statiq.Html;
9-
using Statiq.Markdown;
10-
using Statiq.Razor;
5+
using Statiq.Web.Modules;
116
using Statiq.Yaml;
127

138
namespace Statiq.Web.Pipelines
@@ -16,6 +11,8 @@ public class Data : Pipeline
1611
{
1712
public Data()
1813
{
14+
Dependencies.Add(nameof(DirectoryMetadata));
15+
1916
InputModules = new ModuleList
2017
{
2118
new ReadFiles(Config.FromSetting<IEnumerable<string>>(WebKeys.DataFiles))
@@ -26,6 +23,9 @@ public Data()
2623
// Concat all documents from externally declared dependencies (exclude explicit dependencies above)
2724
new ConcatDocuments(Config.FromContext<IEnumerable<IDocument>>(ctx => ctx.Outputs.FromPipelines(ctx.Pipeline.GetAllDependencies(ctx).Except(Dependencies).ToArray()))),
2825

26+
// Apply directory metadata
27+
new ApplyDirectoryMetadata(),
28+
2929
// Parse the content into metadata depending on the content type
3030
new ExecuteSwitch(Config.FromDocument(doc => doc.ContentProvider.MediaType))
3131
.Case(MediaTypes.Json, new ParseJson())
@@ -46,7 +46,7 @@ public Data()
4646

4747
OutputModules = new ModuleList
4848
{
49-
new FilterDocuments(Config.FromSetting<bool>(WebKeys.OutputData)),
49+
new FilterDocuments(Config.FromDocument<bool>(WebKeys.ShouldOutput)),
5050
new WriteFiles()
5151
};
5252
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO.Pipelines;
4+
using System.Linq;
5+
using System.Text;
6+
using Statiq.App;
7+
using Statiq.Common;
8+
using Statiq.Core;
9+
using Statiq.Html;
10+
using Statiq.Markdown;
11+
using Statiq.Razor;
12+
using Statiq.Yaml;
13+
14+
namespace Statiq.Web.Pipelines
15+
{
16+
public class DirectoryMetadata : Pipeline
17+
{
18+
public DirectoryMetadata()
19+
{
20+
InputModules = new ModuleList
21+
{
22+
new ReadFiles(Config.FromSetting<IEnumerable<string>>(WebKeys.DirectoryMetadataFiles))
23+
};
24+
25+
ProcessModules = new ModuleList
26+
{
27+
// Parse the content into metadata depending on the content type
28+
new ExecuteSwitch(Config.FromDocument(doc => doc.ContentProvider.MediaType))
29+
.Case(MediaTypes.Json, new ParseJson())
30+
.Case(MediaTypes.Yaml, new ParseYaml())
31+
};
32+
}
33+
}
34+
}

src/Statiq.Web/Pipelines/Sitemap.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ public Sitemap()
1616
{
1717
new ExecuteIf(Config.FromSetting<bool>(WebKeys.GenerateSitemap))
1818
{
19-
new ConcatDocuments(nameof(Content)),
19+
new ConcatDocuments(nameof(Content))
20+
{
21+
new FilterDocuments(Config.FromDocument(WebKeys.ShouldOutput, true))
22+
},
2023
new ConcatDocuments(nameof(Data))
2124
{
22-
new FilterDocuments(Config.FromSetting<bool>(WebKeys.OutputData))
25+
new FilterDocuments(Config.FromDocument<bool>(WebKeys.ShouldOutput))
2326
},
2427
new ConcatDocuments(nameof(Archives)),
2528
new FlattenTree(),

src/Statiq.Web/WebKeys.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public static class WebKeys
2525
/// </summary>
2626
public const string DataFiles = nameof(DataFiles);
2727

28+
/// <summary>
29+
/// The globbing pattern(s) that will be used to read directory metadata.
30+
/// </summary>
31+
public const string DirectoryMetadataFiles = nameof(DirectoryMetadataFiles);
32+
2833
public const string OptimizeContentFileNames = nameof(OptimizeContentFileNames);
2934

3035
public const string OptimizeDataFileNames = nameof(OptimizeDataFileNames);
@@ -160,9 +165,10 @@ public static class WebKeys
160165
public const string FeedItemThreadUpdated = nameof(FeedItemThreadUpdated);
161166

162167
/// <summary>
163-
/// Indicates that the data file (.json, .yaml, etc.) should be output (by default data files are not output).
168+
/// Indicates that the content or data file should be output.
169+
/// By default content files are output and data files are not.
164170
/// </summary>
165-
public const string OutputData = nameof(OutputData);
171+
public const string ShouldOutput = nameof(ShouldOutput);
166172

167173
/// <summary>
168174
/// Indicates the layout file that should be used for this document.
@@ -175,5 +181,21 @@ public static class WebKeys
175181
/// if no <see cref="Title"/> metadata is defined for the document).
176182
/// </summary>
177183
public const string Xref = nameof(Xref);
184+
185+
/// <summary>
186+
/// Used with directory metadata to indicate if the metadata should be applied
187+
/// recursively to files in child directories (the default is <c>true</c>).
188+
/// </summary>
189+
public const string Recursive = nameof(Recursive);
190+
191+
/// <summary>
192+
/// Indicates that post-process templates should be rendered (the default is <c>true</c>).
193+
/// </summary>
194+
/// <remarks>
195+
/// Set this to <c>false</c> for a document to prevent rendering post-process templates such as Razor.
196+
/// This can be helpful when you have small bits of content like Markdown that you want to render
197+
/// as HTML but not as an entire page so that it can be included in other pages.
198+
/// </remarks>
199+
public const string RenderPostProcessTemplates = nameof(RenderPostProcessTemplates);
178200
}
179201
}

0 commit comments

Comments
 (0)