diff --git a/Program.cs b/Program.cs
index edfe1f8..447b581 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,223 +1,317 @@
-/*
- * csSiteGen - A static site generator written in c#
- * Copyright © 2022 Robert Morrison
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using Serilog.Events;
-using Serilog;
-using System.CommandLine.Builder;
-using System.CommandLine.Help;
-using System.CommandLine.Parsing;
-using System.CommandLine;
-using System.Diagnostics;
-using System.Reflection;
-using Spectre.Console;
-
-namespace csSiteGen;
-
-class Program
-{
-
- static int Main(string[] args)
- {
-
- // Get the current versiion number
- string? version = Assembly.GetEntryAssembly()?.GetCustomAttribute()?.InformationalVersion;
-
- // Configure logger
- Log.Logger = new LoggerConfiguration()
- .MinimumLevel.Debug()
- .WriteTo.File("log.log")
- .CreateLogger();
-
- Log.Information("Starting New Instance of csSiteGen");
-
- if (version is not null)
- {
- Log.Information("Version: {ver}", version);
- }
- else
- {
- Log.Warning("Cannot get Version Information");
- }
-
- // It is very likely that this program will only work on linux. As such it is worth warning the user about this.
- if (!OperatingSystem.IsLinux())
- {
- Log.Warning("This program has only been tested on linux and cannot be assumed to work on other Operating Systems");
- // AnsiConsole.MarkupLine("[[[yellow]Warning[/]]] This program is only tested on linux systems, it may not work on this Operating System.");
- }
-
- Stopwatch TotalExecutionTime = Stopwatch.StartNew();
-
- var inputDirectoryOption = new Option(
- name: "--input",
- description: "The directory that contains the site source.");
- inputDirectoryOption.IsRequired = true;
- inputDirectoryOption.AddValidator(result =>
- {
- if (!result.GetValueForOption(inputDirectoryOption)!.Exists)
- {
- result.ErrorMessage = $"Input directory {result.GetValueForOption(inputDirectoryOption)!.FullName} does not exist";
- }
- });
-
- var outputDirectoryOption = new Option(
- name: "--output",
- description: "The directory that the site should be output to.");
- outputDirectoryOption.IsRequired = true;
-
- var rootCommand = new RootCommand("csSiteGen");
-
- var cleanCommand = new Command("clean", "Clean the output directory");
- cleanCommand.AddOption(outputDirectoryOption);
- cleanCommand.SetHandler(async (directory) =>
- {
- await Task.Run(() =>
- {
- Clean(directory);
- });
- } ,outputDirectoryOption);
-
- var convertCommand = new Command("convert", "Convert the input directory and place the files in the output directory.");
- convertCommand.AddOption(inputDirectoryOption);
- convertCommand.AddOption(outputDirectoryOption);
- convertCommand.SetHandler(async (inputDir, outputDir) =>
- {
- await Task.Run(() =>
- {
- Convert(inputDir, outputDir);
- });
- }, inputDirectoryOption, outputDirectoryOption);
-
- rootCommand.AddCommand(cleanCommand);
- rootCommand.AddCommand(convertCommand);
-
- var parser = new CommandLineBuilder(rootCommand)
- .UseDefaults()
- .Build();
-
- parser.Invoke(args);
-
-
- TotalExecutionTime.Stop();
- Log.Information("TotalExecutionTime {time:000}ms", TotalExecutionTime.ElapsedMilliseconds);
-
- Log.CloseAndFlush();
- return 0;
- }
-
- static int Convert(DirectoryInfo inputDir, DirectoryInfo outputDir)
- {
- AnsiConsole.Console.Profile.Capabilities.Ansi = true;
-
- List siteFiles = new();
-
- Utils.GetFiles(inputDir).ForEach(x => siteFiles.Add(new SiteFile(x)));
- Log.Information("SiteFiles: {@sf} {count}", siteFiles, siteFiles.Count);
-
- Console.WriteLine($"Converting {siteFiles.Count} files from {inputDir.FullName} to {outputDir.FullName}");
- RuntimeSettings settings = new(inputDir,outputDir);
-
-
- Dictionary fileStatus = new();
- Log.Debug("fileStatus {@fileStatus}",fileStatus);
-
- AnsiConsole.Progress()
- .AutoRefresh(true)
- .Columns(new ProgressColumn[]
- {
- new TaskDescriptionColumn(),
- new ProgressBarColumn(),
- new PercentageColumn(),
- new RemainingTimeColumn(),
- new SpinnerColumn()
- })
- .Start(ctx =>
- {
- var tasks = siteFiles.Select(x => ctx.AddTask($"Converting {x.Name}")).ToList();
- var overallTask = ctx.AddTask("[bold]Converting Files[/]");
- overallTask.MaxValue = siteFiles.Count();
-
- for (int i = 0; i < siteFiles.Count; i++)
- {
- tasks[i].MaxValue = 1;
- Log.Information("Converting file {name} {i}/{count}",siteFiles[i].Name,i+1,siteFiles.Count());
- bool res = siteFiles[i].Convert(settings);
- tasks[i].Increment(1);
- tasks[i].StopTask();
- if (!res)
- {
- Log.Warning("{name} Failed...",siteFiles[i].FullName);
- tasks[i].Description += " [red]FAILED[/]";
- }
- Log.Information("adding {@siteFile} conversion status to fileStatus", siteFiles[i]);
- Log.Debug("FileStatus {@fileStatus}",fileStatus);
- fileStatus.Add(siteFiles[i].FullName,res);
- overallTask.Increment(1);
- }
- });
-
- Log.Information("Conversion Status {@status}", fileStatus);
-
- var Failed = fileStatus.Where(x => x.Value == false);
- if ( Failed.Count() > 0)
- {
- foreach (var fail in Failed)
- {
- AnsiConsole.MarkupLineInterpolated(
- $"[red]File [blue]\"{fail.Key}\"[/] failed to convert[/]");
- AnsiConsole.MarkupLine("[yellow]See log for more details[/]");
- }
- }
-
- return 0;
- }
-
- static int Clean(DirectoryInfo outputDir)
- {
- if (!outputDir.Exists)
- {
- Log.Warning("Not deleting {dir} as it doesn't exist",outputDir.FullName);
- AnsiConsole.MarkupLineInterpolated($"[bold][[[yellow]Warning[/]]][/] Not cleaning [blue]\"{outputDir}\"[/] as it does not exist.");
- return 0; // success because it doesn't exist.
- }
- try
- {
- Log.Information("Cleaning {dir}",outputDir.FullName);
- AnsiConsole.MarkupInterpolated($"Cleaning [blue]\"{outputDir.FullName}\"[/]");
- outputDir.Delete(recursive: true);
- outputDir.Create();
- AnsiConsole.MarkupLine(" [bold][[[green]OK[/]]][/]");
- AnsiConsole.MarkupLineInterpolated($"\t[grey]>>[/] [green]All files in {outputDir.FullName} purged successfully[/]");
- return 0;
- }
- catch (System.Security.SecurityException e)
- {
- AnsiConsole.MarkupLine(" [bold][[[red]Fail[/]]][/]");
- AnsiConsole.MarkupLine("[orangered1]See log for more details about what went wrong.[/]");
- Log.Error(e, "Failed to delete directory {dir} due to permission error.", outputDir.FullName);
- return 1;
- }
- catch (Exception e)
- {
- AnsiConsole.MarkupLine(" [red][[[bold]Fail[/]]][/]");
- AnsiConsole.MarkupLine("[orangered1]See log for more details about what went wrong.[/]");
- Log.Error(e, "Failed to delete/create directory {dir}", outputDir.FullName);
- return 1;
- }
- }
-}
+/*
+ * csSiteGen - A static site generator written in c#
+ * Copyright © 2022 Robert Morrison
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using Serilog.Events;
+using Serilog;
+using System.CommandLine.Builder;
+using System.CommandLine.Help;
+using System.CommandLine.Parsing;
+using System.CommandLine;
+using System.Diagnostics;
+using System.Reflection;
+using Spectre.Console;
+using System.Text.Json;
+
+namespace csSiteGen;
+
+class Program
+{
+
+ static int Main(string[] args)
+ {
+
+ // Get the current version number
+ string? version = Assembly.GetEntryAssembly()?.GetCustomAttribute()?.InformationalVersion;
+
+ // Only log to file
+ // Logging to the console is BAD practice as it tends to be messy.
+ // TODO: Log to a known location using environment to find the correct location.
+ Log.Logger = new LoggerConfiguration()
+ .MinimumLevel.Debug()
+ .WriteTo.File("log.log")
+ .CreateLogger();
+
+ Log.Information("Starting New Instance of csSiteGen");
+
+ if (version is not null)
+ {
+ Log.Information("Version: {ver}", version);
+ }
+ else
+ {
+ Log.Warning("Cannot get Version Information");
+ }
+
+ // It is very likely that this program will only work on linux. As such it is worth warning the user about this.
+ // With further testing and handling of any edge cases it _may_ be possible to have this work anywhere.
+ if (!OperatingSystem.IsLinux())
+ {
+ Log.Warning("This program has only been tested on linux and cannot be assumed to work on other Operating Systems");
+ // AnsiConsole.MarkupLine("[[[yellow]Warning[/]]] This program is only tested on linux systems, it may not work on this Operating System.");
+ }
+
+ Stopwatch TotalExecutionTime = Stopwatch.StartNew();
+
+ /* !! IMPORTANT !!
+ WARN:
+ This code uses system.commandline which is still in pre-release
+ the following section of code will contain comments to explain the intent of the programmer
+ which may be useful if system.commandline has breaking changes
+ */
+
+
+ // First the option for the project directory is created
+ var ProjectDirectoryOption = new Option(
+ name: "--project",
+ description: "The Directory for the project");
+ ProjectDirectoryOption.IsRequired = false; // it is not required as not providing it infers that the current directory is the project directory
+ // If the option is used then the input is validated before control passes to any of the actual code.
+ ProjectDirectoryOption.AddValidator(result =>
+ {
+ if (!result.GetValueForOption(ProjectDirectoryOption)!.Exists)
+ {
+ result.ErrorMessage = $"Project directory {result.GetValueForOption(ProjectDirectoryOption)} does not exist.";
+ }
+ }
+ );
+
+ // The root command is the entry point for commandline but otherwise does nothing.
+ var rootCommand = new RootCommand("csSiteGen");
+
+
+ // TODO: Verify if the use of async in these functions is necessary
+
+ // This creates the command for cleaning a projects output directory.
+ var cleanCommand = new Command("clean", "Clean the projects output directory");
+ cleanCommand.AddOption(ProjectDirectoryOption); // This command can use the project directory option we created earlier
+ cleanCommand.SetHandler(async (ProjectDirectory) =>
+ {
+ await Task.Run(() =>
+ {
+ Clean(ProjectDirectory);
+ });
+ },ProjectDirectoryOption);
+
+ // This creates the command for actually converting the project.
+ var convertCommand = new Command("convert", "Convert the projects input directory and place the files in the output directory.");
+ convertCommand.AddOption(ProjectDirectoryOption); // This command can use the project directory option
+ convertCommand.SetHandler(async (ProjectDirectory) =>
+ {
+ await Task.Run(() =>
+ {
+ Convert(ProjectDirectory);
+ });
+ },ProjectDirectoryOption);
+
+ // Adding the commands to the root command makes them actually callable on the commandline
+ rootCommand.AddCommand(cleanCommand);
+ rootCommand.AddCommand(convertCommand);
+
+ // The parser is what actually handles the arguments and dispatches them to the appropriate commands.
+ // This is used instead of the simpler method of just Invoking the root command as it automatically creates usage statements.
+ // It also makes a user aware that a subcommand needs to be used.
+ var parser = new CommandLineBuilder(rootCommand)
+ .UseDefaults()
+ .Build();
+
+ parser.Invoke(args);
+
+
+ TotalExecutionTime.Stop();
+ Log.Information("TotalExecutionTime {time:000}ms", TotalExecutionTime.ElapsedMilliseconds);
+
+ // Providing there were no early exits it is best to properly close the log before we exit.
+ Log.CloseAndFlush();
+ return 0;
+ }
+
+ static int Convert(DirectoryInfo? ProjectDirectory)
+ {
+ Log.Information("Convert command was called, beginning conversion.");
+
+ // WARN: This is only temporary as Spectre.Console does not recognise some Linux terminals
+ // A better solution that checks the terminal value and sets this option should be added in the future.
+ AnsiConsole.Console.Profile.Capabilities.Ansi = true;
+
+
+ // NOTE: Future refactors may merge ProjectSettings and RuntimeSettings
+ ProjectSettings projectSettings = GetProjectSettings(ProjectDirectory);
+
+ DirectoryInfo inputDir = new(projectSettings.Source);
+ DirectoryInfo outputDir = new(projectSettings.Destination);
+
+ RuntimeSettings settings = new(inputDir,outputDir);
+ settings.setBaseUrl(projectSettings.BaseUrl);
+
+ List siteFiles = new();
+
+ Utils.GetFiles(inputDir).ForEach(x => siteFiles.Add(new SiteFile(x)));
+ Log.Information("SiteFiles: {@sf} {count}", siteFiles, siteFiles.Count);
+
+ Console.WriteLine($"Converting {siteFiles.Count} files from {inputDir.FullName} to {outputDir.FullName}");
+
+
+ Dictionary fileStatus = new();
+ Log.Debug("fileStatus {@fileStatus}",fileStatus);
+
+ AnsiConsole.Progress()
+ .AutoRefresh(true)
+ .Columns(new ProgressColumn[]
+ {
+ new TaskDescriptionColumn(),
+ new ProgressBarColumn(),
+ new PercentageColumn(),
+ new RemainingTimeColumn(),
+ new SpinnerColumn()
+ })
+ .Start(ctx =>
+ {
+ var tasks = siteFiles.Select(x => ctx.AddTask($"Converting {x.Name}")).ToList();
+ var overallTask = ctx.AddTask("[bold]Converting Files[/]");
+ overallTask.MaxValue = siteFiles.Count();
+
+ for (int i = 0; i < siteFiles.Count; i++)
+ {
+ tasks[i].MaxValue = 1;
+ Log.Information("Converting file {name} {i}/{count}",siteFiles[i].Name,i+1,siteFiles.Count());
+ bool res = siteFiles[i].Convert(settings);
+ tasks[i].Increment(1);
+ tasks[i].StopTask();
+ if (!res)
+ {
+ Log.Warning("{name} Failed...",siteFiles[i].FullName);
+ tasks[i].Description += " [red]FAILED[/]";
+ }
+ Log.Information("adding {@siteFile} conversion status to fileStatus", siteFiles[i]);
+ Log.Debug("FileStatus {@fileStatus}",fileStatus);
+ fileStatus.Add(siteFiles[i].FullName,res);
+ overallTask.Increment(1);
+ }
+ });
+
+ Log.Information("Conversion Status {@status}", fileStatus);
+
+ var Failed = fileStatus.Where(x => x.Value == false);
+ if ( Failed.Count() > 0)
+ {
+ foreach (var fail in Failed)
+ {
+ AnsiConsole.MarkupLineInterpolated(
+ $"[red]File [blue]\"{fail.Key}\"[/] failed to convert[/]");
+ AnsiConsole.MarkupLine("[yellow]See log for more details[/]");
+ }
+ }
+
+ return 0;
+ }
+
+ static int Clean(DirectoryInfo? ProjectDirectory)
+ {
+ Log.Information("Clean command was called, Beginning cleaning");
+
+ // NOTE: Future refactors may merge ProjectSettings and RuntimeSettings
+ ProjectSettings projectSettings = GetProjectSettings(ProjectDirectory);
+
+ DirectoryInfo inputDir = new(projectSettings.Source);
+ DirectoryInfo outputDir = new(projectSettings.Destination);
+
+ if (!outputDir.Exists)
+ {
+ Log.Warning("Not deleting {dir} as it doesn't exist",outputDir.FullName);
+ AnsiConsole.MarkupLineInterpolated($"[bold][[[yellow]Warning[/]]][/] Not cleaning [blue]\"{outputDir}\"[/] as it does not exist.");
+ return 0; // success because it doesn't exist.
+ }
+ try
+ {
+ Log.Information("Cleaning {dir}",outputDir.FullName);
+ AnsiConsole.MarkupInterpolated($"Cleaning [blue]\"{outputDir.FullName}\"[/]");
+ outputDir.Delete(recursive: true);
+ outputDir.Create();
+ AnsiConsole.MarkupLine(" [bold][[[green]OK[/]]][/]");
+ AnsiConsole.MarkupLineInterpolated($"\t[grey]>>[/] [green]All files in {outputDir.FullName} purged successfully[/]");
+ return 0;
+ }
+ catch (System.Security.SecurityException e)
+ {
+ AnsiConsole.MarkupLine(" [bold][[[red]Fail[/]]][/]");
+ AnsiConsole.MarkupLine("[orangered1]See log for more details about what went wrong.[/]");
+ Log.Error(e, "Failed to delete directory {dir} due to permission error.", outputDir.FullName);
+ return 1;
+ }
+ catch (Exception e)
+ {
+ AnsiConsole.MarkupLine(" [red][[[bold]Fail[/]]][/]");
+ AnsiConsole.MarkupLine("[orangered1]See log for more details about what went wrong.[/]");
+ Log.Error(e, "Failed to delete/create directory {dir}", outputDir.FullName);
+ return 1;
+ }
+ }
+
+ static void EnforceConsistency(ProjectSettings projectSettings)
+ {
+ // Grab the metadata.
+
+ // Read the metadata.
+
+ // Find deleted files.
+
+ // Figure out what the new name for those files would be.
+
+ // Remove those files.
+
+ }
+
+ static ProjectSettings GetProjectSettings(DirectoryInfo? ProjectDirectory)
+ {
+ // TODO: implement proper error handling where file access is performed.
+
+ if (ProjectDirectory is null)
+ {
+ // use the current directory if no project directory is passed.
+ ProjectDirectory = new DirectoryInfo(".");
+ }
+ Log.Information("{projectdir} => fullname {pdfn}",ProjectDirectory, ProjectDirectory.FullName);
+ FileInfo projectFile = new (Path.Combine(ProjectDirectory.FullName,"cssitegen.json"));
+
+
+ if (!projectFile.Exists)
+ {
+ Log.Fatal("Cannot locate project file {pf} in {dir}",projectFile,ProjectDirectory);
+ Environment.Exit(1);
+ }
+
+ Log.Information("Located Project File {pf}",projectFile.FullName);
+ ProjectSettings? projectSettings = JsonSerializer
+ .Deserialize(
+ projectFile
+ .OpenText()
+ .ReadToEnd()
+ );
+
+ if (projectSettings is null)
+ {
+ Log.Fatal("Cannot deserialize projectFile");
+ Environment.Exit(1);
+ }
+ projectSettings.setProjectRoot(ProjectDirectory);
+
+ Log.Information("{@ps}",projectSettings);
+
+ return projectSettings;
+ }
+}
diff --git a/ProjectSettings/ProjectSettings.cs b/ProjectSettings/ProjectSettings.cs
new file mode 100644
index 0000000..5c2e412
--- /dev/null
+++ b/ProjectSettings/ProjectSettings.cs
@@ -0,0 +1,44 @@
+using System.Text.Json.Serialization;
+// project settings is a user accessible config to set the Source and destination of a site
+// This may include more scope in the future such as holding the site base address etc..
+
+class ProjectSettings
+{
+ private string _Source;
+ private string _Destination;
+ private DirectoryInfo? _ProjectRoot;
+ public string? BaseUrl {get; private set;}
+
+ public string Source {get {
+ if (_ProjectRoot is null)
+ {
+ return _Source;
+ }
+ return Path.Combine(_ProjectRoot.FullName,_Source);
+ }}
+ public string Destination {get {
+ if (_ProjectRoot is null)
+ {
+ return _Destination;
+ }
+ return Path.Combine(_ProjectRoot.FullName,_Destination);
+ }}
+
+ [JsonConstructor]
+ public ProjectSettings(String source, String destination, string baseUrl) {
+ _Source = source;
+ _Destination = destination;
+ BaseUrl = baseUrl;
+ }
+
+ public void setProjectRoot(string projectRoot) {
+ _ProjectRoot = new(projectRoot);
+ }
+ public void setProjectRoot(DirectoryInfo projectRoot) {
+ _ProjectRoot = projectRoot;
+ }
+
+ public void setBaseUrl(string baseUrl) {
+ BaseUrl = baseUrl;
+ }
+}
diff --git a/RuntimeSettings/RuntimeSettings.cs b/RuntimeSettings/RuntimeSettings.cs
index 92aff3e..8c7e627 100644
--- a/RuntimeSettings/RuntimeSettings.cs
+++ b/RuntimeSettings/RuntimeSettings.cs
@@ -2,11 +2,12 @@ namespace csSiteGen;
///
-/// Class RuntimeSettings
Contains all the settings that could be loaded from the commandline.
+/// Class RuntimeSettings Contains all the settings that could be loaded from the commandline.
///
public class RuntimeSettings {
public DirectoryInfo InputDirectory {get; private set;}
public DirectoryInfo OutputDirectory {get; private set;}
+ public string? BaseUrl {get; private set;}
public RuntimeSettings(string inputDirectory, string outputDirectory){
@@ -26,4 +27,7 @@ public class RuntimeSettings {
*/
}
+ public void setBaseUrl(string? baseurl) {
+ BaseUrl = baseurl;
+ }
}
diff --git a/SiteFile/SiteFile.ConverterFunctions.cs b/SiteFile/SiteFile.ConverterFunctions.cs
index d15a414..0710b93 100644
--- a/SiteFile/SiteFile.ConverterFunctions.cs
+++ b/SiteFile/SiteFile.ConverterFunctions.cs
@@ -15,6 +15,11 @@ public static class Conversions{
{".md", Pandoc},
};
+ private static readonly string[] BaseUrlFiletypes = {
+ ".md",
+ ".html"
+ };
+
///
/// TEST FUNCTION.
@@ -28,7 +33,7 @@ public static class Conversions{
}
///
- /// Copy the file verbatim
+ /// Copy the file verbatim (doing any baseurl replacements if needed)
///
public static bool RawCpy(FileInfo file, RuntimeSettings settings){
FileInfo newPath = new FileInfo(GetNewName(file,settings,null));
@@ -41,7 +46,14 @@ public static class Conversions{
}
try {
- file.CopyTo(newPath.FullName, overwrite: true);
+ if (BaseUrlFiletypes.Contains(file.Extension))
+ {
+ File.WriteAllText(newPath.FullName, BaseUrlReplace(file, settings));
+ }
+ else
+ {
+ file.CopyTo(newPath.FullName, overwrite: true);
+ }
}
catch (Exception e){
Log.Fatal(e,"Copy Failed");
@@ -54,6 +66,11 @@ public static class Conversions{
/// Execute pandoc on the file, automatically detecting the template to use.
///
public static bool Pandoc(FileInfo file, RuntimeSettings settings){
+ // NOTE: Some of the code later where the tmpfile is created for baseurl replacement may be too safe.
+ // the extension checks may be unnecessary, but this depends on if this function will be retooled to run pandoc for different conversions.
+ // for now I have take the safer approach, but the leaner approach may be used in the future when the project is more mature
+
+
Log.Information("Attempting to convert {file} using pandoc",file.Name);
// Look for pandoc
@@ -91,7 +108,37 @@ public static class Conversions{
searchDir = searchDir.Parent;
} while (searchDir != settings.InputDirectory); // Check last as we want to search the InputDirectory
- string pandocArgs = $"{file.FullName} -o {GetNewName(file,settings,".html")}";
+ // the empty string is used as it has a defined identity
+ string tmpFile = string.Empty;
+ if (BaseUrlFiletypes.Contains(file.Extension))
+ {
+ Log.Information("Replacing baseurl for file {f}",file.FullName);
+ tmpFile = Path.Join(Path.GetTempPath(),"pandoc",file.Name);
+ Directory.CreateDirectory(Path.GetDirectoryName(tmpFile)!); // NOTE: It is practially impossible that this would actually return null
+ File.Create(tmpFile).Close(); // TODO: Use the filestream provided by File.Create within a using block to write the text
+ File.WriteAllText(tmpFile,BaseUrlReplace(file,settings));
+
+ if (template is not null)
+ {
+ Log.Information("Replacing baseurl in template file");
+ string tmpTemplateFile = Path.Join(Path.GetTempPath(),"pandoc",template.Name);
+ Directory.CreateDirectory(Path.GetDirectoryName(tmpTemplateFile)!); // NOTE: It is practially impossible that this would actually return null
+ File.Create(tmpTemplateFile).Close(); // TODO: Use the filestream provided by File.Create within a using block to write the text
+ File.WriteAllText(tmpTemplateFile,BaseUrlReplace(template,settings));
+ template = new(tmpTemplateFile);
+ }
+ }
+
+ string pandocArgs;
+ // If we have created a temporary file we need to ensure that we use it.
+ if (!string.IsNullOrEmpty(tmpFile))
+ {
+ pandocArgs = $"{tmpFile} -o {GetNewName(file,settings,".html")}";
+ }
+ else
+ {
+ pandocArgs = $"{file.FullName} -o {GetNewName(file,settings,".html")}";
+ }
if (template is not null)
{
@@ -103,7 +150,18 @@ public static class Conversions{
Log.Warning("Pandoc template for {file} not found",file.Name);
}
- return RunExternalProgram(pandoc,pandocArgs);
+ if (!Directory.Exists(Path.GetDirectoryName(GetNewName(file,settings,".html"))))
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(GetNewName(file,settings,".html"))!);
+ }
+
+ bool pandocReturn = RunExternalProgram(pandoc,pandocArgs);
+ // If we made a tmpfile delete it after running pandoc against it.
+ if (!string.IsNullOrEmpty(tmpFile))
+ {
+ File.Delete(tmpFile);
+ }
+ return pandocReturn;
}
@@ -138,8 +196,8 @@ public static class Conversions{
RunProgram.WaitForExit();
- Log.Debug("{program} stdout {stdout}", program, stdout);
- Log.Debug("{program} stderr {stderr}", program, stderr);
+ Log.Debug("{program} STDOUT:\n{stdout}", program, stdout);
+ Log.Debug("{program} STDERR:\n{stderr}", program, stderr);
if (RunProgram.ExitCode != 0)
{
@@ -155,4 +213,23 @@ public static class Conversions{
.Replace(settings.InputDirectory.FullName, settings.OutputDirectory.FullName)
.Replace(file.Extension,newExtension ?? file.Extension);
}
+
+ private static string? BaseUrlReplace(FileInfo file, RuntimeSettings settings){
+ Log.Information("Doing BaseUrlReplace for {f}", file.FullName);
+ // Read the file
+ using (StreamReader FileReader = file.OpenText())
+ {
+ string filestring = FileReader.ReadToEnd();
+
+ if (settings.BaseUrl is null)
+ {
+ Log.Warning("BaseUrl is null, replacing templateString with nothing.");
+ return filestring.Replace("%BASEURL%","");
+ }
+
+ Log.Information("Replacing templateString with {BaseUrl}",settings.BaseUrl);
+ return filestring.Replace("%BASEURL%",settings.BaseUrl);
+ }
+ }
+
}
diff --git a/SiteFile/SiteFile.cs b/SiteFile/SiteFile.cs
index 0142c57..933d945 100644
--- a/SiteFile/SiteFile.cs
+++ b/SiteFile/SiteFile.cs
@@ -74,7 +74,11 @@ public partial class SiteFile
// NOTE: By this point Metadata CANNOT be null as LoadMetadata would either have loaded the JSON or instantiated blank MetaData
// The compiler however is unaware of this as it uses side effects, so we tell it there is no possible null value here.
- return (Metadata!.GetValueOrDefault(info.FullName, DateTime.MinValue) == info.LastWriteTimeUtc);
+ Log.Debug("Attempting to check metadata for file {f}",info.FullName);
+ Log.Debug("METADATA={MD}",Metadata!.GetValueOrDefault(info.FullName,DateTime.MinValue));
+ Log.Debug("FILETIME={FT}",info.LastWriteTimeUtc);
+ Log.Debug("RES={res}",(Metadata!.GetValueOrDefault(info.FullName, DateTime.MinValue) != info.LastWriteTimeUtc));
+ return (Metadata!.GetValueOrDefault(info.FullName, DateTime.MinValue) != info.LastWriteTimeUtc);
}
diff --git a/TODO b/TODO
index 01c937f..ad9dcb2 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,9 @@
+- UPDATE README
+
+- Make the code such that the metadata file knows what source file an output file came from, this will allow us to delete files from source
+ and force consistency by removing them from the dst directory too.
+
- Add a pre-commit or other type of hook to ensure that the Testing directory is properly cleaned and reset before commit.
-- Double check that links are processed correctly by testing against a copy of my blog source.
- - Also add the appropriate metadata to the markdown files to make sure that they are properly detected.
- - Potentially host a local Test version to fully ensure that everything can be made to work properly.
+ - Ensure that the .gitkeep file is placed into Testing/dst
+
- Look into the possiblity of using a TemplateTemplate, And generating the pandoc template on the fly making it possible to switch out domian/prefix stuff
diff --git a/Testing/cssitegen.json b/Testing/cssitegen.json
new file mode 100644
index 0000000..fd0fa32
--- /dev/null
+++ b/Testing/cssitegen.json
@@ -0,0 +1,5 @@
+{
+ "Source" : "./src",
+ "Destination" : "./dst",
+ "BaseUrl" : null
+}
diff --git a/Utils/Utils.GetFiles.cs b/Utils/Utils.GetFiles.cs
index a1ee635..4838cec 100644
--- a/Utils/Utils.GetFiles.cs
+++ b/Utils/Utils.GetFiles.cs
@@ -4,6 +4,6 @@ public static partial class Utils {
// Abstract Directory.GetFiles to get a List as it will be easier to handle later.
public static List GetFiles(DirectoryInfo dir){
- return dir.GetFiles("*",SearchOption.AllDirectories).ToList();
+ return dir.GetFiles("*",SearchOption.AllDirectories).Where(x => x.Name != ".template").ToList();
}
}
diff --git a/Utils/Utils.PathSearch.cs b/Utils/Utils.PathSearch.cs
index fdb687b..d28c910 100644
--- a/Utils/Utils.PathSearch.cs
+++ b/Utils/Utils.PathSearch.cs
@@ -3,6 +3,8 @@ namespace csSiteGen;
public static partial class Utils {
+ // As the PathSearch utility will be called for every convertible file
+ // It has been memoized which mean subsequent calls for the same argument just return the result
static Dictionary PathSearchMemo = new();
///
diff --git a/csSiteGen.csproj b/csSiteGen.csproj
index a142bd7..9751fb6 100644
--- a/csSiteGen.csproj
+++ b/csSiteGen.csproj
@@ -4,7 +4,7 @@
Exe
$(RELEASE_VERSION)
- 0.0.1
+ 0.0.2
$([System.DateTime]::UtcNow.ToString(`yyyyMMdd-HHmm`))