NT.BlazorAnalyzer 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package NT.BlazorAnalyzer --version 1.0.0
                    
NuGet\Install-Package NT.BlazorAnalyzer -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NT.BlazorAnalyzer" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NT.BlazorAnalyzer" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="NT.BlazorAnalyzer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NT.BlazorAnalyzer --version 1.0.0
                    
#r "nuget: NT.BlazorAnalyzer, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NT.BlazorAnalyzer@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NT.BlazorAnalyzer&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=NT.BlazorAnalyzer&version=1.0.0
                    
Install as a Cake Tool

NT.BlazorAnalyzer

NT.BlazorAnalyzer is a Roslyn analyzer for Blazor component error-handling rules.

Current focus:

  • interactive .razor components
  • the generated BuildRenderTree shape from Razor
  • component methods in .razor and .razor.cs partials

Projects

Rules

NTBA0001

Warning when an explicitly interactive component has an unprotected interactive render root. The analyzer evaluates each top-level render root independently, ignores PageTitle and HeadContent, allows inert HTML roots, and requires event-callback HTML roots or component roots to be protected by ErrorBoundary or a derived component.

Diagnostic text: Interactive component '{ComponentName}' should protect interactive render roots with ErrorBoundary or a derived component

NTBA0002

Warning when a method in an interactive component without a valid root ErrorBoundary can be reached without try/catch handling.

This rule is entrypoint-oriented, not helper-oriented:

  • root/API methods should be protected
  • helper methods do not need their own try/catch when every reachable caller already has one
  • a wrapper method does not need a warning if it only delegates to another safe member method
  • a helper method does warn if at least one uncaught root path reaches it

Diagnostic text: Method '{MethodName}' in interactive component '{ComponentName}' can be reached without try/catch handling

NTBA0003

Warning when an interactive lifecycle method with operational code does not use try/catch.

Covered lifecycle methods:

  • OnInitialized
  • OnInitializedAsync
  • OnParametersSet
  • OnParametersSetAsync
  • OnAfterRender
  • OnAfterRenderAsync
  • SetParametersAsync

NTBA0004

Warning when Dispose or DisposeAsync contains operational code without try/catch.

NTBA0005

Warning when a component method performs JS interop without try/catch.

NTBA0006

Warning when JS interop is performed in early lifecycle methods before OnAfterRender{Async} without an interactivity guard.

This warning is suppressed when the JS interop call is inside an if statement that checks for interactivity, for example if (RendererInfo.IsInteractive).

NTBA0007

Warning on async void methods in interactive components.

NTBA0008

Warning on catch blocks that neither:

  • log
  • track/report telemetry
  • nor rethrow

NTBA0009

Warning when a component opens ErrorBoundary first but does not provide ErrorContent.

Warning Examples

Emits NTBA0001 and NTBA0002

@rendermode InteractiveServer

<button @onclick="IncrementCount">Click</button>

@code {
    private void IncrementCount()
    {
        CurrentCount++;
    }

    private int CurrentCount { get; set; }
}

Why:

  • the root button has an interactive callback and is not protected by ErrorBoundary
  • IncrementCount is a UI entry method and has no try/catch

Emits NTBA0001 and NTBA0002 even if the component type derives from ErrorBoundary

public partial class MyComponent : ErrorBoundary
{
    private void HandleClick()
    {
        DoWork();
    }
}

If the generated BuildRenderTree contains an unprotected interactive root, the component still warns. The rule is based on rendered roots, not the component base type.

Emits NTBA0002 on an uncaught root and helper

@rendermode InteractiveServer

<button @onclick="HandleUnsafe">Unsafe</button>
<button @onclick="HandleSafe">Safe</button>

@code {
    private void HandleSafe()
    {
        try
        {
            IncrementCore();
        }
        catch (Exception)
        {
        }
    }

    private void HandleUnsafe()
    {
        IncrementCore();
    }

    private void IncrementCore()
    {
        CurrentCount++;
    }

    private int CurrentCount { get; set; }
}

Why:

  • HandleUnsafe is reachable without try/catch
  • IncrementCore is also reachable from an uncaught root path

Does not emit NTBA0002 for a helper used only from caught roots

@rendermode InteractiveServer

<button @onclick="HandleClick">Click</button>

@code {
    private void HandleClick()
    {
        try
        {
            IncrementCore();
        }
        catch (Exception)
        {
        }
    }

    private void IncrementCore()
    {
        CurrentCount++;
    }

    private int CurrentCount { get; set; }
}

Why:

  • IncrementCore has no try/catch
  • every reachable caller path into IncrementCore is already protected

