oxc-project/oxc · warning · OxcDiagnostic
Exporting mutable '{kind_str}' binding, use 'const' instead.
Error message
Exporting mutable '{kind_str}' binding, use 'const' instead. What it means
Diagnostic from the oxlint rule import/no-mutable-exports (style category). It fires when a variable declaration with kind `let` or `var` is exported (`export let x` / `export var x`). Mutable export bindings are confusing: consumers see live bindings whose value can change at any time, and bundlers/CJS interop handle mutation inconsistently, so the rule demands immutable `const` exports. The message names the exact kind (var vs let) and the help repeats it in the fix suggestion.
Source
Thrown at crates/oxc_linter/src/rules/import/no_mutable_exports.rs:16
use oxc_ast::{
AstKind,
ast::{
Declaration, Expression, ModuleExportName, VariableDeclaration, VariableDeclarationKind,
},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::ReferenceId;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule};
fn no_mutable_exports_diagnostic(span: Span, kind: VariableDeclarationKind) -> OxcDiagnostic {
let kind_str = if kind == VariableDeclarationKind::Var { "var" } else { "let" };
OxcDiagnostic::warn(format!("Exporting mutable '{kind_str}' binding, use 'const' instead."))
.with_help(format!("Replace '{kind_str}' with 'const' to export an immutable binding."))
.with_label(span)
}
// <https://github.com/import-js/eslint-plugin-import/blob/v2.31.0/docs/rules/no-mutable-exports.md>
#[derive(Debug, Default, Clone)]
pub struct NoMutableExports;
declare_oxc_lint!(
/// ### What it does
///
/// Forbids the use of mutable exports with var or let.
///
/// ### Why is this bad?
///
/// In general, we should always export constants
///
/// ### ExamplesView on GitHub (pinned to e1e7af627c)
Solutions
- Change the declaration to `const` when the binding is never reassigned (mutating object properties is still fine)
- If mutation is the point, export a getter or functions instead: `export const getState = () => state` or export an object with methods
- Export an immutable snapshot and keep mutation internal to the module
Example fix
// before
export let config = { debug: false };
// after
export const config = { debug: false }; // mutate properties, never the binding Defensive patterns
Strategy: validation
Validate before calling
// rg -n "^export\\s+(let|var)\\s" src
// .oxlintrc.json: { "rules": { "import/no-mutable-exports": "warn" } } Prevention
- Export const bindings; mutate object internals, never the binding
- Model changing state with exported functions/getters instead of reassigning exports
- Remember CJS consumers and some bundlers snapshot export values — mutation is not portable
When it happens
Trigger: `export let counter = 0;` or `export var config = {};` — exported VariableDeclarations whose kind is not Const. Function and class exports are not flagged. Emitted from no_mutable_exports_diagnostic at crates/oxc_linter/src/rules/import/no_mutable_exports.rs:16, which maps Var → "var" and everything else (let) → "let".
Common situations: State exported from modules (counters, caches, feature flags) that authors expected to mutate; converting CJS `module.exports.x = let-variable` to ESM; interop with bundlers that snapshot export values, causing the mutation to be invisible downstream.
Related errors
- Prefer default export on a file with single export.
- Prefer default export to be present on every file that has e
- Tried to get an allocator from an empty `FixedSizeAllocatorP
- Expected '{curr_kind}' syntax before '{prev_kind}' syntax.
- Imports should be sorted alphabetically.
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/caf2696bc7bbbc95.
Report an issue: GitHub.