oxc-project/oxc · warning · OxcDiagnostic
Expected {name} instead of {actual}
Error message
Expected {name} instead of {actual} What it means
Diagnostic from the oxlint rule import/no-commonjs (restriction category). It enforces ESM syntax and reports CommonJS usage: `require()` calls produce "Expected import instead of require" and `module.exports`/`exports.*` usage produces "Expected export instead of exports". Options: `allowRequire` (default false), `allowPrimitiveModules` (default false), and `allowConditionalRequire` (default true — require inside if/try/logical/ternary is allowed).
Source
Thrown at crates/oxc_linter/src/rules/import/no_commonjs.rs:19
use oxc_ast::{
AstKind,
ast::{Argument, Expression},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use oxc_str::static_ident;
use schemars::JsonSchema;
use serde::Deserialize;
use crate::{
AstNode,
context::LintContext,
rule::{DefaultRuleConfig, Rule},
};
fn no_commonjs_diagnostic(span: Span, name: &str, actual: &str) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("Expected {name} instead of {actual}"))
.with_help("Do not use CommonJS `require` calls and `module.exports` or `exports.*`")
.with_label(span)
}
#[derive(Debug, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct NoCommonjs {
/// If `allowPrimitiveModules` option is set to true, the following is valid:
///
/// ```js
/// module.exports = "foo";
/// module.exports = function rule(context) {
/// return { /* ... */ };
/// };
/// ```
///
/// but this is still reported:
///View on GitHub (pinned to e1e7af627c)
Solutions
- Convert the file to ESM: `require(x)` → `import ... from "x"`, `module.exports = v` → `export default v`, `exports.k = v` → `export const k = v`
- Scope the rule off for genuinely-CJS files via config overrides (e.g. *.cjs, *.config.js) instead of editing the code
- If requires must stay, relax with options: `{ "allowRequire": true, "allowPrimitiveModules": true }`
- Keep intentional lazy loads inside if/try blocks, which `allowConditionalRequire` (default true) already permits
Example fix
// before
var mod = require("./mod");
module.exports = { hello: function () { return "Hello"; } };
// after
import mod from "./mod";
export function hello() { return "Hello"; } Defensive patterns
Strategy: validation
Validate before calling
// fail fast in CI before lint: refuse new CJS in ESM sources
// package.json script: "check:cjs": "node -e \"const g=require('child_process').execSync;const r=g('rg -l \\"module.exports|require\\" src --type js -g \\"!*.cjs\\" || true').toString().trim();if(r){console.error('CJS found:\\n'+r);process.exit(1)}\"" Prevention
- Set "type": "module" in package.json early and convert entry points first
- Keep genuinely-CJS files (*.cjs, jest.config.js) out of the rule via config overrides
- Use `import type`/ESM in new code and put lazy loads inside if/try so allowConditionalRequire covers them
When it happens
Trigger: Top-level `var mod = require("fs")`; `module.exports = {...}` or `module.exports = "Hola"` or `exports.sayHello = function(){}` (when the member expression object is exactly the identifier `module` with property `exports`). Conditional requires inside `if`/`try`/`&&`/ternary pass by default. Emitted from no_commonjs_diagnostic at crates/oxc_linter/src/rules/import/no_commonjs.rs:19.
Common situations: Migrating a CommonJS Node project to ESM ("type": "module") and cleaning up leftovers; mixed codebases where config files (jest.config.js, .eslintrc.js, gulpfile) are still CJS; linting scripts that reuse browser-incompatible CJS patterns.
Related errors
- Exporting mutable '{kind_str}' binding, use 'const' instead.
- This module could be mistakenly parsed as script instead of
- Expected "import" statement instead of "require" call
- Could not find the reuseWorker option in ${path}
- {error_message:?}
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/4983d39b15637327.
Report an issue: GitHub.