diff --git a/source/BaseGeneratorCommand.cs b/source/BaseGeneratorCommand.cs
index 3a827765da5c75067d377c34f588272d8718eced..7d84c05cfb74fcae3a1f083c7040c59067e4b31f 100644
--- a/source/BaseGeneratorCommand.cs
+++ b/source/BaseGeneratorCommand.cs
@@ -17,6 +17,11 @@ namespace SuCoS;
///
public abstract class BaseGeneratorCommand
{
+ ///
+ /// Command line options
+ ///
+ readonly IGenerateOptions options;
+
///
/// The configuration file name.
///
@@ -167,6 +172,7 @@ public abstract class BaseGeneratorCommand
{
throw new ArgumentNullException(nameof(options));
}
+ this.options = options;
Log.Information("Source path: {source}", propertyValue: options.Source);
@@ -228,12 +234,25 @@ public abstract class BaseGeneratorCommand
try
{
var frontmatter = ReadSourceFrontmatter(file.filePath, file.content, site, frontmatterParser);
- site.Pages.Add(frontmatter);
- site.RegularPages.Add(frontmatter);
+
+ if (IsValidDate(frontmatter))
+ {
+ site.Pages.Add(frontmatter);
+ site.RegularPages.Add(frontmatter);
+
+ // Convert the Markdown content to HTML
+ frontmatter.ContentPreRendered = Markdown.ToHtml(frontmatter.ContentRaw);
+ frontmatter.Permalink = "/" + GetOutputPath(file.filePath, site, frontmatter);
+
+ if (site.HomePage is null && string.IsNullOrEmpty(frontmatter.SourcePath) && frontmatter.SourceFileNameWithoutExtension == "index")
+ {
+ site.HomePage = frontmatter;
+ }
+ }
}
- catch
+ catch (Exception ex)
{
- Log.Error("Error parsing file {file}", file.filePath);
+ Log.Error(ex, "Error parsing file {file}", file.filePath);
}
// Use interlocked to safely increment the counter in a multi-threaded environment
@@ -359,18 +378,22 @@ public abstract class BaseGeneratorCommand
var frontmatter = frontmatterParser.ParseFrontmatter(site, filePath, ref content, this)
?? throw new FormatException($"Error parsing frontmatter for {filePath}");
- // Convert the Markdown content to HTML
- frontmatter.ContentPreRendered = Markdown.ToHtml(frontmatter.ContentRaw, markdownPipeline);
- frontmatter.Permalink = "/" + GetOutputPath(filePath, site, frontmatter);
-
- if (site.HomePage is null && string.IsNullOrEmpty(frontmatter.SourcePath) && frontmatter.SourceFileNameWithoutExtension == "index")
- {
- site.HomePage = frontmatter;
- }
-
return frontmatter;
}
+ ///
+ /// Check if the page have a publishing date from the past.
+ ///
+ /// The given page
+ ///
+ bool IsValidDate(Frontmatter frontmatter)
+ {
+ return (frontmatter.PublishDate is null && frontmatter.Date is null)
+ || options.Future
+ || (frontmatter.PublishDate != null && (frontmatter.PublishDate <= DateTime.Now))
+ || (frontmatter.Date != null && (frontmatter.Date <= DateTime.Now));
+ }
+
///
/// Gets the output path for the file.
///
diff --git a/source/BuildOptions.cs b/source/BuildOptions.cs
index 11b073a966e220e7b475ebdbc6f5dd2e8ee71953..b1454b1fe994fc65da46781cf23501ba30a5aa61 100644
--- a/source/BuildOptions.cs
+++ b/source/BuildOptions.cs
@@ -5,18 +5,15 @@ namespace SuCoS;
///
public class BuildOptions : IGenerateOptions
{
- ///
- /// The path of the source files.
- ///
+ ///
public string Source { get; set; } = ".";
- ///
- /// The path of the output files.
- ///
+ ///
public string Output { get; set; } = "./public";
- ///
- /// If true, the program will print more information.
- ///
+ ///
+ public bool Future { get; set; } = false;
+
+ ///
public bool Verbose { get; set; } = false;
}
diff --git a/source/IGenerateOptions.cs b/source/IGenerateOptions.cs
index f11cf04e2e71c2edeb319e3b3a0688128c050056..a89f3be7126dd5c5f1122fe4fd6b9f2e539ea722 100644
--- a/source/IGenerateOptions.cs
+++ b/source/IGenerateOptions.cs
@@ -15,6 +15,11 @@ public interface IGenerateOptions
///
public string Output { get; set; }
+ ///
+ /// Consider
+ ///
+ bool Future { get; set; }
+
///
/// If true, the program will print more information.
///
diff --git a/source/Models/Frontmatter.cs b/source/Models/Frontmatter.cs
index aa5bebe9b587e48ab4fe9f54111bab29329387f3..323df8ebf9e9d80584c300bf1bd97ebdfe743491 100644
--- a/source/Models/Frontmatter.cs
+++ b/source/Models/Frontmatter.cs
@@ -16,6 +16,26 @@ public class Frontmatter
///
public string Title { get; init; }
+ ///
+ /// Gets or sets the date of the page.
+ ///
+ public DateTime? Date { get; set; }
+
+ ///
+ /// Gets or sets the last modification date of the page.
+ ///
+ public DateTime? LastMod { get; set; }
+
+ ///
+ /// Gets or sets the publish date of the page.
+ ///
+ public DateTime? PublishDate { get; set; }
+
+ ///
+ /// Gets or sets the expiry date of the page.
+ ///
+ public DateTime? ExpiryDate { get; set; }
+
///
/// The path of the file, if it's a file.
///
diff --git a/source/Parser/YAMLParser.cs b/source/Parser/YAMLParser.cs
index 5ac8d4c5ecd03d70b1d93cefee9660252e4851c2..ae7453e11d00c7b30951bbe75c2a0399e431e825 100644
--- a/source/Parser/YAMLParser.cs
+++ b/source/Parser/YAMLParser.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text.RegularExpressions;
+using Serilog;
using SuCoS.Models;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
@@ -14,7 +16,7 @@ namespace SuCoS.Parser;
public partial class YAMLParser : IFrontmatterParser
{
[GeneratedRegex(@"^---\s*[\r\n](?.*?)[\r\n]---\s*", RegexOptions.Singleline)]
- private partial Regex _regex();
+ private partial Regex regex();
///
public Frontmatter? ParseFrontmatter(Site site, string filePath, ref string fileContent, BaseGeneratorCommand frontmatterManager)
@@ -25,7 +27,7 @@ public partial class YAMLParser : IFrontmatterParser
}
Frontmatter? frontmatter = null;
- var match = _regex().Match(fileContent);
+ var match = regex().Match(fileContent);
if (match.Success)
{
var frontmatterString = match.Groups["frontmatter"].Value;
@@ -41,6 +43,10 @@ public partial class YAMLParser : IFrontmatterParser
_ = frontmatterDictionary.TryGetValue("Title", out var titleValue);
_ = frontmatterDictionary.TryGetValue("URL", out var urlValue);
_ = frontmatterDictionary.TryGetValue("Type", out var typeValue);
+ _ = frontmatterDictionary.TryGetValue("Date", out var dateValue);
+ _ = frontmatterDictionary.TryGetValue("LastMod", out var dateLastModValue);
+ _ = frontmatterDictionary.TryGetValue("PublishDate", out var datePublishValue);
+ _ = frontmatterDictionary.TryGetValue("ExpiryDate", out var dateExpiryValue);
var section = GetSection(site, filePath);
List tags = new();
@@ -69,7 +75,11 @@ public partial class YAMLParser : IFrontmatterParser
URL = urlValue?.ToString(),
Section = section,
Type = typeValue?.ToString() ?? section,
- Kind = Kind.single
+ Kind = Kind.single,
+ Date = DateTime.TryParse(dateValue?.ToString(), out var parsedDate) ? parsedDate : null,
+ LastMod = DateTime.TryParse(dateLastModValue?.ToString(), out var parsedLastMod) ? parsedLastMod : null,
+ PublishDate = DateTime.TryParse(datePublishValue?.ToString(), out var parsedPublishDate) ? parsedPublishDate : null,
+ ExpiryDate = DateTime.TryParse(dateExpiryValue?.ToString(), out var parsedExpiryDate) ? parsedExpiryDate : null
};
foreach (var tagName in tags)
diff --git a/source/Program.cs b/source/Program.cs
index 0dafccdcb1542b7b99b0576e36b1fe32eb526ca0..8e3266effc07ce7d1ab17c0333fa03081dd581bf 100644
--- a/source/Program.cs
+++ b/source/Program.cs
@@ -30,40 +30,45 @@ public class Program
// Shared options between the commands
var sourceOption = new Option(new[] { "--source", "-s" }, () => ".", "Source directory path");
+ var futureOption = new Option(new[] { "--future", "-f" }, () => false, "Include content with dates in the future");
var verboseOption = new Option(new[] { "--verbose", "-v" }, () => false, "Verbose output");
// BuildCommand setup
var buildOutputOption = new Option(new[] { "--output", "-o" }, () => "./public", "Output directory path");
Command buildCommand = new("build", "Builds the site")
- {
- sourceOption,
- buildOutputOption,
- verboseOption
- };
- buildCommand.SetHandler((source, output, verbose) =>
+ {
+ sourceOption,
+ buildOutputOption,
+ futureOption,
+ verboseOption
+ };
+ buildCommand.SetHandler((source, output, future, verbose) =>
{
BuildOptions buildOptions = new()
{
Source = source,
Output = output,
+ Future = future,
Verbose = verbose
};
_ = new BuildCommand(buildOptions);
},
- sourceOption, buildOutputOption, verboseOption);
+ sourceOption, buildOutputOption, futureOption, verboseOption);
// ServerCommand setup
Command serveCommand = new("serve", "Starts the server")
{
sourceOption,
+ futureOption,
verboseOption
};
- serveCommand.SetHandler(async (source, verbose) =>
+ serveCommand.SetHandler(async (source, future, verbose) =>
{
ServeOptions serverOptions = new()
{
Source = source,
+ Future = future,
Verbose = verbose
};
@@ -71,7 +76,7 @@ public class Program
await serveCommand.RunServer();
await Task.Delay(-1); // Wait forever.
},
- sourceOption, verboseOption);
+ sourceOption, futureOption, verboseOption);
RootCommand rootCommand = new("SuCoS commands")
{
diff --git a/source/ServeOptions.cs b/source/ServeOptions.cs
index b0a339acd39d3b31be719057239d617051de0720..d393250f351815a9c8c5b89679938a2d2e35269e 100644
--- a/source/ServeOptions.cs
+++ b/source/ServeOptions.cs
@@ -5,18 +5,15 @@ namespace SuCoS;
///
public class ServeOptions : IGenerateOptions
{
- ///
- /// The path of the source files.
- ///
+ ///
public string Source { get; set; } = ".";
- ///
- /// The path of the output files.
- ///
+ ///
public string Output { get; set; } = "./public";
- ///
- /// If true, the program will print more information.
- ///
+ ///
+ public bool Future { get; set; } = false;
+
+ ///
public bool Verbose { get; set; } = false;
}