diff --git a/.build.Nuke/Build.Test.cs b/.build.Nuke/Build.Test.cs
index 937b3f6fdd851c4352ba3fed5224d566b1403d81..c1d0c371cce12a76b54307f5994f66aa842895da 100644
--- a/.build.Nuke/Build.Test.cs
+++ b/.build.Nuke/Build.Test.cs
@@ -6,7 +6,6 @@ using Nuke.Common.Tools.ReportGenerator;
using Nuke.Common.IO;
using Nuke.Common.Tools.Coverlet;
using static Nuke.Common.Tools.Coverlet.CoverletTasks;
-using static Nuke.Common.IO.FileSystemTasks;
using Serilog;
using System;
@@ -20,8 +19,6 @@ sealed partial class Build : NukeBuild
{
AbsolutePath testDirectory => RootDirectory / "test";
AbsolutePath testDLLDirectory => testDirectory / "bin" / "Debug" / "net7.0";
- AbsolutePath testSiteSourceDirectory => RootDirectory / "test" / ".TestSites";
- AbsolutePath testSiteDestinationDirectory => testDLLDirectory / ".TestSites";
AbsolutePath testAssembly => testDLLDirectory / "test.dll";
AbsolutePath coverageDirectory => RootDirectory / "coverage-results";
AbsolutePath coverageResultDirectory => coverageDirectory / "coverage";
@@ -29,16 +26,8 @@ sealed partial class Build : NukeBuild
AbsolutePath coverageReportDirectory => coverageDirectory / "report";
AbsolutePath coverageReportSummaryDirectory => coverageReportDirectory / "Summary.txt";
- Target PrepareTestFiles => _ => _
- .After(Clean)
- .Executes(() =>
- {
- testSiteDestinationDirectory.CreateOrCleanDirectory();
- CopyDirectoryRecursively(testSiteSourceDirectory, testSiteDestinationDirectory, DirectoryExistsPolicy.Merge);
- });
-
Target Test => _ => _
- .DependsOn(Compile, PrepareTestFiles)
+ .DependsOn(Compile)
.Executes(() =>
{
coverageResultDirectory.CreateDirectory();
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index 9dbaef2a329f0d078efc6021c3c112e4abfcdf60..7def812ce0dc0a3e51e5560b0cd87f877e9eaf09 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -115,7 +115,6 @@
"CreatePackage",
"GitLabCreateRelease",
"GitLabCreateTag",
- "PrepareTestFiles",
"Publish",
"Restore",
"ShowCurrentVersion",
@@ -141,7 +140,6 @@
"CreatePackage",
"GitLabCreateRelease",
"GitLabCreateTag",
- "PrepareTestFiles",
"Publish",
"Restore",
"ShowCurrentVersion",
diff --git a/source/BuildCommand.cs b/source/BuildCommand.cs
index 57766406029fcfdaa3495e19ff42633f6ae9e55c..8f6c16e4f0a0aa6f394bc75e23708221ba97a783 100644
--- a/source/BuildCommand.cs
+++ b/source/BuildCommand.cs
@@ -65,7 +65,7 @@ public class BuildCommand : BaseGeneratorCommand
File.WriteAllText(outputAbsolutePath, result);
// Log
- logger.Debug("Page created: {Permalink}", frontmatter.Permalink);
+ logger.Debug("Page created: {Permalink}", outputAbsolutePath);
// Use interlocked to safely increment the counter in a multi-threaded environment
_ = Interlocked.Increment(ref pagesCreated);
diff --git a/source/Helpers/FileUtils.cs b/source/Helpers/FileUtils.cs
index a098144fce5d9e0fab3c9a45a6f02063c0fe72a0..d69f404e9c9be596b17d66328ef17136f22729ca 100644
--- a/source/Helpers/FileUtils.cs
+++ b/source/Helpers/FileUtils.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using SuCoS.Models;
namespace SuCoS.Helper;
@@ -14,20 +15,20 @@ public static class FileUtils
/// Gets all Markdown files in the specified directory.
///
/// The directory path.
+ /// The initial directory path.
/// The list of Markdown file paths.
- public static List GetAllMarkdownFiles(string directory)
+ public static List GetAllMarkdownFiles(string directory, string? basePath = null)
{
- var markdownFiles = new List();
- var files = Directory.GetFiles(directory, "*.md");
- markdownFiles.AddRange(files);
+ basePath ??= directory;
+ var files = Directory.GetFiles(directory, "*.md").ToList();
var subdirectories = Directory.GetDirectories(directory);
foreach (var subdirectory in subdirectories)
{
- markdownFiles.AddRange(GetAllMarkdownFiles(subdirectory));
+ files.AddRange(GetAllMarkdownFiles(subdirectory, basePath));
}
- return markdownFiles;
+ return files;
}
///
diff --git a/source/Helpers/SiteHelper.cs b/source/Helpers/SiteHelper.cs
index 9993b2e46a0bfd246e72b4f78c9068a03894c221..ecaa537d678a0a4db7cebc4cf3b325e9e6f702d6 100644
--- a/source/Helpers/SiteHelper.cs
+++ b/source/Helpers/SiteHelper.cs
@@ -91,7 +91,9 @@ public static class SiteHelper
site.ResetCache();
- site.ScanAllMarkdownFiles();
+ // Scan content files
+ var markdownFiles = FileUtils.GetAllMarkdownFiles(site.SourceContentPath);
+ site.ContentPaths.AddRange(markdownFiles);
site.ParseSourceFiles(stopwatch);
diff --git a/source/Models/Frontmatter.cs b/source/Models/Frontmatter.cs
index 33cd6236d29b9e1591d06bd1bc161b3de5c50a92..466ea35b037d77bcb45f0b288c6e317893d24cd4 100644
--- a/source/Models/Frontmatter.cs
+++ b/source/Models/Frontmatter.cs
@@ -316,6 +316,7 @@ public class Frontmatter : IBaseContent, IParams
URLforce ??= URL
?? (isIndex ? "{{ page.SourcePathDirectory }}" : "{{ page.SourcePathDirectory }}/{{ page.Title }}");
+
try
{
if (Site.FluidParser.TryParse(URLforce, out var template, out var error))
diff --git a/source/Models/Site.cs b/source/Models/Site.cs
index 4ec4c888b438c3d6b7b008375269832bdb836967..3c59960c00887512c8464ad6213812392953570f 100644
--- a/source/Models/Site.cs
+++ b/source/Models/Site.cs
@@ -123,7 +123,7 @@ public class Site : IParams
/// List of all content to be scanned and processed.
///
[YamlIgnore]
- public List<(string filePath, string content)> RawPages { get; set; } = new();
+ public List ContentPaths { get; set; } = new();
///
/// Command line options
@@ -217,22 +217,6 @@ public class Site : IParams
this.Clock = clock;
}
- ///
- /// Scans all markdown files in the source directory.
- ///
- public void ScanAllMarkdownFiles()
- {
- // Scan content files
- var markdownFiles = FileUtils.GetAllMarkdownFiles(SourceContentPath);
-
- foreach (var fileAbsolutePath in markdownFiles)
- {
- var content = File.ReadAllText(fileAbsolutePath);
- var relativePath = Path.GetRelativePath(SourceContentPath, fileAbsolutePath);
- RawPages.Add((relativePath, content));
- }
- }
-
///
/// Resets the template cache to force a reload of all templates.
///
@@ -321,11 +305,12 @@ public class Site : IParams
// Process the source files, extracting the frontmatter
var filesParsed = 0; // counter to keep track of the number of files processed
- _ = Parallel.ForEach(RawPages, file =>
+ _ = Parallel.ForEach(ContentPaths, filePath =>
{
try
{
- var frontmatter = ReadSourceFrontmatter(file.filePath, file.content, frontmatterParser);
+ var frontmatter = frontmatterParser.ParseFrontmatterAndMarkdownFromFile(this, filePath, SourceContentPath)
+ ?? throw new FormatException($"Error parsing frontmatter for {filePath}");
if (frontmatter.IsValidDate(options))
{
@@ -334,7 +319,7 @@ public class Site : IParams
}
catch (Exception ex)
{
- Logger?.Error(ex, "Error parsing file {file}", file.filePath);
+ Logger?.Error(ex, "Error parsing file {file}", filePath);
}
// Use interlocked to safely increment the counter in a multi-threaded environment
@@ -435,30 +420,4 @@ public class Site : IParams
CreateAutomaticFrontmatter(contentTemplate, frontmatter);
}
}
-
- ///
- /// Reads the frontmatter from the source file.
- ///
- /// The file path.
- /// The file content.
- /// The frontmatter parser.
- /// The parsed frontmatter.
- private Frontmatter ReadSourceFrontmatter(string filePath, string content, IFrontmatterParser frontmatterParser)
- {
- // test if filePath or config is null
- if (filePath is null)
- {
- throw new ArgumentNullException(nameof(filePath));
- }
- if (frontmatterParser is null)
- {
- throw new ArgumentNullException(nameof(frontmatterParser));
- }
-
- // Separate the YAML frontmatter from the file content
- var frontmatter = frontmatterParser.ParseFrontmatter(this, filePath, content)
- ?? throw new FormatException($"Error parsing frontmatter for {filePath}");
-
- return frontmatter;
- }
}
diff --git a/source/Parser/IFrontmatterParser.cs b/source/Parser/IFrontmatterParser.cs
index 045d9c5b38bff099f36693fd37339b8a8a6ad840..0277a6069c92d63910e8f482452351a53037df69 100644
--- a/source/Parser/IFrontmatterParser.cs
+++ b/source/Parser/IFrontmatterParser.cs
@@ -11,10 +11,19 @@ public interface IFrontmatterParser
/// Extract the frontmatter from the content file.
///
///
+ ///
+ ///
+ ///
+ Frontmatter? ParseFrontmatterAndMarkdownFromFile(Site site, in string filePath, in string sourceContentPath);
+
+ ///
+ /// Extract the frontmatter from the content.
+ ///
+ ///
///
///
///
- Frontmatter? ParseFrontmatter(Site site, in string filePath, in string fileContent);
+ Frontmatter? ParseFrontmatterAndMarkdown(Site site, in string filePath, in string fileContent);
///
/// Parse the app config file.
diff --git a/source/Parser/YAMLParser.cs b/source/Parser/YAMLParser.cs
index f25235f97d3890584cdaba46327fd30422f920e8..897c27128e69662aebfbe531eb9d606047d13927 100644
--- a/source/Parser/YAMLParser.cs
+++ b/source/Parser/YAMLParser.cs
@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text.RegularExpressions;
+using System.Text;
using SuCoS.Helper;
using SuCoS.Models;
using YamlDotNet.Serialization;
@@ -14,9 +14,6 @@ namespace SuCoS.Parser;
///
public partial class YAMLParser : IFrontmatterParser
{
- [GeneratedRegex(@"^---\s*[\r\n](?.*?)[\r\n]---\s*", RegexOptions.Singleline)]
- private partial Regex YAMLRegex();
-
///
/// YamlDotNet parser, strictly set to allow automatically parse only known fields
///
@@ -32,7 +29,7 @@ public partial class YAMLParser : IFrontmatterParser
.Build();
///
- public Frontmatter? ParseFrontmatter(Site site, in string filePath, in string fileContent)
+ public Frontmatter? ParseFrontmatterAndMarkdownFromFile(Site site, in string filePath, in string? sourceContentPath = null)
{
if (site is null)
{
@@ -43,20 +40,56 @@ public partial class YAMLParser : IFrontmatterParser
throw new ArgumentNullException(nameof(filePath));
}
- var match = YAMLRegex().Match(fileContent);
- if (match.Success)
+ string? fileContent;
+ string? fileRelativePath;
+ try
+ {
+ fileContent = File.ReadAllText(filePath);
+ fileRelativePath = Path.GetRelativePath(sourceContentPath ?? string.Empty, filePath);
+ }
+ catch (Exception ex)
+ {
+ throw new FileNotFoundException(filePath, ex);
+ }
+
+ return ParseFrontmatterAndMarkdown(site, fileRelativePath, fileContent);
+ }
+
+ ///
+ public Frontmatter? ParseFrontmatterAndMarkdown(Site site, in string fileRelativePath, in string fileContent)
+ {
+ if (site is null)
+ {
+ throw new ArgumentNullException(nameof(site));
+ }
+ if (fileRelativePath is null)
+ {
+ throw new ArgumentNullException(nameof(fileRelativePath));
+ }
+
+ using var content = new StringReader(fileContent);
+ var frontmatterBuilder = new StringBuilder();
+ string? line;
+
+ while ((line = content.ReadLine()) != null && line != "---") { }
+ while ((line = content.ReadLine()) != null && line != "---")
{
- var yaml = match.Groups["frontmatter"].Value;
- var rawContent = fileContent[match.Length..].TrimStart('\n');
- var frontmatter = ParseYAML(ref site, filePath, yaml, rawContent);
- return frontmatter;
+ frontmatterBuilder.AppendLine(line);
}
- return null;
+
+ // Join the read lines to form the frontmatter
+ var yaml = frontmatterBuilder.ToString();
+ var rawContent = content.ReadToEnd();
+
+ // Now, you can parse the YAML frontmatter
+ var page = ParseYAML(ref site, fileRelativePath, yaml, rawContent);
+
+ return page;
}
- private Frontmatter ParseYAML(ref Site site, in string filePath, string frontmatter, string rawContent)
+ private Frontmatter ParseYAML(ref Site site, in string filePath, string yaml, in string rawContent)
{
- var page = yamlDeserializerRigid.Deserialize(frontmatter);
+ var page = yamlDeserializerRigid.Deserialize(new StringReader(yaml)) ?? throw new FormatException("Error parsing frontmatter");
var sourceFileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath) ?? string.Empty;
var section = SiteHelper.GetSection(filePath);
page.RawContent = rawContent;
@@ -68,13 +101,14 @@ public partial class YAMLParser : IFrontmatterParser
page.Title ??= sourceFileNameWithoutExtension;
page.Type ??= section;
- var yamlObject = yamlDeserializer.Deserialize(new StringReader(frontmatter));
+ var yamlObject = yamlDeserializer.Deserialize(new StringReader(yaml));
if (yamlObject is Dictionary