diff --git a/source/Helpers/SiteHelper.cs b/source/Helpers/SiteHelper.cs
index 06dd373281ad8ffd682721f86f6973725ec8a3f1..858d05a3f231007cf2d6b392b4952aa425bc5a15 100644
--- a/source/Helpers/SiteHelper.cs
+++ b/source/Helpers/SiteHelper.cs
@@ -25,21 +25,21 @@ public static class SiteHelper
/// Creates the pages dictionary.
///
///
- public static Site Init(string configFile, IGenerateOptions options, IMetadataParser frontMatterParser, FilterDelegate whereParamsFilter, ILogger logger, StopwatchReporter stopwatch)
+ public static Site Init(string configFile, IGenerateOptions options, IMetadataParser parser, FilterDelegate whereParamsFilter, ILogger logger, StopwatchReporter stopwatch)
{
ArgumentNullException.ThrowIfNull(stopwatch);
SiteSettings siteSettings;
try
{
- siteSettings = ParseSettings(configFile, options, frontMatterParser);
+ siteSettings = ParseSettings(configFile, options, parser);
}
catch
{
throw;
}
- var site = new Site(options, siteSettings, frontMatterParser, logger, null);
+ var site = new Site(options, siteSettings, parser, logger, null);
// Liquid template options, needed to theme the content
// but also parse URLs
@@ -53,7 +53,10 @@ public static class SiteHelper
stopwatch.Stop("Parse", site.FilesParsedToReport);
- site.TemplateOptions.FileProvider = new PhysicalFileProvider(Path.GetFullPath(site.SourceThemePath));
+ if (Directory.Exists(Path.GetFullPath(site.SourceThemePath)))
+ {
+ site.TemplateOptions.FileProvider = new PhysicalFileProvider(Path.GetFullPath(site.SourceThemePath));
+ }
return site;
}
@@ -89,13 +92,13 @@ public static class SiteHelper
/// Reads the application settings.
///
/// The generate options.
- /// The front matter parser.
+ /// The front matter parser.
/// The site settings file.
/// The site settings.
- private static SiteSettings ParseSettings(string configFile, IGenerateOptions options, IMetadataParser frontMatterParser)
+ public static SiteSettings ParseSettings(string configFile, IGenerateOptions options, IMetadataParser parser)
{
ArgumentNullException.ThrowIfNull(options);
- ArgumentNullException.ThrowIfNull(frontMatterParser);
+ ArgumentNullException.ThrowIfNull(parser);
// Read the main configation
var filePath = Path.Combine(options.Source, configFile);
@@ -105,7 +108,7 @@ public static class SiteHelper
}
var fileContent = File.ReadAllText(filePath);
- var siteSettings = frontMatterParser.ParseSiteSettings(fileContent)
+ var siteSettings = parser.ParseSiteSettings(fileContent)
?? throw new FormatException($"Error reading app config {configFile}");
return siteSettings;
}
diff --git a/source/Models/Site.cs b/source/Models/Site.cs
index 81162ffa16ee243ea98de1be228b92809ecca479..264efbc78293ded3bdfc27ed2b7ba349ace3139a 100644
--- a/source/Models/Site.cs
+++ b/source/Models/Site.cs
@@ -71,7 +71,7 @@ public class Site : ISite
///
/// The path theme.
///
- public string SourceThemePath => Path.Combine(Options.Source, "theme");
+ public string SourceThemePath => Path.Combine(Options.Source, settings.ThemeDir, settings.Theme ?? string.Empty);
///
/// The path of the static content (that will be copied as is), based on the theme path.
diff --git a/source/Models/SiteSettings.cs b/source/Models/SiteSettings.cs
index 29ec6514a332ffe6af3e69d85e878382467017c3..41b0b5f1d49c05335bb76d181d8ba19c060a1f94 100644
--- a/source/Models/SiteSettings.cs
+++ b/source/Models/SiteSettings.cs
@@ -21,13 +21,23 @@ public class SiteSettings : IParams
///
/// Copyright information
///
- public string? Copyright { get; set; } = string.Empty;
+ public string? Copyright { get; set; }
///
/// The base URL that will be used to build public links.
///
public string BaseURL { get; set; } = string.Empty;
+ ///
+ /// The global site theme.
+ ///
+ public string? Theme { get; set; }
+
+ ///
+ /// The theme folder where all themes are placed (if any).
+ ///
+ public string ThemeDir { get; set; } = "themes";
+
///
/// The appearance of a URL is either ugly or pretty.
///
diff --git a/source/NewSiteCommand.cs b/source/NewSiteCommand.cs
index 7d01736d791737e00a626e42e48407d498686e0b..b46b99139292704efd2ed7709f5d92a6a24eab88 100644
--- a/source/NewSiteCommand.cs
+++ b/source/NewSiteCommand.cs
@@ -1,14 +1,14 @@
using Serilog;
using SuCoS.Models;
using SuCoS.Models.CommandLineOptions;
-using YamlDotNet.Serialization;
+using SuCoS.Parser;
namespace SuCoS;
///
/// Check links of a given site.
///
-public sealed partial class NewSiteCommand(NewSiteOptions settings, ILogger logger)
+public sealed partial class NewSiteCommand(NewSiteOptions options, ILogger logger)
{
///
/// Run the app
@@ -18,18 +18,18 @@ public sealed partial class NewSiteCommand(NewSiteOptions settings, ILogger logg
{
var siteSettings = new SiteSettings()
{
- Title = settings.Title,
- Description = settings.Description,
- BaseURL = settings.BaseURL,
+ Title = options.Title,
+ Description = options.Description,
+ BaseURL = options.BaseURL
};
// TODO: Refactor Site class to not need YAML parser nor FrontMatterParser
- var site = new Site(new ServeOptions() { SourceOption = settings.Output }, siteSettings, null!, logger, null);
+ var site = new Site(new ServeOptions() { SourceOption = options.Output }, siteSettings, null!, logger, null);
- var outputPath = Path.GetFullPath(settings.Output);
+ var outputPath = Path.GetFullPath(options.Output);
var siteSettingsPath = Path.Combine(outputPath, "sucos.yaml");
- if (File.Exists(siteSettingsPath) && !settings.Force)
+ if (File.Exists(siteSettingsPath) && !options.Force)
{
logger.Error("{directoryPath} already exists", outputPath);
return 1;
@@ -41,15 +41,16 @@ public sealed partial class NewSiteCommand(NewSiteOptions settings, ILogger logg
try
{
- ExportSiteSettings(siteSettings, siteSettingsPath);
+ var parser = new YAMLParser();
+ parser.Export(siteSettings, siteSettingsPath);
}
catch (Exception ex)
{
logger.Error("Failed to export site settings: {ex}", ex);
return 1;
}
- logger.Information("Done");
+ logger.Information("Done");
return 0;
}
@@ -65,25 +66,4 @@ public sealed partial class NewSiteCommand(NewSiteOptions settings, ILogger logg
Directory.CreateDirectory(folder);
}
}
-
- // TODO: move all YAML parsing to this own class
- #region YAML
- ///
- /// YamlDotNet parser to loosely parse the YAML file. Used to include all non-matching fields
- /// into Params.
- ///
- ISerializer yamlDeserializer = new SerializerBuilder()
- .IgnoreFields()
- .ConfigureDefaultValuesHandling(
- DefaultValuesHandling.OmitEmptyCollections
- | DefaultValuesHandling.OmitDefaults
- | DefaultValuesHandling.OmitNull)
- .Build();
-
- void ExportSiteSettings(SiteSettings siteSettings, string siteSettingsPath)
- {
- var siteSettingsConverted = yamlDeserializer.Serialize(siteSettings);
- File.WriteAllText(siteSettingsPath, siteSettingsConverted);
- }
- #endregion YAML
}
\ No newline at end of file
diff --git a/source/Parser/IMetadataParser.cs b/source/Parser/IMetadataParser.cs
index 959e84c19569746dc28ee850ee50ffb85c3eda5f..57c0db7812f89a7a2863cb48638d21d4fd1209ec 100644
--- a/source/Parser/IMetadataParser.cs
+++ b/source/Parser/IMetadataParser.cs
@@ -30,4 +30,11 @@ public interface IMetadataParser
///
///
SiteSettings ParseSiteSettings(string configFileContent);
+
+ ///
+ /// Deserialized a object.
+ ///
+ ///
+ ///
+ void Export(T data, string path);
}
\ No newline at end of file
diff --git a/source/Parser/YAMLParser.cs b/source/Parser/YAMLParser.cs
index d37c5547cc7c28691d4ceac5bdccacfca7acca34..ac84d866284c390abd99a27a226867f31db6b33d 100644
--- a/source/Parser/YAMLParser.cs
+++ b/source/Parser/YAMLParser.cs
@@ -13,14 +13,14 @@ public class YAMLParser : IMetadataParser
///
/// YamlDotNet parser, strictly set to allow automatically parse only known fields
///
- private readonly IDeserializer yamlDeserializerRigid;
+ private readonly IDeserializer deserializer;
///
/// ctor
///
public YAMLParser()
{
- yamlDeserializerRigid = new StaticDeserializerBuilder(new StaticAOTContext())
+ deserializer = new StaticDeserializerBuilder(new StaticAOTContext())
.WithTypeConverter(new ParamsConverter())
.IgnoreUnmatchedProperties()
.Build();
@@ -93,7 +93,7 @@ public class YAMLParser : IMetadataParser
)
{
var frontMatter =
- yamlDeserializerRigid.Deserialize(
+ deserializer.Deserialize(
new StringReader(yaml)
) ?? throw new FormatException("Error parsing front matter");
var section = SiteHelper.GetSection(fileRelativePath);
@@ -108,7 +108,21 @@ public class YAMLParser : IMetadataParser
///
public SiteSettings ParseSiteSettings(string yaml)
{
- var settings = yamlDeserializerRigid.Deserialize(yaml);
+ var settings = deserializer.Deserialize(yaml);
return settings;
}
+
+ ///
+ public void Export(T data, string path)
+ {
+ var deserializer = new SerializerBuilder()
+ .IgnoreFields()
+ .ConfigureDefaultValuesHandling(
+ DefaultValuesHandling.OmitEmptyCollections
+ | DefaultValuesHandling.OmitDefaults
+ | DefaultValuesHandling.OmitNull)
+ .Build();
+ var dataString = deserializer.Serialize(data);
+ File.WriteAllText(path, dataString);
+ }
}
diff --git a/test/.TestSites/05-theme-no-baseof/sucos.yaml b/test/.TestSites/05-theme-no-baseof/sucos.yaml
index e0addbe03e7787863648b62fcd497fffe7c490ce..9f708e20badf8f657dcc1f365c54e19feb1503e1 100644
--- a/test/.TestSites/05-theme-no-baseof/sucos.yaml
+++ b/test/.TestSites/05-theme-no-baseof/sucos.yaml
@@ -1 +1,2 @@
-Title: test
\ No newline at end of file
+Title: test
+Theme: test
diff --git a/test/.TestSites/05-theme-no-baseof/theme/_default/index.liquid b/test/.TestSites/05-theme-no-baseof/themes/test/_default/index.liquid
similarity index 100%
rename from test/.TestSites/05-theme-no-baseof/theme/_default/index.liquid
rename to test/.TestSites/05-theme-no-baseof/themes/test/_default/index.liquid
diff --git a/test/.TestSites/05-theme-no-baseof/theme/_default/list.liquid b/test/.TestSites/05-theme-no-baseof/themes/test/_default/list.liquid
similarity index 100%
rename from test/.TestSites/05-theme-no-baseof/theme/_default/list.liquid
rename to test/.TestSites/05-theme-no-baseof/themes/test/_default/list.liquid
diff --git a/test/.TestSites/05-theme-no-baseof/theme/_default/single.liquid b/test/.TestSites/05-theme-no-baseof/themes/test/_default/single.liquid
similarity index 100%
rename from test/.TestSites/05-theme-no-baseof/theme/_default/single.liquid
rename to test/.TestSites/05-theme-no-baseof/themes/test/_default/single.liquid
diff --git a/test/.TestSites/06-theme/sucos.yaml b/test/.TestSites/06-theme/sucos.yaml
index e0addbe03e7787863648b62fcd497fffe7c490ce..9f708e20badf8f657dcc1f365c54e19feb1503e1 100644
--- a/test/.TestSites/06-theme/sucos.yaml
+++ b/test/.TestSites/06-theme/sucos.yaml
@@ -1 +1,2 @@
-Title: test
\ No newline at end of file
+Title: test
+Theme: test
diff --git a/test/.TestSites/06-theme/theme/_default/baseof.liquid b/test/.TestSites/06-theme/themes/test/_default/baseof.liquid
similarity index 100%
rename from test/.TestSites/06-theme/theme/_default/baseof.liquid
rename to test/.TestSites/06-theme/themes/test/_default/baseof.liquid
diff --git a/test/.TestSites/06-theme/theme/_default/index.liquid b/test/.TestSites/06-theme/themes/test/_default/index.liquid
similarity index 100%
rename from test/.TestSites/06-theme/theme/_default/index.liquid
rename to test/.TestSites/06-theme/themes/test/_default/index.liquid
diff --git a/test/.TestSites/06-theme/theme/_default/list.liquid b/test/.TestSites/06-theme/themes/test/_default/list.liquid
similarity index 100%
rename from test/.TestSites/06-theme/theme/_default/list.liquid
rename to test/.TestSites/06-theme/themes/test/_default/list.liquid
diff --git a/test/.TestSites/06-theme/theme/_default/single.liquid b/test/.TestSites/06-theme/themes/test/_default/single.liquid
similarity index 100%
rename from test/.TestSites/06-theme/theme/_default/single.liquid
rename to test/.TestSites/06-theme/themes/test/_default/single.liquid
diff --git a/test/.TestSites/07-theme-no-baseof-error/sucos.yaml b/test/.TestSites/07-theme-no-baseof-error/sucos.yaml
index e0addbe03e7787863648b62fcd497fffe7c490ce..9f708e20badf8f657dcc1f365c54e19feb1503e1 100644
--- a/test/.TestSites/07-theme-no-baseof-error/sucos.yaml
+++ b/test/.TestSites/07-theme-no-baseof-error/sucos.yaml
@@ -1 +1,2 @@
-Title: test
\ No newline at end of file
+Title: test
+Theme: test
diff --git a/test/.TestSites/07-theme-no-baseof-error/theme/_default/index.liquid b/test/.TestSites/07-theme-no-baseof-error/themes/test/_default/index.liquid
similarity index 100%
rename from test/.TestSites/07-theme-no-baseof-error/theme/_default/index.liquid
rename to test/.TestSites/07-theme-no-baseof-error/themes/test/_default/index.liquid
diff --git a/test/.TestSites/07-theme-no-baseof-error/theme/_default/list.liquid b/test/.TestSites/07-theme-no-baseof-error/themes/test/_default/list.liquid
similarity index 100%
rename from test/.TestSites/07-theme-no-baseof-error/theme/_default/list.liquid
rename to test/.TestSites/07-theme-no-baseof-error/themes/test/_default/list.liquid
diff --git a/test/.TestSites/07-theme-no-baseof-error/theme/_default/single.liquid b/test/.TestSites/07-theme-no-baseof-error/themes/test/_default/single.liquid
similarity index 100%
rename from test/.TestSites/07-theme-no-baseof-error/theme/_default/single.liquid
rename to test/.TestSites/07-theme-no-baseof-error/themes/test/_default/single.liquid
diff --git a/test/.TestSites/08-theme-html/sucos.yaml b/test/.TestSites/08-theme-html/sucos.yaml
index e0addbe03e7787863648b62fcd497fffe7c490ce..9f708e20badf8f657dcc1f365c54e19feb1503e1 100644
--- a/test/.TestSites/08-theme-html/sucos.yaml
+++ b/test/.TestSites/08-theme-html/sucos.yaml
@@ -1 +1,2 @@
-Title: test
\ No newline at end of file
+Title: test
+Theme: test
diff --git a/test/.TestSites/08-theme-html/theme/_default/baseof.liquid b/test/.TestSites/08-theme-html/themes/test/_default/baseof.liquid
similarity index 100%
rename from test/.TestSites/08-theme-html/theme/_default/baseof.liquid
rename to test/.TestSites/08-theme-html/themes/test/_default/baseof.liquid
diff --git a/test/.TestSites/08-theme-html/theme/_default/index.liquid b/test/.TestSites/08-theme-html/themes/test/_default/index.liquid
similarity index 100%
rename from test/.TestSites/08-theme-html/theme/_default/index.liquid
rename to test/.TestSites/08-theme-html/themes/test/_default/index.liquid
diff --git a/test/.TestSites/08-theme-html/theme/_default/list.liquid b/test/.TestSites/08-theme-html/themes/test/_default/list.liquid
similarity index 100%
rename from test/.TestSites/08-theme-html/theme/_default/list.liquid
rename to test/.TestSites/08-theme-html/themes/test/_default/list.liquid
diff --git a/test/.TestSites/08-theme-html/theme/_default/single.liquid b/test/.TestSites/08-theme-html/themes/test/_default/single.liquid
similarity index 100%
rename from test/.TestSites/08-theme-html/theme/_default/single.liquid
rename to test/.TestSites/08-theme-html/themes/test/_default/single.liquid
diff --git a/test/Models/SiteTests.cs b/test/Models/SiteTests.cs
index 123d3480c59a4b831a804045557ec41e7505c0a5..9207cb46ff53453cd52b72a3f65f5e2c15835c8f 100644
--- a/test/Models/SiteTests.cs
+++ b/test/Models/SiteTests.cs
@@ -1,3 +1,4 @@
+using SuCoS.Helpers;
using SuCoS.Models;
using SuCoS.Models.CommandLineOptions;
using Xunit;
@@ -46,7 +47,7 @@ public class SiteTests : TestSetup
// Assert
Assert.NotNull(site.Home);
Assert.True(site.Home.IsHome);
- _ = Assert.Single(site.OutputReferences.Values.Where(output => output is IPage page && page.IsHome));
+ _ = Assert.Single(site.OutputReferences.Values.Where(output => output is IPage page && page.IsHome));
}
[Theory]
@@ -154,8 +155,8 @@ public class SiteTests : TestSetup
// Act
site.ParseAndScanSourceFiles(null);
- // Assert
- _ = site.OutputReferences.TryGetValue("/tags", out var output);
+ // Assert
+ _ = site.OutputReferences.TryGetValue("/tags", out var output);
var tagSectionPage = output as IPage;
Assert.NotNull(tagSectionPage);
Assert.Equal(2, tagSectionPage.Pages.Count());
@@ -177,8 +178,8 @@ public class SiteTests : TestSetup
// Act
site.ParseAndScanSourceFiles(null);
- // Assert
- _ = site.OutputReferences.TryGetValue("/tags/tag1", out var output);
+ // Assert
+ _ = site.OutputReferences.TryGetValue("/tags/tag1", out var output);
var page = output as IPage;
Assert.NotNull(page);
Assert.Equal(10, page.Pages.Count());
@@ -202,8 +203,8 @@ public class SiteTests : TestSetup
// Act
site.ParseAndScanSourceFiles(null);
- // Assert
- _ = site.OutputReferences.TryGetValue(url, out var output);
+ // Assert
+ _ = site.OutputReferences.TryGetValue(url, out var output);
var page = output as IPage;
Assert.NotNull(page);
Assert.Equal(expectedContent, page.Content);
@@ -232,13 +233,15 @@ public class SiteTests : TestSetup
{
SourceArgument = Path.GetFullPath(Path.Combine(testSitesPath, testSitePathCONST05))
};
- site.Options = options;
+ var parser = new SuCoS.Parser.YAMLParser();
+ var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser);
+ site = new Site(options, siteSettings, parser, loggerMock, null);
// Act
site.ParseAndScanSourceFiles(null);
- // Assert
- _ = site.OutputReferences.TryGetValue(url, out var output);
+ // Assert
+ _ = site.OutputReferences.TryGetValue(url, out var output);
var page = output as IPage;
Assert.NotNull(page);
Assert.Equal(expectedContentPreRendered, page.ContentPreRendered);
@@ -258,13 +261,15 @@ public class SiteTests : TestSetup
{
SourceArgument = Path.GetFullPath(Path.Combine(testSitesPath, testSitePathCONST07))
};
- site.Options = options;
+ var parser = new SuCoS.Parser.YAMLParser();
+ var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser);
+ site = new Site(options, siteSettings, parser, loggerMock, null);
// Act
site.ParseAndScanSourceFiles(null);
- // Assert
- _ = site.OutputReferences.TryGetValue(url, out var output);
+ // Assert
+ _ = site.OutputReferences.TryGetValue(url, out var output);
var page = output as IPage;
Assert.NotNull(page);
Assert.Equal(string.Empty, page.Content);
@@ -298,13 +303,15 @@ public class SiteTests : TestSetup
{
SourceArgument = Path.GetFullPath(Path.Combine(testSitesPath, testSitePathCONST06))
};
- site.Options = options;
+ var parser = new SuCoS.Parser.YAMLParser();
+ var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser);
+ site = new Site(options, siteSettings, parser, loggerMock, null);
// Act
site.ParseAndScanSourceFiles(null);
- // Assert
- _ = site.OutputReferences.TryGetValue(url, out var output);
+ // Assert
+ _ = site.OutputReferences.TryGetValue(url, out var output);
var page = output as IPage;
Assert.NotNull(page);
Assert.Equal(expectedContentPreRendered, page.ContentPreRendered);
diff --git a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs
index e1b4fc83119d2561b8d36ed1b65938c19c644b01..267153e71fd3ebf1fb26df6d266587b868fd15f5 100644
--- a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs
+++ b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs
@@ -1,4 +1,6 @@
using NSubstitute;
+using SuCoS.Helpers;
+using SuCoS.Models;
using SuCoS.Models.CommandLineOptions;
using SuCoS.ServerHandlers;
using Xunit;
@@ -34,10 +36,14 @@ public class RegisteredPageRequestHandlerTests : TestSetup
{
// Arrange
var siteFullPath = Path.GetFullPath(Path.Combine(testSitesPath, testSitePath));
- site.Options = new GenerateOptions
+ GenerateOptions options = new()
{
SourceArgument = siteFullPath
};
+ var parser = new SuCoS.Parser.YAMLParser();
+ var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser);
+ site = new Site(options, siteSettings, parser, loggerMock, null);
+
var registeredPageRequest = new RegisteredPageRequest(site);
var response = Substitute.For();