Does not emit NTBA0002 for a delegating wrapper around a safe method

@rendermode InteractiveServer

<button @onclick="HandleClick">Click</button>

@code {
    private void HandleClick() => HandleClickCore();

    private void HandleClickCore()
    {
        try
        {
            Save();
        }
        catch (Exception)
        {
        }
    }

    private void Save()
    {
    }
}

Why:

  • HandleClick delegates entirely to HandleClickCore
  • HandleClickCore already provides the protection

Does not emit when interactive content is protected by ErrorBoundary

@rendermode InteractiveServer

<ErrorBoundary>
    <button @onclick="IncrementCount">Click</button>
</ErrorBoundary>

@code {
    private void IncrementCount()
    {
        CurrentCount++;
    }

    private int CurrentCount { get; set; }
}

Why:

  • the interactive button is inside ErrorBoundary
  • NTBA0002 is suppressed because the interactive component is already boundary-protected

Emits NTBA0003 for an uncaught lifecycle method

@rendermode InteractiveServer

@code {
    protected override async Task OnInitializedAsync()
    {
        await LoadAsync();
    }

    private Task LoadAsync() => Task.CompletedTask;
}

Emits NTBA0005 for uncaught JS interop

@rendermode InteractiveServer

<button @onclick="HandleClick">Click</button>

@code {
    [Inject] private IJSRuntime JS { get; set; } = default!;

    private async Task HandleClick()
    {
        await JS.InvokeVoidAsync("doSomething");
    }
}

Emits NTBA0006 for JS interop too early in the lifecycle

@rendermode InteractiveServer

@code {
    [Inject] private IJSRuntime JS { get; set; } = default!;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            await JS.InvokeVoidAsync("doSomething");
        }
        catch (Exception)
        {
            throw;
        }
    }
}

Does not emit NTBA0006 when interactivity is explicitly checked

@rendermode InteractiveServer

@code {
    [Inject] private IJSRuntime JS { get; set; } = default!;

    protected override async Task OnInitializedAsync()
    {
        if (RendererInfo.IsInteractive)
        {
            await JS.InvokeVoidAsync("doSomething");
        }
    }
}

Emits NTBA0008 for swallowed exceptions

@rendermode InteractiveServer

<button @onclick="HandleClick">Click</button>

@code {
    private void HandleClick()
    {
        try
        {
            Save();
        }
        catch (Exception)
        {
        }
    }
}

Emits NTBA0009 when root ErrorBoundary has no ErrorContent

@rendermode InteractiveServer

<ErrorBoundary>
    <button @onclick="HandleClick">Click</button>
</ErrorBoundary>

Build And Test

dotnet test NT.BlazorAnalyzer.slnx -v minimal
dotnet build NT.BlazorAnalyzer.slnx -c Release -v minimal
dotnet test NT.BlazorAnalyzer.slnx -v minimal -p:TestingPlatformCommandLineArguments="--coverage --coverage-output-format cobertura --coverage-output ./TestResults/coverage.cobertura.xml"

GitHub CI/CD

GitHub Actions files are under .github/workflows:

  • ci.yml: restores, builds, tests, and collects coverage on pull requests and pushes to main; after validation passes on main, it installs semantic-release with npm install --no-save and runs npx semantic-release
  • release.yml: runs when a v* tag is pushed, packs the analyzer using the tag version, and publishes the .nupkg and .snupkg to NuGet

Release configuration lives in .releaserc.json and uses:

  • @semantic-release/commit-analyzer
  • @semantic-release/release-notes-generator
  • @semantic-release/github creating the GitHub release with generated release notes

The release flow is intentionally split:

  • main push passes build/test, then semantic-release creates the GitHub release and v${version} tag on the validated commit
  • tag creation starts the package publish workflow
  • the tag version is passed as both the build version and NuGet package version

Required GitHub secrets:

  • SEMANTIC_RELEASE_TOKEN: GitHub token with repository contents write permission. This must be a PAT or equivalent token because tags created by the default GITHUB_TOKEN do not reliably trigger the tag-based publish workflow.
  • NUGET_API_KEY: NuGet.org API key with package push permissions

The NuGet package is published as a Roslyn analyzer package, so the analyzer assembly is placed under analyzers/dotnet/cs in the generated .nupkg.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.2 649 4/27/2026
1.2.1 108 4/25/2026
1.2.0 118 4/23/2026
1.1.0 107 4/16/2026
1.0.1 111 4/16/2026
1.0.0 105 4/15/2026

See the GitHub Releases page for version-specific release notes.