oxc-project/oxc · warning

File has too many classes ({total}). Maximum allowed is {max

Error message

File has too many classes ({total}). Maximum allowed is {max}

What it means

Diagnostic of the `max-classes-per-file` rule. It counts class declarations (and by default class expressions too) in a single file and reports when the count exceeds the configured maximum, with default `max: 1` (crates/oxc_linter/src/rules/eslint/max_classes_per_file.rs:38-41). The span labels the class that pushed the file over the limit. The intent is one-responsibility-per-file cohesion.

Source

Thrown at crates/oxc_linter/src/rules/eslint/max_classes_per_file.rs:13

use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{
    context::LintContext,
    rule::{DefaultRuleConfig, Rule},
};

fn max_classes_per_file_diagnostic(total: u32, max: u32, span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!("File has too many classes ({total}). Maximum allowed is {max}"))
        .with_help("Reduce the number of classes in this file")
        .with_label(span)
}

#[derive(Debug, Default, Clone, Deserialize)]
pub struct MaxClassesPerFile(Box<MaxClassesPerFileConfig>);

#[derive(Debug, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct MaxClassesPerFileConfig {
    /// The maximum number of classes allowed per file.
    pub max: u32,
    /// Whether to ignore class expressions when counting classes.
    pub ignore_expressions: bool,
}

impl std::ops::Deref for MaxClassesPerFile {
    type Target = MaxClassesPerFileConfig;

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Split the file so each class lives in its own module (matches the rule's intent and default max of 1).
  2. If multiple related classes legitimately belong together (e.g. AST node types, DTOs), raise the limit: `{ "max": 5 }`.
  3. Set `ignoreExpressions: true` when class expressions are used for anonymous/one-off implementations such as HOCs or dynamic classes.

Example fix

// before (file: shapes.js)
class Circle { /* ... */ }
class Square { /* ... */ }

// after: shapes/circle.js -> export class Circle {...}
//        shapes/square.js -> export class Square {...}
Defensive patterns

Strategy: validation

Validate before calling

// CI gate before merge:
// oxlint --rule max-classes-per-file='{"max":1}' src/
// For mixed modules: {"max": 3, "ignoreExpressions": true}

Prevention

When it happens

Trigger: A file containing two or more classes with default config: `class A {} class B {}` triggers immediately. Any `class ... {}` declaration increments the counter; class expressions (`const A = class {}`) also count unless `ignoreExpressions: true` is set. Config `{ "max-classes-per-file": ["error", { "max": 3, "ignoreExpressions": true }] }` changes the thresholds.

Common situations: Bundling helper/mock classes or test doubles into one test file; exporting multiple small value-object classes from a barrel-ish module; migrating from ESLint where the rule was enabled with default max 1; refactoring that temporarily introduces an extra class during extraction.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/1ee842307af10910. Report an issue: GitHub.