npm package: @stride.it/appoint-lint-governance
Description: Shareable ESLint flat-config profiles (MVP: TypeScript) for normalizing lint across repos.
Latest version: 0.1.33

# @stride.it/appoint-lint-governance

[![npm version](https://img.shields.io/npm/v/@stride.it/appoint-lint-governance.svg)](https://www.npmjs.com/package/@stride.it/appoint-lint-governance)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
[![ESLint](https://img.shields.io/badge/ESLint-9.0-purple.svg)](https://eslint.org/)

Shareable ESLint v9 flat-config builders to normalize linting across repositories.

This package provides two domains:

- **TypeScript domain**: framework-agnostic TypeScript quality gates (correctness, imports hygiene, security, maintainability, and optional type-aware checks).
- **Framework domain**: React + Next.js + Accessibility (a11y) presets.

Everything is designed to be composed in consumer repos (flat config is order-dependent); this is not an all-or-nothing mega-config.

## What you get

- Composable flat-config builders (arrays of ESLint config objects).
- A minimal baseline for fast adoption.
- A recommended preset that composes the major rule groups.
- Optional type-aware rules (requires a TSConfig project).
- Optional Prettier interop preset to disable formatting-rule conflicts.

Non-goals:

- No test-runner targeting (Vitest/Jest/Playwright rules are out of scope for now).
- No formatting (Prettier remains the formatter; ESLint formatting conflicts are disabled via the interop preset).

## Install

In a consumer repo:

```bash
pnpm add -D @stride.it/appoint-lint-governance eslint
```

ESLint is a peer dependency; your repo owns the ESLint version.

## Quick start (ESLint v9 flat config)

Create (or edit) `eslint.config.mjs`:

```js
import { typescriptMinimal } from "@stride.it/appoint-lint-governance";

export default [...typescriptMinimal()];
```

Run ESLint in your repo as usual:

```bash
pnpm lint
```

## Public API

These builders return arrays you spread into your `eslint.config.*`:

- `typescriptBase(options?)`\
  Baseline correctness + safe defaults (files globs, languageOptions).
- `typescriptMinimal(options?)`\
  Minimal preset (fast adoption baseline).
- `typescriptRecommended(options?)`\
  Recommended preset that composes core groups and can optionally enable type-aware rules.
- `typescriptPrettierInterop(options?)`\
  Disables formatting-rule conflicts when the consumer repo uses Prettier.

Framework domain builders/presets:

- `react(options?)`, `reactRecommended`
- `a11y(options?)`, `a11yRecommended`
- `nextjs(options?)`, `nextjsRecommended` (composes React + A11y + Next rules)

Additionally:

- `plugin`\
  Optional plugin export used by internal configs.

## Recommended preset (most repos)

```js
import { typescriptRecommended } from "@stride.it/appoint-lint-governance";

export default [...typescriptRecommended()];
```

### Type-aware linting (optional)

Type-aware rules can catch issues ESLint cannot detect from syntax alone (for example: misused promises, unsafe operations). They require a TSConfig project.

```js
import { typescriptRecommended } from "@stride.it/appoint-lint-governance";

export default [
  ...typescriptRecommended({
    typeChecked: true,
    tsconfigPath: "./tsconfig.json",
    tsconfigRootDir: import.meta.dirname,
    // Optional: fail CI on deprecated APIs surfaced by type definitions
    reportDeprecated: true,
  }),
];
```

If your Node.js version does not support `import.meta.dirname`, use this portable alternative:

```js
import { fileURLToPath } from "node:url";

const tsconfigRootDir = fileURLToPath(new URL(".", import.meta.url));
```

Then pass `tsconfigRootDir` into `typescriptRecommended({ ... })`.

## Framework domain examples (React / A11y / Next.js)

### A) Domain linting (simple): Next.js preset

Use this when you want “framework linting” as a single preset. This includes **React + A11y + Next.js** rules.

```js
import { nextjsRecommended, typescriptRecommended } from "@stride.it/appoint-lint-governance";

export default [
  ...typescriptRecommended({
    typeChecked: true,
    tsconfigPath: "./tsconfig.json",
    tsconfigRootDir: import.meta.dirname,
  }),
  ...nextjsRecommended,
];
```

### B) Domain linting (explicit): one line per domain

Use this when you want React / A11y / Next.js as separate “domain lines”.

Important: do **not** use `nextjsRecommended` together with `reactRecommended` and `a11yRecommended` (it would duplicate rules).

```js
import {
  a11yRecommended,
  nextjs,
  reactRecommended,
  typescriptRecommended,
} from "@stride.it/appoint-lint-governance";

export default [
  ...typescriptRecommended({
    typeChecked: true,
    tsconfigPath: "./tsconfig.json",
    tsconfigRootDir: import.meta.dirname,
  }),
  ...reactRecommended,
  ...a11yRecommended,
  ...nextjs(),
];
```

### C) Running domains independently (separate configs)

Flat config is a single array, but you can run “per-domain” by creating multiple config files and selecting them with `eslint --config`.

Example scripts:

```json
{
  "scripts": {
    "lint:ts": "eslint --config ./eslint.typescript-only.config.mjs \"src/**/*.{ts,tsx}\"",
    "lint:react": "eslint --config ./eslint.react-only.config.mjs \"src/**/*.{tsx,jsx}\"",
    "lint:a11y": "eslint --config ./eslint.a11y-only.config.mjs \"src/**/*.{tsx,jsx}\"",
    "lint:next": "eslint --config ./eslint.nextjs-only.config.mjs \"src/**/*.{tsx,jsx}\""
  }
}
```

## Prettier interop (optional)

If your project uses Prettier, include the interop preset last so it can override earlier formatting rules:

```js
import {
  typescriptPrettierInterop,
  typescriptRecommended,
} from "@stride.it/appoint-lint-governance";

export default [
  ...typescriptRecommended(),
  ...typescriptPrettierInterop(),
];
```

## Overriding rules in consumer repos

Flat config is order-dependent: later entries win. Consumers can override by appending their own config object after spreading a preset.

If your organization wants to prevent overrides, ESLint itself cannot lock consumer rules; enforce that with CI/policy checks on the consumer repo’s `eslint.config.*`.

## Suggested companion quality gates (outside ESLint)

This package intentionally focuses on ESLint rules. Many teams also enforce these CI gates:

- Type checking: `tsc --noEmit`
- Dependency audit: `pnpm audit` (or your org-approved alternative)
- Secret scanning and SAST (org-dependent)

## Development (this repo)

```bash
pnpm install
pnpm test
pnpm build
```

To validate the publish output locally:

```bash
npm pack
```

Then install the generated `.tgz` into a consumer repo.

## Publishing

1) Bump version:

```bash
pnpm version patch
```

1) Dry run:

```bash
pnpm publish --dry-run
```

1) Publish (requires npm credentials with access to the `@stride.it` scope):

```bash
pnpm publish --access public
```