Tamp.Eslint.V9 0.1.1

Prefix Reserved
dotnet add package Tamp.Eslint.V9 --version 0.1.1
                    
NuGet\Install-Package Tamp.Eslint.V9 -Version 0.1.1
                    
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="Tamp.Eslint.V9" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tamp.Eslint.V9" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="Tamp.Eslint.V9" />
                    
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 Tamp.Eslint.V9 --version 0.1.1
                    
#r "nuget: Tamp.Eslint.V9, 0.1.1"
                    
#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 Tamp.Eslint.V9@0.1.1
                    
#: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=Tamp.Eslint.V9&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=Tamp.Eslint.V9&version=0.1.1
                    
Install as a Cake Tool

Tamp.Eslint.V9

Tamp wrapper for the ESLint v9 CLI. Pattern + style + best-practice linter for TypeScript and JavaScript. Emits SARIF via @microsoft/eslint-formatter-sarif for the Tamp.Sarif ingest chain. Smart binary resolution: project-local → pnpm exec → npm exec → global PATH → pre-flight skip.

Package Status
Tamp.Eslint.V9 0.1.0 (initial)

Install

dotnet add package Tamp.Eslint.V9

Multi-targets net8 / net9 / net10. The wrapper itself is .NET; it shells out to the ESLint CLI which lives in your project (or globally). See Binary resolution for how the wrapper finds it.

Why this exists (and what the existing scanners miss)

The Tamp security pipeline already covers C# via Roslyn analyzers (semantic) and ReSharper InspectCode (style + best-practice). For pattern-based security scanning of TS/JS, Tamp.OpenGrep covers the security-rule families (p/typescript, p/security-audit, p/owasp-top-ten, etc.).

What's still missing for TypeScript / JavaScript codebases: the equivalent of "ReSharper for C#" — a linter that flags style + best-practice issues (unused vars, missing useEffect deps, prefer-const, no-explicit-any, unstable hook ordering, etc.). That's ESLint's domain, and that's what this wrapper enables.

Quick start

using Tamp;
using Tamp.Eslint.V9;

[Solution] readonly Solution Solution = null!;
AbsolutePath WebRoot => RootDirectory / "web";

Target SecurityScanEslint => _ => _
    .Description("TS/JS style + best-practice via ESLint v9.")
    .Executes(() =>
    {
        if (!EslintBinaryResolver.IsAvailable(WebRoot))
        {
            Log.Info("[security] ESLint skipped — no install found at {Dir}", WebRoot);
            return;
        }

        var plan = Eslint.Scan(s => s
            .SetWorkingDirectory(WebRoot)
            .AddTarget("src")
            .AddTarget("tests")
            .SetSarif()
            .SetOutputFile(RootDirectory / "artifacts" / "security" / "eslint.sarif")
            .SetMaxWarnings(0)
            .SetQuiet());

        var exit = ProcessRunner.Execute(plan);
        // ESLint: 0 = clean, 1 = findings, 2+ = tool/config error.
        if (exit > 1) throw new InvalidOperationException($"ESLint failed with exit {exit}.");
    });

Direct invocation (when you know ESLint is installed)

var plan = Eslint.Scan(s => s
    .AddTarget("web/src")
    .SetSarif()
    .SetOutputFile("artifacts/security/eslint.sarif")
    .SetWorkingDirectory("web")
    .SetQuiet());

ProcessRunner.Execute(plan);

Verb surface (v1)

Verb Wraps Required
Scan eslint <flags> <targets> At least one AddTarget(...); OutputFile required when Sarif is true

Out of scope for v1 (file as follow-up tickets if adopters ask):

  • --fix / autofix — mutating source from a security-scan wrapper is a foot-gun; explicit opt-in only.
  • Caching flags (--cache, --cache-location) — adopter-side concern.
  • --rulesdir — non-flat-config legacy mechanism.
  • Other formatters beyond the SARIF + arbitrary string override.

Binary resolution

ESLint isn't shipped through dotnet tool or any Tamp-native install attribute. The wrapper resolves the binary at Scan(...) time in priority order:

  1. Project-local — {WorkingDirectory}/node_modules/.bin/eslint (or eslint.cmd on Windows). Preferred because the project's exact pinned ESLint + plugin versions run.
  2. pnpm exec — pnpm exec eslint -- .... Used when pnpm-lock.yaml or pnpm-workspace.yaml is present at WorkingDirectory AND pnpm is on PATH.
  3. npm exec — npm exec eslint -- .... Used when package-lock.json is present at WorkingDirectory AND npm is on PATH.
  4. Global — eslint on PATH.
  5. Not found — ToCommandPlan() throws InvalidOperationException with an actionable message.

Pre-flighting (skip when not installed)

if (!EslintBinaryResolver.IsAvailable(workingDirectory))
{
    // Log and skip the target — same posture as the rest of the security pipeline.
    return;
}

Overriding resolution explicitly

var binary = new EslintBinaryResolution
{
    Executable = "/opt/ci/eslint/9.30.0/eslint",
    Source = EslintResolutionSource.Explicit,
};

var plan = Eslint.Scan(s => s
    .SetBinary(binary)
    .AddTarget("src"));

SARIF integration

Set SetSarif(true) to emit SARIF 2.1.0 via @microsoft/eslint-formatter-sarif. The formatter is a separate npm package that must be present in the project's devDependencies:

# In your web project root:
pnpm add -D @microsoft/eslint-formatter-sarif
# or
npm install -D @microsoft/eslint-formatter-sarif

The wrapper just emits --format @microsoft/eslint-formatter-sarif --output-file <path>. The Tamp.Sarif reader ingests the produced SARIF unchanged.

Exit-code semantics

ESLint follows the standard linter convention:

Exit Meaning Treat as
0 Clean run, no findings success
1 Findings reported success (findings are still a successful scan)
2+ Tool or config error failure

A security-pipeline target should only throw on exit > 1. This mirrors the Tamp.OpenGrep exit-code convention.

Settings authoring — fluent or object-init

Both produce identical CommandPlans; fluent is canonical in docs.

// Fluent
Eslint.Scan(s => s
    .AddTarget("src")
    .SetSarif()
    .SetOutputFile("out.sarif"));

// Object-init
var settings = new EslintScanSettings
{
    Sarif = true,
    OutputFile = "out.sarif",
};
settings.Targets.Add("src");
Eslint.Scan(settings);

Minimal ESLint v9 flat-config example for TypeScript + React

For reference — adopters typically already have an eslint.config.js. If you don't:

// eslint.config.js — at the root of your web/ project
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    files: ['**/*.{ts,tsx}'],
    plugins: {
      react,
      'react-hooks': reactHooks,
    },
    rules: {
      ...react.configs.recommended.rules,
      ...reactHooks.configs.recommended.rules,
      'react/react-in-jsx-scope': 'off',
    },
    settings: { react: { version: 'detect' } },
  },
];

devDependencies:

pnpm add -D eslint @eslint/js typescript-eslint \
            eslint-plugin-react eslint-plugin-react-hooks \
            @microsoft/eslint-formatter-sarif

Why not Biome / OXLint instead

Both are faster and ergonomically nicer. Open to either as additional satellites later (Tamp.Biome, Tamp.OxLint). ESLint is the incumbent with the largest rule ecosystem (@typescript-eslint, eslint-plugin-react, thousands of community plugins) — best baseline for first coverage.

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
0.1.1 88 9/27/2026
0.1.0 144 5/24/2026