oxc-project/oxc · info · OxcDiagnostic

Unexpected access to `module.exports`.

Error message

Unexpected access to `module.exports`.

What it means

Diagnostic from oxlint rule node/exports-style (style category), the mirror of its sibling: when the rule is configured in 'exports' mode, the codebase standardizes on the short `exports` alias, and any reference to `module.exports` is flagged with the suggestion to use `exports`. Same rationale — one idiom per codebase — and same caveat: only safe because the other diagnostic in this rule forbids reassigning `exports` itself.

Source

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

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")]
enum ExportsStyleMode {
    #[default]
    #[serde(rename = "module.exports")]
    /// Requires `module.exports` and disallows `exports`
    ModuleExports,
    /// Requires `exports` and disallows `module.exports`

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Rewrite the access: 'module.exports.foo = 42' becomes 'exports.foo = 42'
  2. If the file must use module.exports (e.g. it reassigns the export target), keep it and scope the rule to mode "module.exports" for that directory, or disable inline
  3. Normalize the whole module to one form so both diagnostics stay silent

Example fix

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

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

Strategy: validation

Validate before calling

// .oxlintrc.json
"rules": { "node/exports-style": ["error", "exports"] }

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

Prevention

When it happens

Trigger: Config "node/exports-style": ["error", "exports"] (the ExportsStyleMode::Exports serde rename) and the source references the global `module.exports` — e.g. module.exports.foo = 42; — resolved through the same is_global_module_exports helper used for the sibling diagnostics.

Common situations: Codebases that adopted the shorter exports style (common in older Node libs) now linted with oxlint; mode flipped in shared config while some files still use the long form; migrations from eslint-plugin-n/exports-style with the same option value.

Related errors


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