oxc-project/oxc · info · OxcDiagnostic

Unexpected access to `exports`.

Error message

Unexpected access to `exports`.

What it means

Diagnostic from oxlint rule node/exports-style (style category). The rule enforces one CommonJS export idiom per codebase; in the default 'module.exports' mode, any access to the bare global `exports` identifier is flagged with the suggestion to use module.exports. This is purely stylistic — `exports.foo = 1` and `module.exports.foo = 1` behave identically as long as `exports` itself is never reassigned — but mixing the two forms across a module or team hurts grep-ability.

Source

Thrown at crates/oxc_linter/src/rules/node/exports_style.rs:21

    ast::{AssignmentExpression, Expression},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{NodeId, ReferenceId};
use oxc_span::{GetSpan, Span};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
    ast_util::iter_outer_expressions,
    config::GlobalValue,
    context::LintContext,
    rule::{Rule, TupleRuleConfig},
    utils::{is_global_exports_assignment_target, is_global_module_exports},
};

fn unexpected_exports_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected access to `exports`.")
        .with_help("Use `module.exports` instead.")
        .with_label(span)
}

fn unexpected_module_exports_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected access to `module.exports`.")
        .with_help("Use `exports` instead.")
        .with_label(span)
}

fn unexpected_assignment_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected assignment to `exports`.")
        .with_help("Do not modify `exports` itself.")
        .with_label(span)
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Rewrite the access: 'exports.foo = 42' becomes 'module.exports.foo = 42'
  2. If the file or subdirectory deliberately uses the exports style, switch the rule mode: "node/exports-style": ["error", "exports"] (that flips the rule to flag module.exports instead)
  3. Scope the rule per directory via overrides so legacy exports-style folders are exempt

Example fix

// before (mode: module.exports)
exports.hello = function () {};

// after
module.exports.hello = function () {};
Defensive patterns

Strategy: validation

Validate before calling

// .oxlintrc.json — pick ONE mode and apply it repo-wide
"rules": { "node/exports-style": ["error", "module.exports"] }

npx oxlint -c .oxlintrc.json --deny-warning .

Prevention

When it happens

Trigger: The rule is enabled in its default mode (config "node/exports-style": "error" with no option, or "module.exports") and the source contains an identifier reference that resolves to the module-scope global `exports` (detected via the shared is_global_module_exports util) — e.g. exports.foo = 42; or const e = exports;. A local variable shadowing the name `exports` does not trigger the util.

Common situations: Teams adopting a shared oxlint config that standardizes on module.exports; files copied in from a project that used the exports style; refactors that introduce a stray exports.x next to existing module.exports assignments.

Related errors


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