Skip to content

ESLint Plugins

@studnicky/eslint-config ships two custom ESLint plugins:

  • @studnicky — 22 structural and semantic rules that enforce the substrate codebase doctrine.
  • @studnicky/v8 — 27 rules for V8 optimization-sensitive code and the related constructs the codebase constrains consistently.

Register both plugins in your flat config to enable the rules.

Install

Add the GitHub Packages registry to .npmrc:

@studnicky:registry=https://npm.pkg.github.com

Then install the package:

sh
pnpm add -D @studnicky/eslint-config

Install peer dependencies:

sh
pnpm add -D eslint@>=10 typescript-eslint@>=8 @typescript-eslint/eslint-plugin@>=8 @typescript-eslint/parser@>=8 @stylistic/eslint-plugin@>=5 eslint-plugin-import-x@>=4 eslint-plugin-perfectionist@>=5 eslint-plugin-regexp@>=3 eslint-plugin-unused-imports@>=4 typescript@>=6

Public API

The package root exports plugin, v8Plugin, entitySuite, hygieneSuite, v8Suite, and HexagonalSuite. Individual rule implementations stay on their plugin objects: use plugin.rules['single-export'] or v8Plugin.rules['delete-property'] when programmatic rule access is required.

Suites are opt-in

A suite is a flat-config entry bundling one domain's rules at error. Spreading a suite is a deliberate choice to adopt that whole domain; registering plugin alone enables nothing.

SuiteDomain
entitySuiteEntity and data-shape conventions — all-types-are-entities, folder-content-shape, interface-must-be-contract, interface-suffix, interfaces-compose-named-types, no-mixed-callable-shapes, type-alias-invariants, whole-canonical-types
hygieneSuiteGeneral code hygiene — canonical-export-names, clean-diagnostics, descriptive-identifiers, direct-invocation-only, hash-private-fields, inline-trivial-logic, lexical-this-only, prefer-collection-types, require-options-object, single-export, static-method-verbs
v8SuiteV8 performance rules — all 27 rules in the V8 rules table below
HexagonalSuiteHexagonal-architecture import boundaries — adapter-only-import, domain-purity, known-types-outside-adapters, layer-import-boundary. A factory, not a static config: call HexagonalSuite.create(...) with the shared layer config, since all four rules take distinct extra options on top of a common layers/sourceRoot shape.

Enable individual rules instead when a domain's conventions do not apply. type-alias-invariants governs how a type alias establishes schema provenance and stands on its own; all-types-are-entities additionally requires every canonical alias to be the exported Type member of an *Entity namespace, which is a convention a consumer adopts by enabling entitySuite, not a prerequisite for the other rules.

js
// eslint.config.mjs — one rule, without the entity conventions
import { plugin } from '@studnicky/eslint-config';

export default [
  {
    plugins: { '@studnicky': plugin },
    rules: { '@studnicky/type-alias-invariants': 'error' }
  }
];

Usage

Import plugin and v8Plugin and register them in a flat-config entry:

js
// eslint.config.mjs
import { plugin, v8Plugin } from '@studnicky/eslint-config';

export default [
  {
    plugins: { '@studnicky': plugin, '@studnicky/v8': v8Plugin },
    rules: {
      '@studnicky/type-alias-invariants': 'error',
      '@studnicky/v8/array-spread-outside-loops': 'error'
    }
  }
];

Combine with additional rules in the same entry:

js
// eslint.config.mjs
import { plugin, v8Plugin } from '@studnicky/eslint-config';

export default [
  {
    plugins: { '@studnicky': plugin, '@studnicky/v8': v8Plugin },
    rules: {
      '@studnicky/type-alias-invariants': 'error',
      '@studnicky/v8/array-spread-outside-loops': 'error',
      'no-console': 'warn'
    }
  }
];

Using the plugins directly

Import the raw plugin objects for hand-rolled flat config:

ts
// eslint.config.ts
import { plugin, v8Plugin } from '@studnicky/eslint-config';

export default [
  {
    plugins: {
      '@studnicky': plugin,
      '@studnicky/v8': v8Plugin
    },
    rules: {
      '@studnicky/single-export': 'error',
      '@studnicky/v8/delete-property': 'error'
    }
  }
];

Configuration rules

26 rules that enforce structural, semantic, and stylistic constraints.

RuleFixableSeverity
@studnicky/adapter-only-importNoerror
@studnicky/all-types-are-entitiesNoerror
@studnicky/canonical-export-namesNoerror
@studnicky/clean-diagnosticsYeserror
@studnicky/descriptive-identifiersNoerror
@studnicky/direct-invocation-onlyNoerror
@studnicky/domain-purityNoerror
@studnicky/explicit-return-bindingNoerror
@studnicky/folder-content-shapeNoerror
@studnicky/hash-private-fieldsNoerror
@studnicky/inline-trivial-logicYeserror
@studnicky/intake-parse-onlyNoerror
@studnicky/interface-must-be-contractYeserror
@studnicky/interface-suffixNoerror
@studnicky/interfaces-compose-named-typesNoerror
@studnicky/known-types-outside-adaptersNoerror
@studnicky/layer-import-boundaryNoerror
@studnicky/lexical-this-onlyNoerror
@studnicky/no-mixed-callable-shapesNoerror
@studnicky/no-unparsed-assertionNoerror
@studnicky/prefer-collection-typesNowarn
@studnicky/require-options-objectNoerror
@studnicky/single-exportNoerror
@studnicky/static-method-verbsNoerror
@studnicky/type-alias-invariantsPartialerror
@studnicky/whole-canonical-typesNoerror

@studnicky/explicit-return-binding, @studnicky/intake-parse-only, and @studnicky/no-unparsed-assertion are not bundled into any suite above — the latter two share the same exemptPackages boundary-package list (parsing primitives and the compile engine every intake is built from). All three are adopted individually, alongside whichever suites a consumer chooses.

V8 rules

27 rules covering V8 optimization-sensitive allocation, object-shape, iteration, and dynamic-code patterns, alongside related source constraints where measurement does not establish a V8 cost.

RuleFixableSeverity
@studnicky/v8/arguments-objectNoerror
@studnicky/v8/array-concat-outside-loopsNoerror
@studnicky/v8/array-from-iteratorsNoerror
@studnicky/v8/array-from-map-callbackNoerror
@studnicky/v8/array-scan-outside-loopsNoerror
@studnicky/v8/array-splice-outside-loopsNoerror
@studnicky/v8/array-spread-outside-loopsNoerror
@studnicky/v8/chained-array-iterationNoerror
@studnicky/v8/computed-class-propertiesNoerror
@studnicky/v8/computed-object-propertiesNoerror
@studnicky/v8/conditional-property-assignmentNoerror
@studnicky/v8/define-propertyNoerror
@studnicky/v8/delete-propertyNoerror
@studnicky/v8/dynamic-property-accessNoerror
@studnicky/v8/eval-functionNoerror
@studnicky/v8/for-in-loopsNoerror
@studnicky/v8/for-of-arraysNoerror
@studnicky/v8/inline-arrow-functionsNoerror
@studnicky/v8/inline-functionsNoerror
@studnicky/v8/max-switch-casesNoerror
@studnicky/v8/memoize-array-lengthNoerror
@studnicky/v8/object-spreadNoerror
@studnicky/v8/prototype-modificationNoerror
@studnicky/v8/regexp-in-loopsNoerror
@studnicky/v8/switch-statementsNoerror
@studnicky/v8/try-catch-in-loopsNoerror
@studnicky/v8/with-statementNoerror