From 167a146f40361a46c289c119763a3488673e224d Mon Sep 17 00:00:00 2001 From: Sakura Akeno Isayeki Date: Sat, 5 Aug 2023 13:13:08 +0200 Subject: [PATCH 1/4] refactor(manifest): refactor VaultManifestGenerator - Added a project reference to the manifest project in the csproj file. - Refactored VaultManifestGenerator to use IVault instead of FileSystemVault. - Updated GenerateManifestAsync method to handle different versions of .NET. - Removed unused namespaces from VaultManifestGenerator. - Removed unused namespace from GenerateManifestCommand. --- .../Nodsoft.MoltenObsidian.Manifest.csproj | 4 +++ .../VaultManifestGenerator.cs | 26 ++++++++++++++----- .../Manifest/GenerateManifestCommand.cs | 1 - 3 files changed, 24 insertions(+), 7 deletions(-) rename {Nodsoft.MoltenObsidian.Tool/Services => Nodsoft.MoltenObsidian.Manifest}/VaultManifestGenerator.cs (74%) diff --git a/Nodsoft.MoltenObsidian.Manifest/Nodsoft.MoltenObsidian.Manifest.csproj b/Nodsoft.MoltenObsidian.Manifest/Nodsoft.MoltenObsidian.Manifest.csproj index b6bcd46..e602220 100644 --- a/Nodsoft.MoltenObsidian.Manifest/Nodsoft.MoltenObsidian.Manifest.csproj +++ b/Nodsoft.MoltenObsidian.Manifest/Nodsoft.MoltenObsidian.Manifest.csproj @@ -16,4 +16,8 @@ + + + + diff --git a/Nodsoft.MoltenObsidian.Tool/Services/VaultManifestGenerator.cs b/Nodsoft.MoltenObsidian.Manifest/VaultManifestGenerator.cs similarity index 74% rename from Nodsoft.MoltenObsidian.Tool/Services/VaultManifestGenerator.cs rename to Nodsoft.MoltenObsidian.Manifest/VaultManifestGenerator.cs index 089c255..a3f730c 100644 --- a/Nodsoft.MoltenObsidian.Tool/Services/VaultManifestGenerator.cs +++ b/Nodsoft.MoltenObsidian.Manifest/VaultManifestGenerator.cs @@ -1,9 +1,7 @@ using System.Security.Cryptography; using Nodsoft.MoltenObsidian.Vault; -using Nodsoft.MoltenObsidian.Vaults.FileSystem; -using Nodsoft.MoltenObsidian.Manifest; -namespace Nodsoft.MoltenObsidian.Tool.Services; +namespace Nodsoft.MoltenObsidian.Manifest; /// /// Provides manifest generation for a filesystem-based Molten Obsidian vault. @@ -11,11 +9,11 @@ namespace Nodsoft.MoltenObsidian.Tool.Services; public static class VaultManifestGenerator { /// - /// Generates a manifest for the specified filesystem vault. + /// Generates a manifest for the specified vault. /// /// The path to the vault. /// A instance. - public static async Task GenerateManifestAsync(FileSystemVault vault) + public static async Task GenerateManifestAsync(IVault vault) { List files = new(); @@ -46,11 +44,27 @@ private static async Task ExtractManifestAsync(IVaultFile file) // Read the file. We'll need it later. await using Stream stream = await file.OpenReadAsync(); +#if !NET7_0_OR_GREATER + byte[] fileBytes = new byte[stream.Length]; + int readBytesCount = await stream.ReadAsync(fileBytes); + + if (readBytesCount != stream.Length) + { + throw new InvalidOperationException("The file could not be read."); + } +#endif + // Build a new file object return new() { Path = file.Path, - Hash = Convert.ToBase64String(await SHA256.HashDataAsync(stream)), // Create a SHA256 hash of the file + +#if NET7_0_OR_GREATER + Hash = Convert.ToBase64String(await SHA256.HashDataAsync(stream)), // Create a SHA256 hash of the file +#else + Hash = Convert.ToBase64String(SHA256.HashData(fileBytes)), // Create a SHA256 hash of the file +#endif + Size = stream.Length, ContentType = Manifest.MimeTypes.GetMimeType(file.Name), // A fuller namespace is required here because of a conflict with the other apparitions of MimeTypes. Metadata = file is IVaultNote note ? (await note.ReadDocumentAsync()).Frontmatter : new() diff --git a/Nodsoft.MoltenObsidian.Tool/Commands/Manifest/GenerateManifestCommand.cs b/Nodsoft.MoltenObsidian.Tool/Commands/Manifest/GenerateManifestCommand.cs index 8da1087..d38ce9f 100644 --- a/Nodsoft.MoltenObsidian.Tool/Commands/Manifest/GenerateManifestCommand.cs +++ b/Nodsoft.MoltenObsidian.Tool/Commands/Manifest/GenerateManifestCommand.cs @@ -2,7 +2,6 @@ using System.Text.Json; using System.Text.Json.Serialization; using JetBrains.Annotations; -using Nodsoft.MoltenObsidian.Tool.Services; using Nodsoft.MoltenObsidian.Vaults.FileSystem; using Nodsoft.MoltenObsidian.Manifest; using Spectre.Console; From 884580d6090adc8b4103409ec07df34a69022a8b Mon Sep 17 00:00:00 2001 From: Sakura Akeno Isayeki Date: Sat, 5 Aug 2023 14:34:24 +0200 Subject: [PATCH 2/4] refactor(ObsidianText): Improve front matter splitting and BOM trimming - Refactored the `SplitYamlFrontMatter` method to improve readability and maintainability. - Added a new private method `TrimBom` to remove the Byte Order Mark (BOM) from the text. - Updated the code to trim BOM from the first line of text if present. --- Nodsoft.MoltenObsidian/ObsidianText.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Nodsoft.MoltenObsidian/ObsidianText.cs b/Nodsoft.MoltenObsidian/ObsidianText.cs index 529b38d..2d38718 100644 --- a/Nodsoft.MoltenObsidian/ObsidianText.cs +++ b/Nodsoft.MoltenObsidian/ObsidianText.cs @@ -97,14 +97,12 @@ public string ToHtml(ObsidianHtmlConverter converter) => _vaultFile is null /// A tuple containing the YAML front matter and the remaining Obsidian Markdown content. private static (string? frontMatter, string content) SplitYamlFrontMatter(string obsidianMarkdown) { - // The front matter is a YAML document at the beginning of the file, delimited by three dashes. - static bool _LineDelimiterPredicate(string line) => line is "---"; - // We need to find the first line with three dashes, and then the next line with three dashes. // The content between the two lines is the front matter. // If there is no front matter, the content is the whole file. string[] lines = obsidianMarkdown.Split(new[] { Environment.NewLine }, StringSplitOptions.None); - + TrimBom(ref lines[0]); + int firstLine = Array.FindIndex(lines, _LineDelimiterPredicate); int secondLine = Array.FindIndex(lines, firstLine + 1, _LineDelimiterPredicate); @@ -117,6 +115,9 @@ private static (string? frontMatter, string content) SplitYamlFrontMatter(string string content = string.Join(Environment.NewLine, lines[(secondLine + 1)..]); return (frontMatter, content); + + // The front matter is a YAML document at the beginning of the file, delimited by three dashes. + static bool _LineDelimiterPredicate(string line) => line is "---"; } /// @@ -126,4 +127,12 @@ private static (string? frontMatter, string content) SplitYamlFrontMatter(string /// A dictionary of key-value pairs. /// Thrown when the YAML front matter is invalid. public static Dictionary ParseYamlFrontMatter(string frontMatter) => _yamlDeserializer.Deserialize>(frontMatter); + + private static void TrimBom(scoped ref string text) + { + if (text.Length is not 0 && text[0] is '\uFEFF') + { + text = text[1..]; + } + } } \ No newline at end of file From a9037a71af34c6fd81d5ceefa03bd597b6f36ac9 Mon Sep 17 00:00:00 2001 From: Sakura Akeno Isayeki Date: Sat, 5 Aug 2023 15:37:54 +0200 Subject: [PATCH 3/4] feat(ObsidianText): Add SplitByNewline method This commit adds a new method `SplitByNewline` to the `ObsidianText` class. This method splits a specified string by newlines, handling different newline characters (`\r\n`, `\r`, `\n`). It returns an array of strings representing the split lines. The purpose of this change is to improve the handling of line splitting in the `ObsidianText` class, providing more flexibility and compatibility with different newline formats. --- Nodsoft.MoltenObsidian/ObsidianText.cs | 42 ++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/Nodsoft.MoltenObsidian/ObsidianText.cs b/Nodsoft.MoltenObsidian/ObsidianText.cs index 2d38718..5a752fe 100644 --- a/Nodsoft.MoltenObsidian/ObsidianText.cs +++ b/Nodsoft.MoltenObsidian/ObsidianText.cs @@ -1,4 +1,5 @@ -using Nodsoft.MoltenObsidian.Converter; +using System.Text; +using Nodsoft.MoltenObsidian.Converter; using Nodsoft.MoltenObsidian.Vault; using YamlDotNet.Core; using YamlDotNet.Serialization; @@ -100,7 +101,9 @@ private static (string? frontMatter, string content) SplitYamlFrontMatter(string // We need to find the first line with three dashes, and then the next line with three dashes. // The content between the two lines is the front matter. // If there is no front matter, the content is the whole file. - string[] lines = obsidianMarkdown.Split(new[] { Environment.NewLine }, StringSplitOptions.None); + + string[] lines = SplitByNewline(obsidianMarkdown); +// string[] lines = obsidianMarkdown.Split(new[] { Environment.NewLine }, StringSplitOptions.None); TrimBom(ref lines[0]); int firstLine = Array.FindIndex(lines, _LineDelimiterPredicate); @@ -135,4 +138,39 @@ private static void TrimBom(scoped ref string text) text = text[1..]; } } + + /// + /// Splits the specified string by newlines, handling first \r\n, then \r, then \n. + /// + /// + /// + public static string[] SplitByNewline(ReadOnlySpan input) + { + var result = new List(); + + int previousIndex = 0; + for (int i = 0; i < input.Length; i++) + { + if (input[i] is '\n' || (input[i] is '\r' && (i == input.Length - 1 || input[i + 1] is not '\n'))) + { + result.Add(input[previousIndex..i].ToString()); + + previousIndex = i + 1; + } + else if (input[i] is '\r' && i < input.Length - 1 && input[i + 1] is '\n') + { + result.Add(input[previousIndex..i].ToString()); + + previousIndex = i + 2; + i++; + } + } + + if (previousIndex < input.Length) + { + result.Add(input[previousIndex..].ToString()); + } + + return result.ToArray(); + } } \ No newline at end of file From b3dfbfd232e0e4a5caa837eb6703b505e480828b Mon Sep 17 00:00:00 2001 From: Sakura Akeno Isayeki Date: Sat, 5 Aug 2023 15:43:59 +0200 Subject: [PATCH 4/4] feat(SSG): Update debug output and file writing logic - Updated the debug output in the GenerateStaticSiteCommand.cs file to use Markdown syntax for better readability. - Modified the logic for writing files in the GenerateStaticSiteCommand.cs file to truncate existing files rather than byte-level overwrites. --- .../Commands/SSG/GenerateStaticSiteCommand.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Nodsoft.MoltenObsidian.Tool/Commands/SSG/GenerateStaticSiteCommand.cs b/Nodsoft.MoltenObsidian.Tool/Commands/SSG/GenerateStaticSiteCommand.cs index 33e1c89..258c2b1 100644 --- a/Nodsoft.MoltenObsidian.Tool/Commands/SSG/GenerateStaticSiteCommand.cs +++ b/Nodsoft.MoltenObsidian.Tool/Commands/SSG/GenerateStaticSiteCommand.cs @@ -96,12 +96,12 @@ await AnsiConsole.Status().StartAsync("Generating static assets.", async ctx => if (settings.DebugMode) { - AnsiConsole.Console.MarkupLine(/*lang=markdown*/$"[grey]Ignoring folders:[/] {string.Join("[grey], [/]", settings.IgnoredFolders ?? new[] { "*None*" })}"); - AnsiConsole.Console.MarkupLine(/*lang=markdown*/$"[grey]Ignoring files:[/] {string.Join("[grey], [/]", settings.IgnoredFiles ?? new[] { "*None*" })}"); + AnsiConsole.Console.MarkupLine(/*lang=md*/$"[grey]Ignoring folders:[/] {string.Join("[grey], [/]", settings.IgnoredFolders ?? new[] { "*None*" })}"); + AnsiConsole.Console.MarkupLine(/*lang=md*/$"[grey]Ignoring files:[/] {string.Join("[grey], [/]", settings.IgnoredFiles ?? new[] { "*None*" })}"); AnsiConsole.Console.MarkupLine(settings.OutputPath is null - ? /*lang=markdown*/$"[grey]Output path defaulted to current directory: [/]{Environment.CurrentDirectory}" - : /*lang=markdown*/$"[grey]Output path set: [/]{settings.OutputPath}" + ? /*lang=md*/$"[grey]Output path defaulted to current directory: [/]{Environment.CurrentDirectory}" + : /*lang=md*/$"[grey]Output path set: [/]{settings.OutputPath}" ); } @@ -116,19 +116,12 @@ await AnsiConsole.Status().StartAsync("Generating static assets.", async ctx => } List fileData = await StaticSiteGenerator.CreateOutputFilesAsync(settings.OutputPath!.ToString(), pathFilePair); - - // foreach((FileInfo file, byte[] data) in fileData) - // { - // await WriteDataAsync(file, data); - // } - await Task.WhenAll(fileData.Select(WriteDataAsync)); } - AnsiConsole.Console.MarkupLine(/*lang=markdown*/$"Wrote manifest to [green link]{settings.OutputPath}[/]."); + AnsiConsole.Console.MarkupLine(/*lang=md*/$"Wrote static files to [green link]{settings.OutputPath}[/]."); }); - return 0; } @@ -138,7 +131,7 @@ private static async Task WriteDataAsync(InfoDataPair pair) { pair.FileInfo.Directory.Create(); } - await using FileStream stream = pair.FileInfo.Open(FileMode.OpenOrCreate, FileAccess.Write); + await using FileStream stream = pair.FileInfo.Open(FileMode.Create, FileAccess.Write); await stream.WriteAsync(pair.FileData); await stream.FlushAsync();