diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c024d5025..a4acf0723 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,8 @@ on: push: branches: - main + - net80incremental + jobs: build: name: Build @@ -19,20 +21,19 @@ jobs: NUGET_CERT_REVOCATION_MODE: offline steps: - name: Get Source - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive - name: Install .NET Core SDK - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: | - 3.1.x - 6.0.x + 8.x - name: Build and Test run: dotnet test --logger "trx;LogFileName=test-results.trx" - name: Upload Test Results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 # Stick with v3 until https://github.com/dorny/test-reporter/issues/343 is resolved if: success() || failure() with: name: test-results-${{ matrix.os }} - path: '**/test-results.trx' \ No newline at end of file + path: "**/test-results.trx" diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml index 6bc50c1a6..05e19c4f4 100644 --- a/.github/workflows/test-report.yml +++ b/.github/workflows/test-report.yml @@ -1,7 +1,7 @@ -name: 'Test Report' +name: "Test Report" on: workflow_run: - workflows: [ 'Build' ] + workflows: ["Build"] types: - completed jobs: @@ -13,9 +13,9 @@ jobs: os: [windows-latest, ubuntu-latest, macos-latest] steps: - name: Process Test Results - uses: dorny/test-reporter@v1 + uses: dorny/test-reporter@v1.7.0 with: artifact: test-results-${{ matrix.os }} - name: 'Test Results (${{ matrix.os }})' - path: '**/*.trx' - reporter: dotnet-trx \ No newline at end of file + name: "Test Results (${{ matrix.os }})" + path: "**/*.trx" + reporter: dotnet-trx diff --git a/Directory.Build.props b/Directory.Build.props index 3d207d2be..9883d1996 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,16 +20,16 @@ true snupkg true - netcoreapp3.1 + net8.0 true - NU1901;NU1902;NU1903;NU1904;CA1724 + NU1901;NU1902;NU1903;NU1904;CA1040;CA1724;CA1062;CA1710;CA1711;CA1725;CS8600;CS8601;CS8602;CS8603;CS8604;CS8618;CS8619;CS8620;CS8622;CS8625;CS8631 - + diff --git a/src/core/Statiq.App/Commands/PipelinesCommand{TSettings}.cs b/src/core/Statiq.App/Commands/PipelinesCommand{TSettings}.cs index c07d5e902..583ff6355 100644 --- a/src/core/Statiq.App/Commands/PipelinesCommand{TSettings}.cs +++ b/src/core/Statiq.App/Commands/PipelinesCommand{TSettings}.cs @@ -38,7 +38,9 @@ protected override async Task ExecuteEngineAsync( { new ConsoleListener(() => { +#pragma warning disable VSTHRD103 cancellationTokenSource.Cancel(); +#pragma warning restore VSTHRD103 return Task.CompletedTask; }); return (int)await engineManager.ExecuteAsync(cancellationTokenSource); diff --git a/src/core/Statiq.App/Statiq.App.csproj b/src/core/Statiq.App/Statiq.App.csproj index 65d025a2a..d32951f7e 100644 --- a/src/core/Statiq.App/Statiq.App.csproj +++ b/src/core/Statiq.App/Statiq.App.csproj @@ -11,14 +11,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/core/Statiq.Common/Content/MediaTypes.cs b/src/core/Statiq.Common/Content/MediaTypes.cs index 484d3abc3..85a6560e7 100644 --- a/src/core/Statiq.Common/Content/MediaTypes.cs +++ b/src/core/Statiq.Common/Content/MediaTypes.cs @@ -1086,7 +1086,9 @@ async Task Main() /// The media type. /// Will return a "text/x-[extension]" default media type if a mapping is not found. /// true if the media type could be determined, false otherwise. +#pragma warning disable CA1021 // Avoid out parameters public static bool TryGet(string path, out string mediaType, bool defaultIfNotFound = true) +#pragma warning restore CA1021 // Avoid out parameters { if (string.IsNullOrWhiteSpace(path)) { diff --git a/src/core/Statiq.Common/Documents/ObjectDocument{T}.cs b/src/core/Statiq.Common/Documents/ObjectDocument{T}.cs index d6fd9c02d..7e5053536 100644 --- a/src/core/Statiq.Common/Documents/ObjectDocument{T}.cs +++ b/src/core/Statiq.Common/Documents/ObjectDocument{T}.cs @@ -30,7 +30,9 @@ public sealed class ObjectDocument : IDocument /// /// The underlying object. /// +#pragma warning disable CA1720 // Identifier contains type name public T Object { get; } +#pragma warning restore CA1720 // Identifier contains type name /// public NormalizedPath Source { get; } diff --git a/src/core/Statiq.Common/IO/IReadOnlyFileSystemExtensions.cs b/src/core/Statiq.Common/IO/IReadOnlyFileSystemExtensions.cs index c3505d19c..05f8690f3 100644 --- a/src/core/Statiq.Common/IO/IReadOnlyFileSystemExtensions.cs +++ b/src/core/Statiq.Common/IO/IReadOnlyFileSystemExtensions.cs @@ -21,10 +21,12 @@ public static class IReadOnlyFileSystemExtensions /// path is deeper than the real one). This contains all the mapped input paths that couldn't be unmapped. /// /// The "unmapped" input paths. +#pragma warning disable CA1021 public static IEnumerable GetUnmappedInputPaths( this IReadOnlyFileSystem fileSystem, in NormalizedPath path, out HashSet nonExistingMappedPaths) +#pragma warning restore CA1021 { fileSystem.ThrowIfNull(nameof(fileSystem)); path.ThrowIfNull(nameof(path)); diff --git a/src/core/Statiq.Common/Scripting/IScriptHelper.cs b/src/core/Statiq.Common/Scripting/IScriptHelper.cs index 8d7c7ea88..213810bb0 100644 --- a/src/core/Statiq.Common/Scripting/IScriptHelper.cs +++ b/src/core/Statiq.Common/Scripting/IScriptHelper.cs @@ -46,7 +46,9 @@ public interface IScriptHelper /// true if the candidate string is a script string that should be cached, /// false if the candidate string is a script string that should not be cached, /// and null otherwise. +#pragma warning disable CA1021 public static bool? TryGetScriptString(string str, out string script) +#pragma warning restore CA1021 { if (TryGetScriptString(str, true, out script)) { diff --git a/src/core/Statiq.Common/Statiq.Common.csproj b/src/core/Statiq.Common/Statiq.Common.csproj index 3b1aa596c..e383b311e 100644 --- a/src/core/Statiq.Common/Statiq.Common.csproj +++ b/src/core/Statiq.Common/Statiq.Common.csproj @@ -4,13 +4,13 @@ Statiq Static StaticContent StaticSite Blog BlogEngine - - - - - - - + + + + + + + diff --git a/src/core/Statiq.Common/Util/ItemStreams/ItemStream{TItem}.cs b/src/core/Statiq.Common/Util/ItemStreams/ItemStream{TItem}.cs index a155e3a74..691a4de90 100644 --- a/src/core/Statiq.Common/Util/ItemStreams/ItemStream{TItem}.cs +++ b/src/core/Statiq.Common/Util/ItemStreams/ItemStream{TItem}.cs @@ -196,7 +196,9 @@ public sealed override int Read(Span buffer) public sealed override int WriteTimeout { get => base.WriteTimeout; set => base.WriteTimeout = value; } +#pragma warning disable CS0672,SYSLIB0010 // Member overrides obsolete member public sealed override object InitializeLifetimeService() => base.InitializeLifetimeService(); +#pragma warning restore CS0672,SYSLIB0010 // Member overrides obsolete member public sealed override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => base.BeginRead(buffer, offset, count, callback, state); diff --git a/src/core/Statiq.Common/Util/LockingStreamWrapper.cs b/src/core/Statiq.Common/Util/LockingStreamWrapper.cs index 76e88daf0..f8f6e8a0a 100644 --- a/src/core/Statiq.Common/Util/LockingStreamWrapper.cs +++ b/src/core/Statiq.Common/Util/LockingStreamWrapper.cs @@ -23,7 +23,9 @@ public LockingStreamWrapper(Stream stream, bool disposeStream) _disposeStream = disposeStream; } +#pragma warning disable CA1721 protected Stream Stream { get; } +#pragma warning restore CA1721 /// /// Gets the wrapped stream and locks access until it's disposed. diff --git a/src/core/Statiq.Common/Util/StringStream.cs b/src/core/Statiq.Common/Util/StringStream.cs index 00fa69b69..a56f8de9a 100644 --- a/src/core/Statiq.Common/Util/StringStream.cs +++ b/src/core/Statiq.Common/Util/StringStream.cs @@ -46,7 +46,9 @@ protected override void Dispose(bool disposing) } } +#pragma warning disable CA1720 // Identifier contains type name public string String { get; } +#pragma warning restore CA1720 // Identifier contains type name public override bool CanRead => true; diff --git a/src/core/Statiq.Core/Execution/ExecutionPipeline.cs b/src/core/Statiq.Core/Execution/ExecutionPipeline.cs index a7ec894e5..f8836eafe 100644 --- a/src/core/Statiq.Core/Execution/ExecutionPipeline.cs +++ b/src/core/Statiq.Core/Execution/ExecutionPipeline.cs @@ -79,7 +79,7 @@ protected ExecutionPipeline() public virtual ExecutionPolicy ExecutionPolicy { get; set; } protected sealed override Task> ExecuteContextAsync(IExecutionContext context) => - context.Phase switch + (context ?? throw new ArgumentNullException(nameof(context))).Phase switch { Phase.Input => ExecuteInputPhaseAsync(context), Phase.Process => ExecuteProcessPhaseAsync(context), diff --git a/src/core/Statiq.Core/Execution/IEngineExtensions.cs b/src/core/Statiq.Core/Execution/IEngineExtensions.cs index 015a76b06..25048d52f 100644 --- a/src/core/Statiq.Core/Execution/IEngineExtensions.cs +++ b/src/core/Statiq.Core/Execution/IEngineExtensions.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Reflection; using Microsoft.Extensions.Logging; +using Polly; using Statiq.Common; namespace Statiq.Core @@ -10,6 +11,9 @@ public static class IEngineExtensions { public static void LogAndCheckVersion(this IEngine engine, Assembly assembly, string name, string minimumVersionKey) { + ArgumentNullException.ThrowIfNull(assembly); + ArgumentNullException.ThrowIfNull(engine); + if (!(Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) is AssemblyInformationalVersionAttribute versionAttribute)) { throw new Exception($"Could not determine the {name} version from {assembly.FullName}"); @@ -18,16 +22,16 @@ public static void LogAndCheckVersion(this IEngine engine, Assembly assembly, st // Get and print the version string informationalVersion = versionAttribute.InformationalVersion; engine.Logger.LogInformation($"{name} version {informationalVersion}", true); - SemVer.Version version = new SemVer.Version(informationalVersion, true); + SemanticVersioning.Version version = new SemanticVersioning.Version(informationalVersion, true); // Get all version ranges - (string Key, SemVer.Version Version)[] minimumVersions = engine.Settings.Keys + (string Key, SemanticVersioning.Version Version)[] minimumVersions = engine.Settings.Keys .Where(k => k.StartsWith(minimumVersionKey)) .Select(k => (Key: k, Value: engine.Settings.GetString(k))) .Where(x => !x.Value.IsNullOrWhiteSpace()) - .Select(x => (x.Key, new SemVer.Version(x.Value, true))) + .Select(x => (x.Key, new SemanticVersioning.Version(x.Value, true))) .ToArray(); - foreach ((string Key, SemVer.Version Version) minimumVersion in minimumVersions) + foreach ((string Key, SemanticVersioning.Version Version) minimumVersion in minimumVersions) { if (version < minimumVersion.Version) { diff --git a/src/core/Statiq.Core/Execution/MemoryStreamFactory.cs b/src/core/Statiq.Core/Execution/MemoryStreamFactory.cs index de1638279..7ff8194e0 100644 --- a/src/core/Statiq.Core/Execution/MemoryStreamFactory.cs +++ b/src/core/Statiq.Core/Execution/MemoryStreamFactory.cs @@ -14,13 +14,14 @@ public class MemoryStreamFactory : IMemoryStreamFactory private const int BlockSize = 16384; private readonly RecyclableMemoryStreamManager _manager = - new RecyclableMemoryStreamManager( +#pragma warning disable SA1000 + new(new RecyclableMemoryStreamManager.Options( +#pragma warning restore SA1000 BlockSize, RecyclableMemoryStreamManager.DefaultLargeBufferMultiple, - RecyclableMemoryStreamManager.DefaultMaximumBufferSize) - { - MaximumFreeSmallPoolBytes = BlockSize * 32768L * 2, // 1 GB - }; + RecyclableMemoryStreamManager.DefaultMaximumBufferSize, + BlockSize * 32768L * 2, // 1 GB + 0)); public virtual MemoryStream GetStream() => _manager.GetStream(); diff --git a/src/core/Statiq.Core/Execution/NamespaceCollection.cs b/src/core/Statiq.Core/Execution/NamespaceCollection.cs index 27fa969d4..79e7ce4a0 100644 --- a/src/core/Statiq.Core/Execution/NamespaceCollection.cs +++ b/src/core/Statiq.Core/Execution/NamespaceCollection.cs @@ -1,8 +1,10 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ConcurrentCollections; +using Polly; using Statiq.Common; namespace Statiq.Core @@ -40,6 +42,8 @@ public virtual bool Add(string ns) public virtual void AddRange(IEnumerable namespaces) { + ArgumentNullException.ThrowIfNull(namespaces); + // Iterate manually so we can throw for null or white space foreach (string ns in namespaces) { diff --git a/src/core/Statiq.Core/Modules/Content/AddContentToMetadata.cs b/src/core/Statiq.Core/Modules/Content/AddContentToMetadata.cs index e2a85bc41..6b7789f46 100644 --- a/src/core/Statiq.Core/Modules/Content/AddContentToMetadata.cs +++ b/src/core/Statiq.Core/Modules/Content/AddContentToMetadata.cs @@ -48,6 +48,8 @@ protected override async Task> ExecuteChildrenAsync( IExecutionContext context, ImmutableArray childOutputs) { + ArgumentNullException.ThrowIfNull(context); + if (childOutputs.Length == 0) { return context.Inputs; diff --git a/src/core/Statiq.Core/Modules/Content/AddRtlSupport.cs b/src/core/Statiq.Core/Modules/Content/AddRtlSupport.cs index dcbf2e32c..46bcdd639 100644 --- a/src/core/Statiq.Core/Modules/Content/AddRtlSupport.cs +++ b/src/core/Statiq.Core/Modules/Content/AddRtlSupport.cs @@ -173,7 +173,9 @@ private static bool IsRightToLeft(int c) c == 0x00FB3E; } +#pragma warning disable CA1505 // Avoid unmaintainable code private static bool IsLeftToRight(int c) +#pragma warning restore CA1505 // Avoid unmaintainable code { // Generated from Table D.2 of RFC3454 // http://www.ietf.org/rfc/rfc3454.txt diff --git a/src/core/Statiq.Core/Modules/Content/EscapeHtml.cs b/src/core/Statiq.Core/Modules/Content/EscapeHtml.cs index bfbdb448c..1ca542add 100644 --- a/src/core/Statiq.Core/Modules/Content/EscapeHtml.cs +++ b/src/core/Statiq.Core/Modules/Content/EscapeHtml.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using Polly; using Statiq.Common; namespace Statiq.Core @@ -159,6 +161,8 @@ public EscapeHtml WithDefaultStandard() /// The current module instance. public EscapeHtml WithStandard(params char[] standard) { + ArgumentNullException.ThrowIfNull(standard); + foreach (char c in standard) { _standardCharacters.Add(c); @@ -185,6 +189,8 @@ public EscapeHtml EscapeAllNonstandard() /// The current module instance. public EscapeHtml WithEscapedChar(params char[] toEscape) { + ArgumentNullException.ThrowIfNull(toEscape); + foreach (char c in toEscape) { _currentlyEscapedCharacters.Add( @@ -201,6 +207,8 @@ private string GenerateEscape(char c) protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(input); + string oldContent = await input.GetContentStringAsync(); StringWriter outputString = new StringWriter(); bool escaped = false; diff --git a/src/core/Statiq.Core/Modules/Content/GenerateRedirects.cs b/src/core/Statiq.Core/Modules/Content/GenerateRedirects.cs index f08f68646..1db915c44 100644 --- a/src/core/Statiq.Core/Modules/Content/GenerateRedirects.cs +++ b/src/core/Statiq.Core/Modules/Content/GenerateRedirects.cs @@ -148,6 +148,8 @@ public GenerateRedirects AlwaysCreateAdditionalOutput(bool alwaysCreateAdditiona /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + // Iterate redirects and generate all of the per-redirect documents (I.e., meta refresh pages) ConcurrentDictionary redirects = new ConcurrentDictionary(); diff --git a/src/core/Statiq.Core/Modules/Content/GenerateSitemap.cs b/src/core/Statiq.Core/Modules/Content/GenerateSitemap.cs index 1b981637d..e392755ab 100644 --- a/src/core/Statiq.Core/Modules/Content/GenerateSitemap.cs +++ b/src/core/Statiq.Core/Modules/Content/GenerateSitemap.cs @@ -77,6 +77,8 @@ public GenerateSitemap(Config sitemapItemOrLocation, Func protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + HashSet locations = new HashSet(); // Remove duplicate locations List content = new List(); content.Add(""); diff --git a/src/core/Statiq.Core/Modules/Content/InsertLinks.cs b/src/core/Statiq.Core/Modules/Content/InsertLinks.cs index 7461fd377..fa3e20e32 100644 --- a/src/core/Statiq.Core/Modules/Content/InsertLinks.cs +++ b/src/core/Statiq.Core/Modules/Content/InsertLinks.cs @@ -150,7 +150,7 @@ public InsertLinks WithEndWordSeparators(params char[] endWordSeparators) foreach (IElement element in htmlDocument.QuerySelectorAll(_querySelector).Where(t => !t.Ancestors().Any())) { // Enumerate all descendant text nodes not already in a link element - foreach (IText text in element.Descendents().OfType().Where(t => !t.Ancestors().Any())) + foreach (IText text in element.Descendants().OfType().Where(t => !t.Ancestors().Any())) { if (ReplaceStrings(text, links, out string newText)) { diff --git a/src/core/Statiq.Core/Modules/Content/JoinDocuments.cs b/src/core/Statiq.Core/Modules/Content/JoinDocuments.cs index cc2605743..b1aabcd67 100644 --- a/src/core/Statiq.Core/Modules/Content/JoinDocuments.cs +++ b/src/core/Statiq.Core/Modules/Content/JoinDocuments.cs @@ -53,6 +53,8 @@ public JoinDocuments(string delimiter, JoinedMetadata metaDataMode = JoinedMetad /// A single document in a list. protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + if (context.Inputs.Length < 1) { return context.CreateDocument().Yield(); diff --git a/src/core/Statiq.Core/Modules/Content/MergeContent.cs b/src/core/Statiq.Core/Modules/Content/MergeContent.cs index 60e75be34..8287456a1 100644 --- a/src/core/Statiq.Core/Modules/Content/MergeContent.cs +++ b/src/core/Statiq.Core/Modules/Content/MergeContent.cs @@ -59,6 +59,6 @@ protected override IEnumerable ExecuteChildren( ImmutableArray childOutputs) => _reverse ? childOutputs.SelectMany(childOutput => context.Inputs.Select(input => childOutput.Clone(input.ContentProvider))) - : context.Inputs.SelectMany(input => childOutputs.Select(result => input.Clone(result.ContentProvider))); + : (context ?? throw new ArgumentNullException(nameof(context))).Inputs.SelectMany(input => childOutputs.Select(result => input.Clone(result.ContentProvider))); } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Content/ProcessShortcodes.cs b/src/core/Statiq.Core/Modules/Content/ProcessShortcodes.cs index 0903511ac..05a7a36aa 100644 --- a/src/core/Statiq.Core/Modules/Content/ProcessShortcodes.cs +++ b/src/core/Statiq.Core/Modules/Content/ProcessShortcodes.cs @@ -50,6 +50,9 @@ public ProcessShortcodes(string startDelimiter, string endDelimiter) protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(input); + ArgumentNullException.ThrowIfNull(context); + IContentProvider contentProvider = await ProcessShortcodesAsync(input, input.ContentProvider, context); return contentProvider is null ? input.Yield() : input.Clone(contentProvider).Yield(); } diff --git a/src/core/Statiq.Core/Modules/Content/ReplaceInContent.cs b/src/core/Statiq.Core/Modules/Content/ReplaceInContent.cs index 9e59e9a9f..ed2f1924e 100644 --- a/src/core/Statiq.Core/Modules/Content/ReplaceInContent.cs +++ b/src/core/Statiq.Core/Modules/Content/ReplaceInContent.cs @@ -76,6 +76,8 @@ public ReplaceInContent IsRegex(RegexOptions regexOptions = RegexOptions.None) /// protected override async Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, string value) { + ArgumentNullException.ThrowIfNull(input); + if (value is null) { value = string.Empty; diff --git a/src/core/Statiq.Core/Modules/Content/ReplaceWithContent.cs b/src/core/Statiq.Core/Modules/Content/ReplaceWithContent.cs index d721349a9..5b4d2a630 100644 --- a/src/core/Statiq.Core/Modules/Content/ReplaceWithContent.cs +++ b/src/core/Statiq.Core/Modules/Content/ReplaceWithContent.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using System.Threading.Tasks; using Statiq.Common; @@ -49,6 +50,8 @@ public ReplaceWithContent IsRegex(RegexOptions regexOptions = RegexOptions.None) /// protected override async Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, string value) { + ArgumentNullException.ThrowIfNull(input); + if (value is null) { value = string.Empty; diff --git a/src/core/Statiq.Core/Modules/Content/SetMediaType.cs b/src/core/Statiq.Core/Modules/Content/SetMediaType.cs index 054fa127c..a1f5fd5cf 100644 --- a/src/core/Statiq.Core/Modules/Content/SetMediaType.cs +++ b/src/core/Statiq.Core/Modules/Content/SetMediaType.cs @@ -18,6 +18,6 @@ public SetMediaType(Config mediaType) } protected override IEnumerable ExecuteConfig(IDocument input, IExecutionContext context, string value) => - input.MediaTypeEquals(value) ? input.Yield() : input.Clone(input.ContentProvider.CloneWithMediaType(value)).Yield(); + (input ?? throw new ArgumentNullException(nameof(input))).MediaTypeEquals(value) ? input.Yield() : input.Clone(input.ContentProvider.CloneWithMediaType(value)).Yield(); } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/CacheDocuments.cs b/src/core/Statiq.Core/Modules/Control/CacheDocuments.cs index d6c1bb606..6070a0c60 100644 --- a/src/core/Statiq.Core/Modules/Control/CacheDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/CacheDocuments.cs @@ -134,6 +134,8 @@ private void ResetCache() /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + // If we're disabling the cache, clear any existing entries and just execute children if (context.Settings.GetBool(Keys.DisableCache)) { diff --git a/src/core/Statiq.Core/Modules/Control/CombineDocuments.cs b/src/core/Statiq.Core/Modules/Control/CombineDocuments.cs index 24d36cb53..1d89a75a1 100644 --- a/src/core/Statiq.Core/Modules/Control/CombineDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/CombineDocuments.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Statiq.Common; @@ -21,6 +22,8 @@ public class CombineDocuments : Module /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + IDocument result = null; foreach (IDocument input in context.Inputs) { diff --git a/src/core/Statiq.Core/Modules/Control/ConcatDocuments.cs b/src/core/Statiq.Core/Modules/Control/ConcatDocuments.cs index e2db859ae..2fb195582 100644 --- a/src/core/Statiq.Core/Modules/Control/ConcatDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/ConcatDocuments.cs @@ -40,6 +40,6 @@ public ConcatDocuments(Config> documents) protected override IEnumerable ExecuteChildren( IExecutionContext context, ImmutableArray childOutputs) => - context.Inputs.Concat(childOutputs); + (context ?? throw new ArgumentNullException(nameof(context))).Inputs.Concat(childOutputs); } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/EnumerateValues.cs b/src/core/Statiq.Core/Modules/Control/EnumerateValues.cs index db6680597..deb40c8b7 100644 --- a/src/core/Statiq.Core/Modules/Control/EnumerateValues.cs +++ b/src/core/Statiq.Core/Modules/Control/EnumerateValues.cs @@ -79,6 +79,8 @@ public EnumerateValues WithInputDocument(Config withInput) protected override async Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, IEnumerable value) { + ArgumentNullException.ThrowIfNull(context); + // Return the input document(s) for empty values if (value is null) { diff --git a/src/core/Statiq.Core/Modules/Control/ExecuteBranch.cs b/src/core/Statiq.Core/Modules/Control/ExecuteBranch.cs index 4d7ed5b10..550dfd072 100644 --- a/src/core/Statiq.Core/Modules/Control/ExecuteBranch.cs +++ b/src/core/Statiq.Core/Modules/Control/ExecuteBranch.cs @@ -36,6 +36,8 @@ public ExecuteBranch Branch(IEnumerable modules) /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + List results = new List(); foreach (IEnumerable modules in _branches) { diff --git a/src/core/Statiq.Core/Modules/Control/ExecuteDestinations.cs b/src/core/Statiq.Core/Modules/Control/ExecuteDestinations.cs index be922a816..4ad1851f1 100644 --- a/src/core/Statiq.Core/Modules/Control/ExecuteDestinations.cs +++ b/src/core/Statiq.Core/Modules/Control/ExecuteDestinations.cs @@ -60,7 +60,9 @@ public ExecuteDestinations(IEnumerable patterns, params IModule[] module /// Adds a module to execute. This method is mainly to support collection initialization. /// /// The module to add. +#pragma warning disable CA1725 // Parameter names should match base declaration public void Add(IModule module) => _modules.Add(module); +#pragma warning restore CA1725 // Parameter names should match base declaration public ExecuteDestinations WithModules(params IModule[] modules) { @@ -87,6 +89,8 @@ public ExecuteDestinations WithoutUnmatchedDocuments(bool withoutUnmatchedDocume protected override async Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, IEnumerable value) { + ArgumentNullException.ThrowIfNull(context); + List results = new List(); HashSet matched = new HashSet(context.Inputs.FilterDestinations(value)); if (matched.Count > 0) diff --git a/src/core/Statiq.Core/Modules/Control/ExecuteIf.cs b/src/core/Statiq.Core/Modules/Control/ExecuteIf.cs index 51ee22222..6e55df41f 100644 --- a/src/core/Statiq.Core/Modules/Control/ExecuteIf.cs +++ b/src/core/Statiq.Core/Modules/Control/ExecuteIf.cs @@ -1,4 +1,5 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -210,6 +211,8 @@ public ExecuteIf WithoutUnmatchedDocuments(bool withoutUnmatchedDocuments = true /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + List results = new List(); IReadOnlyList documents = context.Inputs; bool hasExecuted = false; diff --git a/src/core/Statiq.Core/Modules/Control/ExecuteModules.cs b/src/core/Statiq.Core/Modules/Control/ExecuteModules.cs index 967f74862..c7b1ce781 100644 --- a/src/core/Statiq.Core/Modules/Control/ExecuteModules.cs +++ b/src/core/Statiq.Core/Modules/Control/ExecuteModules.cs @@ -26,6 +26,6 @@ public ExecuteModules(params IModule[] modules) protected override IEnumerable ExecuteChildren( IExecutionContext context, ImmutableArray childOutputs) => - context.Inputs; + (context ?? throw new ArgumentNullException(nameof(context))).Inputs; } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/ExecuteSources.cs b/src/core/Statiq.Core/Modules/Control/ExecuteSources.cs index 9ab4916f8..fbb509e72 100644 --- a/src/core/Statiq.Core/Modules/Control/ExecuteSources.cs +++ b/src/core/Statiq.Core/Modules/Control/ExecuteSources.cs @@ -60,7 +60,9 @@ public ExecuteSources(IEnumerable patterns, params IModule[] modules) /// Adds a module to execute. This method is mainly to support collection initialization. /// /// The module to add. +#pragma warning disable CA1725 // Parameter names should match base declaration public void Add(IModule module) => _modules.Add(module); +#pragma warning restore CA1725 // Parameter names should match base declaration public ExecuteSources WithModules(params IModule[] modules) { @@ -87,6 +89,8 @@ public ExecuteSources WithoutUnmatchedDocuments(bool withoutUnmatchedDocuments = protected override async Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, IEnumerable value) { + ArgumentNullException.ThrowIfNull(context); + List results = new List(); HashSet matched = new HashSet(context.Inputs.FilterSources(value)); if (matched.Count > 0) diff --git a/src/core/Statiq.Core/Modules/Control/ExecuteSwitch.cs b/src/core/Statiq.Core/Modules/Control/ExecuteSwitch.cs index 1450ecfa7..b5c9947fe 100644 --- a/src/core/Statiq.Core/Modules/Control/ExecuteSwitch.cs +++ b/src/core/Statiq.Core/Modules/Control/ExecuteSwitch.cs @@ -77,6 +77,8 @@ public ExecuteSwitch Default(IEnumerable modules) /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + List results = new List(); IEnumerable documents = context.Inputs; foreach (Tuple> c in _cases) diff --git a/src/core/Statiq.Core/Modules/Control/ExtractFrontMatter.cs b/src/core/Statiq.Core/Modules/Control/ExtractFrontMatter.cs index 057c0e9a4..72ba19b20 100644 --- a/src/core/Statiq.Core/Modules/Control/ExtractFrontMatter.cs +++ b/src/core/Statiq.Core/Modules/Control/ExtractFrontMatter.cs @@ -165,6 +165,8 @@ public ExtractFrontMatter PreserveFrontMatter(bool preserveFrontMatter = true) // Execute at the context level so we can compile the RegEx pattern(s) once. protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + List regexes = new List(); // Get the explicit delimiter regex diff --git a/src/core/Statiq.Core/Modules/Control/FilterDestinations.cs b/src/core/Statiq.Core/Modules/Control/FilterDestinations.cs index dce1b23b0..e8ff6fc1e 100644 --- a/src/core/Statiq.Core/Modules/Control/FilterDestinations.cs +++ b/src/core/Statiq.Core/Modules/Control/FilterDestinations.cs @@ -57,6 +57,6 @@ protected override IEnumerable ExecuteConfig( IDocument input, IExecutionContext context, IEnumerable value) => - context.Inputs.FilterDestinations(value); + (context ?? throw new ArgumentNullException(nameof(context))).Inputs.FilterDestinations(value); } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/FilterDocuments.cs b/src/core/Statiq.Core/Modules/Control/FilterDocuments.cs index c042a732e..96a3618ca 100644 --- a/src/core/Statiq.Core/Modules/Control/FilterDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/FilterDocuments.cs @@ -65,7 +65,11 @@ public FilterDocuments Or(Config predicate) public FilterDocuments Or(string key) => Or(Config.FromDocument(doc => doc.ContainsKey(key))); /// - protected override async Task> ExecuteContextAsync(IExecutionContext context) => - await context.Inputs.FilterAsync(_predicates, context).ToListAsync(context.CancellationToken); + protected override async Task> ExecuteContextAsync(IExecutionContext context) + { + ArgumentNullException.ThrowIfNull(context); + + return await context.Inputs.FilterAsync(_predicates, context).ToListAsync(context.CancellationToken); + } } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/FilterSources.cs b/src/core/Statiq.Core/Modules/Control/FilterSources.cs index 4c3d1916e..68d296fab 100644 --- a/src/core/Statiq.Core/Modules/Control/FilterSources.cs +++ b/src/core/Statiq.Core/Modules/Control/FilterSources.cs @@ -57,6 +57,6 @@ protected override IEnumerable ExecuteConfig( IDocument input, IExecutionContext context, IEnumerable value) => - context.Inputs.FilterSources(value); + (context ?? throw new ArgumentNullException(nameof(context))).Inputs.FilterSources(value); } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/GroupDocuments.cs b/src/core/Statiq.Core/Modules/Control/GroupDocuments.cs index 3a174164d..e5bdc1d60 100644 --- a/src/core/Statiq.Core/Modules/Control/GroupDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/GroupDocuments.cs @@ -91,6 +91,8 @@ public GroupDocuments WithSource(in NormalizedPath source) /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + List<(IDocument Document, IEnumerable Keys)> groups = await context.Inputs .ToAsyncEnumerable() .SelectAwait(async x => (Document: x, Keys: await _key.GetValueAsync(x, context))) diff --git a/src/core/Statiq.Core/Modules/Control/MergeDocuments.cs b/src/core/Statiq.Core/Modules/Control/MergeDocuments.cs index 4efd28f03..8b2535074 100644 --- a/src/core/Statiq.Core/Modules/Control/MergeDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/MergeDocuments.cs @@ -70,19 +70,31 @@ public MergeDocuments KeepExistingMetadata(bool keepExistingMetadata = true) protected override IEnumerable ExecuteChildren( IExecutionContext context, - ImmutableArray childOutputs) => - _reverse + ImmutableArray childOutputs) + { + ArgumentNullException.ThrowIfNull(context); + + return _reverse ? childOutputs.SelectMany(childOutput => { IMetadata childOutputWithoutSettings = childOutput.WithoutSettings(); return context.Inputs.Select(input => - childOutput.Clone(_keepExistingMetadata ? input.WithoutSettings().GetRawEnumerable().Where(x => !childOutputWithoutSettings.ContainsKey(x.Key)) : input, input.ContentProvider)); + childOutput.Clone( + _keepExistingMetadata + ? input.WithoutSettings().GetRawEnumerable() + .Where(x => !childOutputWithoutSettings.ContainsKey(x.Key)) + : input, input.ContentProvider)); }) : context.Inputs.SelectMany(input => { IMetadata inputWithoutSettings = input.WithoutSettings(); return childOutputs.Select(result => - input.Clone(_keepExistingMetadata ? result.WithoutSettings().GetRawEnumerable().Where(x => !inputWithoutSettings.ContainsKey(x.Key)) : result, result.ContentProvider)); + input.Clone( + _keepExistingMetadata + ? result.WithoutSettings().GetRawEnumerable() + .Where(x => !inputWithoutSettings.ContainsKey(x.Key)) + : result, result.ContentProvider)); }); + } } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/OrderDocuments.cs b/src/core/Statiq.Core/Modules/Control/OrderDocuments.cs index ffcfde247..6581434c7 100644 --- a/src/core/Statiq.Core/Modules/Control/OrderDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/OrderDocuments.cs @@ -149,6 +149,8 @@ public OrderDocuments WithComparison(Comparison comparison) /// protected override Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + IOrderedEnumerable orderdList = null; foreach (Order order in _orders.Reverse()) { diff --git a/src/core/Statiq.Core/Modules/Control/PaginateDocuments.cs b/src/core/Statiq.Core/Modules/Control/PaginateDocuments.cs index 8b032cb4d..e425adabc 100644 --- a/src/core/Statiq.Core/Modules/Control/PaginateDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/PaginateDocuments.cs @@ -86,6 +86,8 @@ public PaginateDocuments WithSource(in NormalizedPath source) /// protected override IEnumerable ExecuteContext(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + // Partition the pages and get a total before skip/take IDocument[][] pages = Partition(context.Inputs, _pageSize) diff --git a/src/core/Statiq.Core/Modules/Control/ProcessSidecarFile.cs b/src/core/Statiq.Core/Modules/Control/ProcessSidecarFile.cs index d1a26fe73..9c246baa0 100644 --- a/src/core/Statiq.Core/Modules/Control/ProcessSidecarFile.cs +++ b/src/core/Statiq.Core/Modules/Control/ProcessSidecarFile.cs @@ -64,6 +64,9 @@ public ProcessSidecarFile(Config sidecarPath, params IModule[] m /// protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(input); + ArgumentNullException.ThrowIfNull(context); + NormalizedPath sidecarPath = await _sidecarPath.GetValueAsync(input, context); if (!sidecarPath.IsNull) { diff --git a/src/core/Statiq.Core/Modules/Control/RetryModules.cs b/src/core/Statiq.Core/Modules/Control/RetryModules.cs index 37f5db701..19ff1381c 100644 --- a/src/core/Statiq.Core/Modules/Control/RetryModules.cs +++ b/src/core/Statiq.Core/Modules/Control/RetryModules.cs @@ -46,14 +46,18 @@ public RetryModules WithFailureMessage(Func failureMessageFactory, return this; } - protected override async Task> ExecuteContextAsync(IExecutionContext context) => - await Policy + protected override async Task> ExecuteContextAsync(IExecutionContext context) + { + ArgumentNullException.ThrowIfNull(context); + + return await Policy .Handle(ex => { if (ex is LoggedException loggedException) { ex = loggedException.InnerException; } + return !_handledExceptionTypes.Contains(ex.GetType()); }) .WaitAndRetryAsync( @@ -68,10 +72,12 @@ await Policy context.Log(_failureMessageLogLevel, failureMessage); } } + return _sleepDurationProvider(attempt); }) .ExecuteAsync( async _ => await context.ExecuteModulesAsync(Children, context.Inputs), context.CancellationToken); + } } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/TakeDocuments.cs b/src/core/Statiq.Core/Modules/Control/TakeDocuments.cs index b9252fc59..897b8cd36 100644 --- a/src/core/Statiq.Core/Modules/Control/TakeDocuments.cs +++ b/src/core/Statiq.Core/Modules/Control/TakeDocuments.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Statiq.Common; @@ -22,6 +23,6 @@ public TakeDocuments(int x) _x = x; } - protected override IEnumerable ExecuteContext(IExecutionContext context) => context.Inputs.Take(_x); + protected override IEnumerable ExecuteContext(IExecutionContext context) => (context ?? throw new ArgumentNullException(nameof(context))).Inputs.Take(_x); } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Control/ThrowException.cs b/src/core/Statiq.Core/Modules/Control/ThrowException.cs index f6532b543..b5d8335b2 100644 --- a/src/core/Statiq.Core/Modules/Control/ThrowException.cs +++ b/src/core/Statiq.Core/Modules/Control/ThrowException.cs @@ -8,7 +8,9 @@ namespace Statiq.Core /// Throws an exception. /// /// +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix public class ThrowException : SyncConfigModule +#pragma warning restore CA1711 // Identifiers should not have incorrect suffix { /// /// Throws the specified exception. @@ -34,6 +36,9 @@ protected override IEnumerable ExecuteConfig(IDocument input, IExecut { throw value; } + + ArgumentNullException.ThrowIfNull(context); + return input is null ? context.Inputs : input.Yield(); } } diff --git a/src/core/Statiq.Core/Modules/Extensibility/EvaluateScript.cs b/src/core/Statiq.Core/Modules/Extensibility/EvaluateScript.cs index b5fe1b562..81a034408 100644 --- a/src/core/Statiq.Core/Modules/Extensibility/EvaluateScript.cs +++ b/src/core/Statiq.Core/Modules/Extensibility/EvaluateScript.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -14,6 +15,8 @@ public class EvaluateScript : ParallelModule { protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + object value = await context.ScriptHelper.EvaluateAsync(await input.GetContentStringAsync(), input); return await context.CloneOrCreateDocumentsAsync(input, input.Yield(), value ?? new NullContent()); } diff --git a/src/core/Statiq.Core/Modules/Extensibility/ExecuteConfig.cs b/src/core/Statiq.Core/Modules/Extensibility/ExecuteConfig.cs index d7d023c56..e0c9ea56e 100644 --- a/src/core/Statiq.Core/Modules/Extensibility/ExecuteConfig.cs +++ b/src/core/Statiq.Core/Modules/Extensibility/ExecuteConfig.cs @@ -48,6 +48,8 @@ public ExecuteConfig(Config config) protected override Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, object value) { + ArgumentNullException.ThrowIfNull(context); + IEnumerable inputs = context.Inputs; if (input is object) { diff --git a/src/core/Statiq.Core/Modules/Extensibility/LogMessage.cs b/src/core/Statiq.Core/Modules/Extensibility/LogMessage.cs index 2e590aff8..68dedd006 100644 --- a/src/core/Statiq.Core/Modules/Extensibility/LogMessage.cs +++ b/src/core/Statiq.Core/Modules/Extensibility/LogMessage.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -41,6 +42,8 @@ public LogMessage(LogLevel logLevel, Config content) protected override IEnumerable ExecuteConfig(IDocument input, IExecutionContext context, string value) { + ArgumentNullException.ThrowIfNull(context); + context.Log(_logLevel, value); return input is null ? context.Inputs : input.Yield(); } diff --git a/src/core/Statiq.Core/Modules/Extensibility/StartProcess.cs b/src/core/Statiq.Core/Modules/Extensibility/StartProcess.cs index 49f36e5ac..bb4a8006a 100644 --- a/src/core/Statiq.Core/Modules/Extensibility/StartProcess.cs +++ b/src/core/Statiq.Core/Modules/Extensibility/StartProcess.cs @@ -302,6 +302,8 @@ public StartProcess WithErrorExitCode(Func isErrorExitCode) protected override IEnumerable ExecuteConfig(IDocument input, IExecutionContext context, IMetadata values) { + ArgumentNullException.ThrowIfNull(context); + // Only execute once if requested if (_onlyOnce && _executed) { diff --git a/src/core/Statiq.Core/Modules/IO/CopyFiles.cs b/src/core/Statiq.Core/Modules/IO/CopyFiles.cs index 7929bf48b..0c2ae64f7 100644 --- a/src/core/Statiq.Core/Modules/IO/CopyFiles.cs +++ b/src/core/Statiq.Core/Modules/IO/CopyFiles.cs @@ -97,6 +97,8 @@ protected override async Task> ExecuteConfigAsync(IDocume { if (value is object) { + ArgumentNullException.ThrowIfNull(context); + // Use a semaphore to limit the write operations so we don't try to do a bunch of writes at once SemaphoreSlim semaphore = new SemaphoreSlim(20, 20); diff --git a/src/core/Statiq.Core/Modules/IO/MirrorResources.cs b/src/core/Statiq.Core/Modules/IO/MirrorResources.cs index b9fd3bbd3..0ade2396a 100644 --- a/src/core/Statiq.Core/Modules/IO/MirrorResources.cs +++ b/src/core/Statiq.Core/Modules/IO/MirrorResources.cs @@ -67,7 +67,10 @@ public MirrorResources(Func pathFunc) protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + #pragma warning disable RCS1163 // Unused parameter. + // Handle invalid HTTPS certificates and allow alternate security protocols (see http://stackoverflow.com/a/5670954/807064) ServicePointManager.ServerCertificateValidationCallback = (s, cert, chain, ssl) => true; #pragma warning restore RCS1163 // Unused parameter. diff --git a/src/core/Statiq.Core/Modules/IO/ReadApplicationInput.cs b/src/core/Statiq.Core/Modules/IO/ReadApplicationInput.cs index 73f9ac096..7cc581919 100644 --- a/src/core/Statiq.Core/Modules/IO/ReadApplicationInput.cs +++ b/src/core/Statiq.Core/Modules/IO/ReadApplicationInput.cs @@ -18,6 +18,8 @@ public class ReadApplicationInput : SyncModule /// protected override IEnumerable ExecuteContext(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + // If ApplicationInput is empty, return nothing if (string.IsNullOrWhiteSpace(context.ApplicationState.Input)) { diff --git a/src/core/Statiq.Core/Modules/IO/ReadFiles.cs b/src/core/Statiq.Core/Modules/IO/ReadFiles.cs index ebca8a585..c8d3938fa 100644 --- a/src/core/Statiq.Core/Modules/IO/ReadFiles.cs +++ b/src/core/Statiq.Core/Modules/IO/ReadFiles.cs @@ -103,6 +103,8 @@ public ReadFiles WithMediaType(Func mediaType) protected override async Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, IEnumerable value) { + ArgumentNullException.ThrowIfNull(context); + IEnumerable files = context.FileSystem.GetInputFiles(value); files = await files.ParallelWhereAsync(async file => _predicate is null || await _predicate(file)); return files.AsParallel() diff --git a/src/core/Statiq.Core/Modules/IO/ReadWeb.cs b/src/core/Statiq.Core/Modules/IO/ReadWeb.cs index 0d6504d0f..d51edd5a6 100644 --- a/src/core/Statiq.Core/Modules/IO/ReadWeb.cs +++ b/src/core/Statiq.Core/Modules/IO/ReadWeb.cs @@ -7,6 +7,7 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Polly; using Statiq.Common; namespace Statiq.Core @@ -53,6 +54,8 @@ public ReadWeb(string uri, WebRequestHeaders headers) /// The current module instance. public ReadWeb WithUris(params string[] uris) { + ArgumentNullException.ThrowIfNull(uris); + foreach (string uri in uris) { _requests.Add(new WebRequest(uri)); diff --git a/src/core/Statiq.Core/Modules/IO/SetDestination.cs b/src/core/Statiq.Core/Modules/IO/SetDestination.cs index 7264e47f0..f6a802196 100644 --- a/src/core/Statiq.Core/Modules/IO/SetDestination.cs +++ b/src/core/Statiq.Core/Modules/IO/SetDestination.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Polly; using Statiq.Common; namespace Statiq.Core @@ -124,6 +125,8 @@ public SetDestination(Config destination, bool ignoreMetadata = public static NormalizedPath GetCurrentDestinationFromMetadata(IDocument doc) { + ArgumentNullException.ThrowIfNull(doc); + NormalizedPath path = doc.GetPath(Keys.DestinationPath); if (!path.IsNull) { @@ -147,6 +150,8 @@ protected override Task> ExecuteConfigAsync( IExecutionContext context, NormalizedPath value) { + ArgumentNullException.ThrowIfNull(input); + if (value.IsNull) { return Task.FromResult(input.Yield()); diff --git a/src/core/Statiq.Core/Modules/IO/WriteFiles.cs b/src/core/Statiq.Core/Modules/IO/WriteFiles.cs index ee744f6ca..1399b4357 100644 --- a/src/core/Statiq.Core/Modules/IO/WriteFiles.cs +++ b/src/core/Statiq.Core/Modules/IO/WriteFiles.cs @@ -70,6 +70,8 @@ public WriteFiles Where(Config predicate) /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + // Use a semaphore to limit the write operations so we don't try to do a bunch of writes at once SemaphoreSlim semaphore = new SemaphoreSlim(20, 20); diff --git a/src/core/Statiq.Core/Modules/Metadata/AddDocumentsToMetadata.cs b/src/core/Statiq.Core/Modules/Metadata/AddDocumentsToMetadata.cs index 1201b498d..e22cc49cd 100644 --- a/src/core/Statiq.Core/Modules/Metadata/AddDocumentsToMetadata.cs +++ b/src/core/Statiq.Core/Modules/Metadata/AddDocumentsToMetadata.cs @@ -45,12 +45,16 @@ public AddDocumentsToMetadata(string key, Config> documen protected override IEnumerable ExecuteChildren( IExecutionContext context, - ImmutableArray childOutputs) => - childOutputs.Length == 0 + ImmutableArray childOutputs) + { + ArgumentNullException.ThrowIfNull(context); + + return childOutputs.Length == 0 ? context.Inputs : context.Inputs.Select(input => input.Clone(new MetadataItems { { _key, childOutputs.Length == 1 ? (object)childOutputs[0] : childOutputs } })); + } } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Metadata/AddIndexes.cs b/src/core/Statiq.Core/Modules/Metadata/AddIndexes.cs index 61d642488..fde463a40 100644 --- a/src/core/Statiq.Core/Modules/Metadata/AddIndexes.cs +++ b/src/core/Statiq.Core/Modules/Metadata/AddIndexes.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Statiq.Common; @@ -13,7 +14,12 @@ namespace Statiq.Core public class AddIndexes : Module { /// - protected override Task> ExecuteContextAsync(IExecutionContext context) => - Task.FromResult(context.Inputs.Select((x, i) => x.Clone(new MetadataItems { { Keys.Index, i + 1 } }))); + protected override Task> ExecuteContextAsync(IExecutionContext context) + { + ArgumentNullException.ThrowIfNull(context); + + return Task.FromResult( + context.Inputs.Select((x, i) => x.Clone(new MetadataItems { { Keys.Index, i + 1 } }))); + } } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Metadata/AddTitle.cs b/src/core/Statiq.Core/Modules/Metadata/AddTitle.cs index e26c29102..c6be4c4c6 100644 --- a/src/core/Statiq.Core/Modules/Metadata/AddTitle.cs +++ b/src/core/Statiq.Core/Modules/Metadata/AddTitle.cs @@ -66,6 +66,8 @@ public AddTitle KeepExisting(bool keepExisting = true) protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(input); + // Check if there's already a title set if (_keepExisting && input.ContainsKey(_key)) { diff --git a/src/core/Statiq.Core/Modules/Metadata/CopyMetadata.cs b/src/core/Statiq.Core/Modules/Metadata/CopyMetadata.cs index 3957d276c..a3f1ac2ae 100644 --- a/src/core/Statiq.Core/Modules/Metadata/CopyMetadata.cs +++ b/src/core/Statiq.Core/Modules/Metadata/CopyMetadata.cs @@ -54,6 +54,8 @@ public CopyMetadata WithFormat(Func format) protected override Task> ExecuteInputAsync(IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(input); + if (input.TryGetValue(_fromKey, out object existingValue)) { if (_format is object) diff --git a/src/core/Statiq.Core/Modules/Metadata/CreateTree.cs b/src/core/Statiq.Core/Modules/Metadata/CreateTree.cs index de20c3c01..e29d2e405 100644 --- a/src/core/Statiq.Core/Modules/Metadata/CreateTree.cs +++ b/src/core/Statiq.Core/Modules/Metadata/CreateTree.cs @@ -171,6 +171,8 @@ public CreateTree WithNesting(bool nesting = true, bool collapseRoot = false) /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + // Create a dictionary of tree nodes TreeNodeEqualityComparer treeNodeEqualityComparer = new TreeNodeEqualityComparer(); Dictionary nodesDictionary = await context.Inputs diff --git a/src/core/Statiq.Core/Modules/Metadata/FlattenTree.cs b/src/core/Statiq.Core/Modules/Metadata/FlattenTree.cs index 789ca0c5e..c1410715d 100644 --- a/src/core/Statiq.Core/Modules/Metadata/FlattenTree.cs +++ b/src/core/Statiq.Core/Modules/Metadata/FlattenTree.cs @@ -84,6 +84,6 @@ public FlattenTree(string treePlaceholderKey, string childrenKey) } protected override IEnumerable ExecuteContext(IExecutionContext context) => - context.Inputs.Flatten(_treePlaceholderKey, _childrenKey); + (context ?? throw new ArgumentNullException(nameof(context))).Inputs.Flatten(_treePlaceholderKey, _childrenKey); } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Metadata/GenerateExcerpt.cs b/src/core/Statiq.Core/Modules/Metadata/GenerateExcerpt.cs index 0613ef801..173d9e73f 100644 --- a/src/core/Statiq.Core/Modules/Metadata/GenerateExcerpt.cs +++ b/src/core/Statiq.Core/Modules/Metadata/GenerateExcerpt.cs @@ -116,6 +116,8 @@ public GenerateExcerpt WithOuterHtml(bool outerHtml) protected override async Task> ExecuteInputAsync(Common.IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(input); + if (string.IsNullOrWhiteSpace(_metadataKey) || (_keepExisting && input.ContainsKey(_metadataKey))) { return input.Yield(); diff --git a/src/core/Statiq.Core/Modules/Metadata/MergeMetadata.cs b/src/core/Statiq.Core/Modules/Metadata/MergeMetadata.cs index ced6d2d84..eefc8b63d 100644 --- a/src/core/Statiq.Core/Modules/Metadata/MergeMetadata.cs +++ b/src/core/Statiq.Core/Modules/Metadata/MergeMetadata.cs @@ -70,19 +70,29 @@ public MergeMetadata KeepExisting(bool keepExisting = true) protected override IEnumerable ExecuteChildren( IExecutionContext context, - ImmutableArray childOutputs) => - _reverse + ImmutableArray childOutputs) + { + ArgumentNullException.ThrowIfNull(context); + + return _reverse ? childOutputs.SelectMany(childOutput => { IMetadata childOutputWithoutSettings = childOutput.WithoutSettings(); return context.Inputs.Select(input => - childOutput.Clone(_keepExisting ? input.WithoutSettings().GetRawEnumerable().Where(x => !childOutputWithoutSettings.ContainsKey(x.Key)) : input)); + childOutput.Clone(_keepExisting + ? input.WithoutSettings().GetRawEnumerable() + .Where(x => !childOutputWithoutSettings.ContainsKey(x.Key)) + : input)); }) : context.Inputs.SelectMany(input => { IMetadata inputWithoutSettings = input.WithoutSettings(); return childOutputs.Select(result => - input.Clone(_keepExisting ? result.WithoutSettings().GetRawEnumerable().Where(x => !inputWithoutSettings.ContainsKey(x.Key)) : result)); + input.Clone(_keepExisting + ? result.WithoutSettings().GetRawEnumerable() + .Where(x => !inputWithoutSettings.ContainsKey(x.Key)) + : result)); }); + } } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Metadata/OptimizeFileName.cs b/src/core/Statiq.Core/Modules/Metadata/OptimizeFileName.cs index 34f8983aa..23c247fdc 100644 --- a/src/core/Statiq.Core/Modules/Metadata/OptimizeFileName.cs +++ b/src/core/Statiq.Core/Modules/Metadata/OptimizeFileName.cs @@ -103,6 +103,8 @@ public OptimizeFileName(Config path, string outputKey) protected override IEnumerable ExecuteConfig(IDocument input, IExecutionContext context, IMetadata values) { + ArgumentNullException.ThrowIfNull(input); + // Get file name NormalizedPath path = values.GetPath(Path); if (path.IsNull) diff --git a/src/core/Statiq.Core/Modules/Metadata/ParseJson.cs b/src/core/Statiq.Core/Modules/Metadata/ParseJson.cs index 263d57e4c..3090fd7d4 100644 --- a/src/core/Statiq.Core/Modules/Metadata/ParseJson.cs +++ b/src/core/Statiq.Core/Modules/Metadata/ParseJson.cs @@ -44,6 +44,8 @@ public ParseJson WithOptions(Action modifyOptions) protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + MetadataDictionary metadata; using (Stream contentStream = input.GetContentStream()) { diff --git a/src/core/Statiq.Core/Modules/Metadata/ReadApi.cs b/src/core/Statiq.Core/Modules/Metadata/ReadApi.cs index 8b9ca92ef..88ed600bb 100644 --- a/src/core/Statiq.Core/Modules/Metadata/ReadApi.cs +++ b/src/core/Statiq.Core/Modules/Metadata/ReadApi.cs @@ -347,6 +347,8 @@ protected virtual async Task>> ExecuteR IExecutionContext context, TClient client) { + ArgumentNullException.ThrowIfNull(request); + try { return await request(input, context, client); diff --git a/src/core/Statiq.Core/Modules/Metadata/ReadSql.cs b/src/core/Statiq.Core/Modules/Metadata/ReadSql.cs index f10e92117..dad981170 100644 --- a/src/core/Statiq.Core/Modules/Metadata/ReadSql.cs +++ b/src/core/Statiq.Core/Modules/Metadata/ReadSql.cs @@ -55,7 +55,9 @@ protected override IEnumerable GetItems(IExecutionContext context) } /// +#pragma warning disable CA1725 // Parameter names should match base declaration protected override IDictionary GetDictionary(DataRow row) => - row.Table.Columns.Cast().ToDictionary(col => col.ColumnName, col => row[col]); + (row ?? throw new ArgumentNullException(nameof(row))).Table.Columns.Cast().ToDictionary(col => col.ColumnName, col => row[col]); +#pragma warning restore CA1725 // Parameter names should match base declaration } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Metadata/ReadXml.cs b/src/core/Statiq.Core/Modules/Metadata/ReadXml.cs index f8bf9db2d..a05e6f6b3 100644 --- a/src/core/Statiq.Core/Modules/Metadata/ReadXml.cs +++ b/src/core/Statiq.Core/Modules/Metadata/ReadXml.cs @@ -90,6 +90,8 @@ protected override IEnumerable> GetItems(IExecutionCo // Get XML from the input documents? if (_data is null) { + ArgumentNullException.ThrowIfNull(context); + return context.Inputs.AsParallel().SelectMany(input => { XmlDocument inputDoc = new XmlDocument(); diff --git a/src/core/Statiq.Core/Modules/Metadata/RemoveTreePlaceholders.cs b/src/core/Statiq.Core/Modules/Metadata/RemoveTreePlaceholders.cs index 5d4e7ed7e..47a1b0416 100644 --- a/src/core/Statiq.Core/Modules/Metadata/RemoveTreePlaceholders.cs +++ b/src/core/Statiq.Core/Modules/Metadata/RemoveTreePlaceholders.cs @@ -32,6 +32,6 @@ public RemoveTreePlaceholders(string treePlaceholderKey) } protected override IEnumerable ExecuteContext(IExecutionContext context) => - context.Inputs.RemoveTreePlaceholders(_treePlaceholderKey); + (context ?? throw new ArgumentNullException(nameof(context))).Inputs.RemoveTreePlaceholders(_treePlaceholderKey); } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Metadata/SetMetadata.cs b/src/core/Statiq.Core/Modules/Metadata/SetMetadata.cs index ab107b1a8..8fd8ccc52 100644 --- a/src/core/Statiq.Core/Modules/Metadata/SetMetadata.cs +++ b/src/core/Statiq.Core/Modules/Metadata/SetMetadata.cs @@ -56,10 +56,14 @@ public SetMetadata OnlyIfNonExisting(bool onlyIfNonExisting = true) return this; } - protected override Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, object value) => - Task.FromResult( + protected override Task> ExecuteConfigAsync(IDocument input, IExecutionContext context, object value) + { + ArgumentNullException.ThrowIfNull(input); + + return Task.FromResult( (_onlyIfNonExisting && input.ContainsKey(_key)) || (_ignoreNull && value is null) ? input.Yield() : input.Clone(new MetadataItems { { _key, value } }).Yield()); + } } } \ No newline at end of file diff --git a/src/core/Statiq.Core/Modules/Metadata/ValidateMetadata{T}.cs b/src/core/Statiq.Core/Modules/Metadata/ValidateMetadata{T}.cs index 8b60559f1..c238a9127 100644 --- a/src/core/Statiq.Core/Modules/Metadata/ValidateMetadata{T}.cs +++ b/src/core/Statiq.Core/Modules/Metadata/ValidateMetadata{T}.cs @@ -85,6 +85,8 @@ public ValidateMetadata WithAssertion(Func execute, string message = /// protected override Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + Parallel.ForEach(context.Inputs, input => { // Check if the key exists diff --git a/src/core/Statiq.Core/Modules/Templates/TransformXslt.cs b/src/core/Statiq.Core/Modules/Templates/TransformXslt.cs index 666895522..9a89695db 100644 --- a/src/core/Statiq.Core/Modules/Templates/TransformXslt.cs +++ b/src/core/Statiq.Core/Modules/Templates/TransformXslt.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -45,6 +46,8 @@ public TransformXslt(params IModule[] modules) protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + XslCompiledTransform xslt = new XslCompiledTransform(); if (_xsltPath is object) diff --git a/src/core/Statiq.Core/Scripting/ReflectionHelper.cs b/src/core/Statiq.Core/Scripting/ReflectionHelper.cs index 4a6d8accd..298540863 100644 --- a/src/core/Statiq.Core/Scripting/ReflectionHelper.cs +++ b/src/core/Statiq.Core/Scripting/ReflectionHelper.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text; +using Polly; using Statiq.Common; namespace Statiq.Core @@ -15,6 +16,8 @@ public static class ReflectionHelper { public static IEnumerable GetCallSignatures(Type type, string callThrough, bool extensions = true) { + ArgumentNullException.ThrowIfNull(type); + Type[] types = type.IsInterface ? type.GetInterfaces().Concat(new[] { type }).ToArray() : new[] { type }; @@ -46,18 +49,20 @@ public static IEnumerable GetCallSignatures(Type type, string callThroug // Always returns public methods public static IEnumerable GetCallableMethods(Type type) => - type.GetMethods() + (type ?? throw new ArgumentNullException(nameof(type))).GetMethods() .Where(m => !m.IsSpecialName && !m.DeclaringType.Equals(typeof(object)) && !m.IsStatic) .Distinct(); // Always returns public properties public static IEnumerable GetCallableProperties(Type type) => - type.GetProperties() + (type ?? throw new ArgumentNullException(nameof(type))).GetProperties() .Where(p => !p.IsSpecialName && !p.DeclaringType.Equals(typeof(object)) && !p.GetAccessors().Any(a => a.IsStatic)) .Distinct(); public static IEnumerable GetExtensionMethods(Type type, IEnumerable candidateTypes = null) { + ArgumentNullException.ThrowIfNull(type); + foreach (Type candidateType in candidateTypes ?? type.Assembly.GetTypes()) { if (candidateType.Name.EndsWith("Extensions")) @@ -84,6 +89,8 @@ public static IEnumerable GetExtensionMethods(Type type, IEnumerable public static string GetPropertySignature(PropertyInfo propertyInfo, bool callable = false, bool getterAndSetter = false) { + ArgumentNullException.ThrowIfNull(propertyInfo); + string visibility = GetVisibility(propertyInfo, out string getterVisibility, out string setterVisibility); ParameterInfo[] parameters = propertyInfo.GetIndexParameters(); if (parameters.Length > 0) @@ -135,6 +142,8 @@ public static string GetPropertySignature(PropertyInfo propertyInfo, bool callab /// public static string GetMethodSignature(MethodInfo method, bool callable = false, bool convertExtensionsToInstance = false) { + ArgumentNullException.ThrowIfNull(method); + // Special case to use interface syntax for GetEnumerator() methods if (method.Name.Equals(nameof(IEnumerable.GetEnumerator)) && method.DeclaringType.IsInterface @@ -263,6 +272,8 @@ private static string BuildParameterSignature(ParameterInfo[] parameters, Method /// public static string GetTypeName(Type type) { + ArgumentNullException.ThrowIfNull(type); + Type nullableType = Nullable.GetUnderlyingType(type); if (nullableType is object) { diff --git a/src/core/Statiq.Core/Scripting/ScriptCompilationException.cs b/src/core/Statiq.Core/Scripting/ScriptCompilationException.cs index c7b9da943..e38d8ad3c 100644 --- a/src/core/Statiq.Core/Scripting/ScriptCompilationException.cs +++ b/src/core/Statiq.Core/Scripting/ScriptCompilationException.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Polly; namespace Statiq.Core { @@ -9,6 +10,8 @@ public class ScriptCompilationException : Exception public ScriptCompilationException(List errorMessages) { + ArgumentNullException.ThrowIfNull(errorMessages); + ErrorMessages = errorMessages.AsReadOnly(); } } diff --git a/src/core/Statiq.Core/Scripting/ScriptHelper.cs b/src/core/Statiq.Core/Scripting/ScriptHelper.cs index 7bbcedc10..027f4245c 100644 --- a/src/core/Statiq.Core/Scripting/ScriptHelper.cs +++ b/src/core/Statiq.Core/Scripting/ScriptHelper.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Polly; using Statiq.Common; namespace Statiq.Core @@ -151,6 +152,8 @@ private static IEnumerable GetScriptNamespaces(IExecutionState execution public static void LogAndEnsureCompilationSuccess(EmitResult result, ILogger logger, string name = null) { + ArgumentNullException.ThrowIfNull(result); + // Log warnings List warningMessages = result.Diagnostics .Where(x => x.Severity == DiagnosticSeverity.Warning) diff --git a/src/core/Statiq.Core/Shortcodes/Content/EvalShortcode.cs b/src/core/Statiq.Core/Shortcodes/Content/EvalShortcode.cs index fdbeaf228..eb1f3f8a5 100644 --- a/src/core/Statiq.Core/Shortcodes/Content/EvalShortcode.cs +++ b/src/core/Statiq.Core/Shortcodes/Content/EvalShortcode.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Statiq.Common; @@ -17,6 +18,8 @@ public class EvalShortcode : Shortcode { public override async Task ExecuteAsync(KeyValuePair[] args, string content, IDocument document, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + object value = await context.ScriptHelper.EvaluateAsync(content, document); return value.ToString(); } diff --git a/src/core/Statiq.Core/Shortcodes/IO/IncludeShortcode.cs b/src/core/Statiq.Core/Shortcodes/IO/IncludeShortcode.cs index 540def53c..19fc4cbcc 100644 --- a/src/core/Statiq.Core/Shortcodes/IO/IncludeShortcode.cs +++ b/src/core/Statiq.Core/Shortcodes/IO/IncludeShortcode.cs @@ -45,6 +45,8 @@ public class IncludeShortcode : Shortcode /// public override async Task ExecuteAsync(KeyValuePair[] args, string content, IDocument document, IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + // See if this is a web include string included = args.SingleValue(); if (included.StartsWith(Uri.UriSchemeHttp + "://") || included.StartsWith(Uri.UriSchemeHttps + "://")) @@ -69,6 +71,8 @@ public override async Task ExecuteAsync(KeyValuePair - - - + + + - - - + + + diff --git a/src/core/Statiq.Core/Util/ArgumentSplitter.cs b/src/core/Statiq.Core/Util/ArgumentSplitter.cs index 12bb2d30f..ddd23a430 100644 --- a/src/core/Statiq.Core/Util/ArgumentSplitter.cs +++ b/src/core/Statiq.Core/Util/ArgumentSplitter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Polly; namespace Statiq.Core { @@ -19,6 +20,8 @@ public static class ArgumentSplitter /// Each quoted argument as delimited in the original string by spaces. public static IEnumerable Split(string arguments) { + ArgumentNullException.ThrowIfNull(arguments); + bool inQuotes = false; return arguments diff --git a/src/core/Statiq.Core/Util/SynchronizationContextRemover.cs b/src/core/Statiq.Core/Util/SynchronizationContextRemover.cs index ec477d237..04ff9e808 100644 --- a/src/core/Statiq.Core/Util/SynchronizationContextRemover.cs +++ b/src/core/Statiq.Core/Util/SynchronizationContextRemover.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using System.Threading; +using Polly; namespace Statiq.Core { @@ -15,6 +16,8 @@ public struct SynchronizationContextRemover : INotifyCompletion public void OnCompleted(Action continuation) { + ArgumentNullException.ThrowIfNull(continuation); + SynchronizationContext previousContext = SynchronizationContext.Current; try { diff --git a/src/core/Statiq.Testing/Analyzers/TestAnalyzerContext.cs b/src/core/Statiq.Testing/Analyzers/TestAnalyzerContext.cs index 2252522cc..86c9ae266 100644 --- a/src/core/Statiq.Testing/Analyzers/TestAnalyzerContext.cs +++ b/src/core/Statiq.Testing/Analyzers/TestAnalyzerContext.cs @@ -23,7 +23,9 @@ public TestAnalyzerContext(IEnumerable inputs) { } +#pragma warning disable CA1721 public LogLevel LogLevel { get; set; } = LogLevel.Warning; +#pragma warning restore CA1721 public ConcurrentBag AnalyzerResults { get; } = new ConcurrentBag(); diff --git a/src/core/Statiq.Testing/Statiq.Testing.csproj b/src/core/Statiq.Testing/Statiq.Testing.csproj index 2405d486f..d38773fa4 100644 --- a/src/core/Statiq.Testing/Statiq.Testing.csproj +++ b/src/core/Statiq.Testing/Statiq.Testing.csproj @@ -4,14 +4,14 @@ Statiq Static StaticContent StaticSite Blog BlogEngine - - + + - + build - + diff --git a/src/extensions/Statiq.CodeAnalysis/AnalyzeCSharp.cs b/src/extensions/Statiq.CodeAnalysis/AnalyzeCSharp.cs index c6c9ef83c..f6d8e1f42 100644 --- a/src/extensions/Statiq.CodeAnalysis/AnalyzeCSharp.cs +++ b/src/extensions/Statiq.CodeAnalysis/AnalyzeCSharp.cs @@ -455,6 +455,8 @@ private static NormalizedPath GetDefaultDestination(ISymbol symbol, in Normalize /// protected override async Task> ExecuteContextAsync(IExecutionContext context) { + ArgumentNullException.ThrowIfNull(context); + // Create the compilation (have to supply an XmlReferenceResolver to handle include XML doc comments) MetadataReference mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); CSharpCompilation compilation = CSharpCompilation diff --git a/src/extensions/Statiq.CodeAnalysis/ISymbolExtensions.cs b/src/extensions/Statiq.CodeAnalysis/ISymbolExtensions.cs index 567bdac43..93380e431 100644 --- a/src/extensions/Statiq.CodeAnalysis/ISymbolExtensions.cs +++ b/src/extensions/Statiq.CodeAnalysis/ISymbolExtensions.cs @@ -95,7 +95,7 @@ public static string GetDisplayName(this ISymbol symbol) // Add .dll to assembly names return symbol.Name + ".dll"; } - if (symbol.Kind == SymbolKind.Namespace) + if (symbol?.Kind == SymbolKind.Namespace) { // Use "global" for the global namespace display name since it's a reserved keyword and it's used to refer to the global namespace in code return symbol.ContainingNamespace is null ? "global" : GetQualifiedName(symbol); diff --git a/src/extensions/Statiq.CodeAnalysis/ReadProject.cs b/src/extensions/Statiq.CodeAnalysis/ReadProject.cs index f827145d2..a1551ec02 100644 --- a/src/extensions/Statiq.CodeAnalysis/ReadProject.cs +++ b/src/extensions/Statiq.CodeAnalysis/ReadProject.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using Buildalyzer; @@ -43,6 +44,9 @@ public ReadProject(Config path) /// protected override IEnumerable GetProjects(IExecutionContext context, IFile file) { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(file); + StringWriter log = new StringWriter(); AnalyzerManager manager = new AnalyzerManager(new AnalyzerManagerOptions { diff --git a/src/extensions/Statiq.CodeAnalysis/ReadSolution.cs b/src/extensions/Statiq.CodeAnalysis/ReadSolution.cs index 8cb9ce803..e6216434a 100644 --- a/src/extensions/Statiq.CodeAnalysis/ReadSolution.cs +++ b/src/extensions/Statiq.CodeAnalysis/ReadSolution.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -44,6 +45,8 @@ public ReadSolution(Config path) /// protected override IEnumerable GetProjects(IExecutionContext context, IFile file) { + ArgumentNullException.ThrowIfNull(file); + StringWriter log = new StringWriter(); AnalyzerManager manager = new AnalyzerManager(file.Path.Parent.FullPath, new AnalyzerManagerOptions { diff --git a/src/extensions/Statiq.CodeAnalysis/ReadWorkspace.cs b/src/extensions/Statiq.CodeAnalysis/ReadWorkspace.cs index 606bcf090..8b962e23e 100644 --- a/src/extensions/Statiq.CodeAnalysis/ReadWorkspace.cs +++ b/src/extensions/Statiq.CodeAnalysis/ReadWorkspace.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Threading.Tasks; using Buildalyzer; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Logging; @@ -74,6 +73,10 @@ public ReadWorkspace WithExtensions(params string[] extensions) protected internal static IAnalyzerResult CompileProject(IExecutionContext context, IProjectAnalyzer analyzer, StringWriter log) { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(analyzer); + ArgumentNullException.ThrowIfNull(log); + log.GetStringBuilder().Clear(); context.LogDebug($"Building project {analyzer.ProjectFile.Path}"); Stopwatch sw = new Stopwatch(); @@ -92,6 +95,8 @@ protected internal static IAnalyzerResult CompileProject(IExecutionContext conte protected override IEnumerable ExecuteConfig(IDocument input, IExecutionContext context, NormalizedPath value) { + ArgumentNullException.ThrowIfNull(context); + if (!value.IsNull) { IFile projectFile = context.FileSystem.GetInputFile(value); diff --git a/src/extensions/Statiq.CodeAnalysis/Statiq.CodeAnalysis.csproj b/src/extensions/Statiq.CodeAnalysis/Statiq.CodeAnalysis.csproj index 578d993fb..af61bf853 100644 --- a/src/extensions/Statiq.CodeAnalysis/Statiq.CodeAnalysis.csproj +++ b/src/extensions/Statiq.CodeAnalysis/Statiq.CodeAnalysis.csproj @@ -4,7 +4,7 @@ Statiq Static StaticContent StaticSite Blog BlogEngine CodeAnalysis Roslyn XmlDocComments DocComments - + diff --git a/src/extensions/Statiq.Feeds/GenerateFeeds.cs b/src/extensions/Statiq.Feeds/GenerateFeeds.cs index 112d1e423..4631c1f2c 100644 --- a/src/extensions/Statiq.Feeds/GenerateFeeds.cs +++ b/src/extensions/Statiq.Feeds/GenerateFeeds.cs @@ -519,7 +519,7 @@ private static string MakeLinksAbsolute(HtmlParser parser, HtmlMarkupFormatter f { IHtmlDocument dom = parser.ParseDocument(string.Empty); INodeList nodes = parser.ParseFragment(value, dom.Body); - IEnumerable elements = nodes.SelectMany(x => x.Descendents().Where(y => y.HasAttribute("href") || y.HasAttribute("src"))); + IEnumerable elements = nodes.SelectMany(x => x.Descendants().Where(y => y.HasAttribute("href") || y.HasAttribute("src"))); bool replaced = false; foreach (IElement element in elements) { diff --git a/src/extensions/Statiq.Feeds/Syndication/Extensions/ExtensibleBase.cs b/src/extensions/Statiq.Feeds/Syndication/Extensions/ExtensibleBase.cs index e1a4ee6dd..357614b0a 100644 --- a/src/extensions/Statiq.Feeds/Syndication/Extensions/ExtensibleBase.cs +++ b/src/extensions/Statiq.Feeds/Syndication/Extensions/ExtensibleBase.cs @@ -118,7 +118,9 @@ public static string ConvertToString(in DateTime dateTime) } protected static string ConvertToString(Uri uri) => +#pragma warning disable SYSLIB0013 uri is null ? null : Uri.EscapeUriString(uri.ToString()); +#pragma warning restore SYSLIB0013 protected static Uri ConvertToUri(string value) { diff --git a/src/extensions/Statiq.Feeds/Syndication/Rss/RssItem.cs b/src/extensions/Statiq.Feeds/Syndication/Rss/RssItem.cs index 6d8d1cb64..fcaaef4e0 100644 --- a/src/extensions/Statiq.Feeds/Syndication/Rss/RssItem.cs +++ b/src/extensions/Statiq.Feeds/Syndication/Rss/RssItem.cs @@ -223,7 +223,9 @@ public bool EnclosureSpecified } [XmlElement("guid")] +#pragma warning disable CA1720 public RssGuid Guid +#pragma warning restore CA1720 { get { diff --git a/src/extensions/Statiq.Markdown/Statiq.Markdown.csproj b/src/extensions/Statiq.Markdown/Statiq.Markdown.csproj index eade706bd..ef8af6a73 100644 --- a/src/extensions/Statiq.Markdown/Statiq.Markdown.csproj +++ b/src/extensions/Statiq.Markdown/Statiq.Markdown.csproj @@ -4,7 +4,7 @@ Statiq Static StaticContent StaticSite Blog BlogEngine Markdown - + diff --git a/src/extensions/Statiq.Razor/Statiq.Razor.csproj b/src/extensions/Statiq.Razor/Statiq.Razor.csproj index 0e62bdfa6..e97d622f7 100644 --- a/src/extensions/Statiq.Razor/Statiq.Razor.csproj +++ b/src/extensions/Statiq.Razor/Statiq.Razor.csproj @@ -7,13 +7,14 @@ - - - - - - - + + + + + + + + diff --git a/src/extensions/Statiq.Razor/StatiqRazorProjectFileSystem.cs b/src/extensions/Statiq.Razor/StatiqRazorProjectFileSystem.cs index b19f66b68..fcf5cf7ea 100644 --- a/src/extensions/Statiq.Razor/StatiqRazorProjectFileSystem.cs +++ b/src/extensions/Statiq.Razor/StatiqRazorProjectFileSystem.cs @@ -28,7 +28,9 @@ public StatiqRazorProjectFileSystem(Microsoft.Extensions.FileProviders.IFileProv } [Obsolete("Use GetItem(string path, string fileKind) instead.")] +#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member public override RazorProjectItem GetItem(string path) +#pragma warning restore CS0809 // Obsolete member overrides non-obsolete member { return GetItem(path, fileKind: null); } diff --git a/src/extensions/Statiq.Xmp/Statiq.Xmp.csproj b/src/extensions/Statiq.Xmp/Statiq.Xmp.csproj index c17d2e92a..6cfaf8828 100644 --- a/src/extensions/Statiq.Xmp/Statiq.Xmp.csproj +++ b/src/extensions/Statiq.Xmp/Statiq.Xmp.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/extensions/Statiq.Yaml/Statiq.Yaml.csproj b/src/extensions/Statiq.Yaml/Statiq.Yaml.csproj index 082aae7c0..d07bae149 100644 --- a/src/extensions/Statiq.Yaml/Statiq.Yaml.csproj +++ b/src/extensions/Statiq.Yaml/Statiq.Yaml.csproj @@ -4,7 +4,7 @@ Statiq Static StaticContent StaticSite Blog BlogEngine Yaml - + diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index aa2133ccb..ab1e9bd95 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -5,7 +5,7 @@ true - - + + \ No newline at end of file diff --git a/tests/core/Statiq.Common.Tests/Statiq.Common.Tests.csproj b/tests/core/Statiq.Common.Tests/Statiq.Common.Tests.csproj index 1b5f8feda..f0e5b6f8c 100644 --- a/tests/core/Statiq.Common.Tests/Statiq.Common.Tests.csproj +++ b/tests/core/Statiq.Common.Tests/Statiq.Common.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/tests/core/Statiq.Core.Tests/Statiq.Core.Tests.csproj b/tests/core/Statiq.Core.Tests/Statiq.Core.Tests.csproj index 17cb182c3..994874d9a 100644 --- a/tests/core/Statiq.Core.Tests/Statiq.Core.Tests.csproj +++ b/tests/core/Statiq.Core.Tests/Statiq.Core.Tests.csproj @@ -5,6 +5,6 @@ - + \ No newline at end of file diff --git a/tests/core/TestConsoleApp/TestConsoleApp.csproj b/tests/core/TestConsoleApp/TestConsoleApp.csproj index c73e0d169..a269962b5 100644 --- a/tests/core/TestConsoleApp/TestConsoleApp.csproj +++ b/tests/core/TestConsoleApp/TestConsoleApp.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net8.0 diff --git a/tests/extensions/Statiq.CodeAnalysis.Tests/AnalyzeCSharpTypesFixture.cs b/tests/extensions/Statiq.CodeAnalysis.Tests/AnalyzeCSharpTypesFixture.cs index bf7654848..f65342313 100644 --- a/tests/extensions/Statiq.CodeAnalysis.Tests/AnalyzeCSharpTypesFixture.cs +++ b/tests/extensions/Statiq.CodeAnalysis.Tests/AnalyzeCSharpTypesFixture.cs @@ -435,7 +435,7 @@ enum Yellow // Then results.Single(x => x["Name"].Equals("Green"))["SpecificKind"].ShouldBe("Class"); results.Single(x => x["Name"].Equals("Blue"))["SpecificKind"].ShouldBe("Class"); - results.Single(x => x["Name"].Equals("Red"))["SpecificKind"].ShouldBe("Structure"); + results.Single(x => x["Name"].Equals("Red"))["SpecificKind"].ShouldBe("Struct"); results.Single(x => x["Name"].Equals("Yellow"))["SpecificKind"].ShouldBe("Enum"); }