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
- Rewrite the access: 'exports.foo = 42' becomes 'module.exports.foo = 42'
- 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)
- 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
- Decide the project's CommonJS export idiom once in the team style guide and encode it as the rule's mode
- Use overrides for vendored/legacy directories so mixed-style code doesn't erode the standard
- Codemod before enforcing: flip stray exports.x to module.exports.x in one commit, then turn the rule on
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
- Unexpected access to `module.exports`.
- Unexpected assignment to `exports`.
- Unexpected require().
- Unexpected assignment to 'exports'.
- Do not mix 'require' and other declarations.
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/8dcfd933ff7c350d.
Report an issue: GitHub.