ESLint 配置 .eslintrc.js 和 .eslintrc.cjs的区别

ESLint 配置 .eslintrc.js 和 .eslintrc.cjs的区别


.eslintrc.js.eslintrc.cjs 的主要区别在于 模块系统文件扩展名约定。以下是详细对比:


📊 核心区别对比表

特性 .eslintrc.js .eslintrc.cjs
模块系统 ES Modules (ESM) CommonJS (CJS)
Node.js 支持 Node.js ≥ 13.2.0(需配置) 所有 Node.js 版本
文件扩展名 .js .cjs
使用场景 现代项目、ESM 环境 传统项目、CJS 环境、混合项目
package.json 类型 "type": "module" 或无 "type": "commonjs" 或无
导出语法 export default module.exports

🔧 语法差异

.eslintrc.js (ES Modules)

javascript

复制代码
// 使用 ES Modules 语法
export default {
  env: {
    browser: true,
    es2021: true
  },
  extends: ['eslint:recommended'],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module'  // 注意:这里是代码的模块类型
  },
  rules: {
    'no-console': 'warn'
  }
};

// 或者使用动态配置
import someConfig from './some-config.js';

export default {
  extends: [someConfig],
  // ... 其他配置
};

.eslintrc.cjs (CommonJS)

javascript

复制代码
// 使用 CommonJS 语法
module.exports = {
  env: {
    browser: true,
    es2021: true
  },
  extends: ['eslint:recommended'],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module'  // 这里仍然可以是 'module'
  },
  rules: {
    'no-console': 'warn'
  }
};

// 或者使用动态配置
const someConfig = require('./some-config.cjs');

module.exports = {
  extends: [someConfig],
  // ... 其他配置
};

🎯 如何选择?

选择 .eslintrc.cjs 的情况

json

复制代码
// 如果你的项目有以下特征:
{
  "场景": [
    "Node.js < 13.2.0",
    "package.json 中有 'type': 'commonjs'",
    "package.json 中没有 type 字段(默认 CommonJS)",
    "项目使用 require() 导入",
    "传统项目或库",
    "需要兼容性"
  ]
}

选择 .eslintrc.js 的情况

json

复制代码
// 如果你的项目有以下特征:
{
  "场景": [
    "Node.js ≥ 13.2.0",
    "package.json 中有 'type': 'module'",
    "项目使用 import/export",
    "现代前端项目(Vite、现代打包工具)",
    "只支持 ESM 的依赖",
    "想要使用 ES Modules 特性"
  ]
}

⚠️ 常见问题与解决方案

问题1:Error [ERR_REQUIRE_ESM]

bash

复制代码
# 当 package.json 有 "type": "module" 时
# 但 ESLint 尝试用 require() 加载 .eslintrc.js

# 解决方案:重命名为 .eslintrc.cjs
mv .eslintrc.js .eslintrc.cjs

问题2:在 ESM 项目中使用 CJS 配置

javascript

复制代码
// 项目是 ESM,但想用 CJS 配置 ESLint
// 使用 .eslintrc.cjs 即可正常工作

// package.json
{
  "type": "module",  // 项目是 ESM
  "devDependencies": {
    "eslint": "^9.0.0"
  }
}

// .eslintrc.cjs  ← 使用 .cjs 扩展名
module.exports = {
  // CJS 语法
};

问题3:混合项目配置

javascript

复制代码
// 项目结构示例
project/
├── package.json           # "type": "module"
├── .eslintrc.cjs          # ESLint 配置(CJS)
├── src/
│   ├── app.js            # ESM 代码
│   └── legacy.cjs        # CJS 代码
└── lib/
    └── commonjs.cjs      # CJS 库代码

🔄 转换示例

从 CJS 转换到 ESM

javascript

复制代码
// 转换前: .eslintrc.js (被当作 CJS)
module.exports = {
  env: { node: true },
  extends: ['eslint:recommended']
};

// 转换后: .eslintrc.js (作为 ESM)
export default {
  env: { node: true },
  extends: ['eslint:recommended']
};

// 或者保持为 CJS,但重命名
// .eslintrc.js → .eslintrc.cjs

从 ESM 转换到 CJS

javascript

复制代码
// 转换前: .eslintrc.js (作为 ESM)
export default {
  env: { node: true },
  extends: ['eslint:recommended']
};

// 转换后: .eslintrc.cjs
module.exports = {
  env: { node: true },
  extends: ['eslint:recommended']
};

🛠️ ESLint 8+ 的配置检测逻辑

ESLint 按照以下顺序检测配置文件:

javascript

复制代码
// ESLint 配置检测流程
1. .eslintrc.js      // 尝试作为当前项目类型的模块加载
2. .eslintrc.cjs     // 总是作为 CommonJS 加载
3. .eslintrc.yaml
4. .eslintrc.yml
5. .eslintrc.json
6. package.json (eslintConfig 字段)

关键点 :如果 .eslintrc.js 加载失败(因为模块类型问题),ESLint 会尝试其他格式。


📁 实际项目示例

示例1:纯 ESM 项目(Vite/现代前端)

json

复制代码
// package.json
{
  "type": "module",
  "devDependencies": {
    "eslint": "^9.0.0"
  }
}

javascript

复制代码
// .eslintrc.js 或 .eslintrc.cjs 都可以
// 但推荐 .eslintrc.cjs 避免兼容性问题

// .eslintrc.cjs
module.exports = {
  env: { browser: true, es2021: true },
  extends: ['eslint:recommended'],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module'
  }
};

示例2:Node.js 后端项目

json

复制代码
// package.json
{
  // 没有 type 字段,默认 CommonJS
  "devDependencies": {
    "eslint": "^9.0.0"
  }
}

javascript

复制代码
// 使用 .eslintrc.js (CJS 语法)
module.exports = {
  env: { node: true },
  extends: ['eslint:recommended'],
  rules: {
    'no-console': 'off'
  }
};

示例3:混合模块项目

json

复制代码
// package.json
{
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/esm/index.js",
      "require": "./dist/cjs/index.js"
    }
  }
}

javascript

复制代码
// .eslintrc.cjs  ← 明确使用 CJS
module.exports = {
  overrides: [
    {
      files: ['src/**/*.js'],
      parserOptions: { sourceType: 'module' }
    },
    {
      files: ['lib/**/*.cjs'],
      parserOptions: { sourceType: 'script' }
    }
  ]
};

🎪 配置优先级与加载机制

优先级顺序(从高到低)

  1. .eslintrc.js(根据 package.json 的 type 决定模块类型)

  2. .eslintrc.cjs(总是 CJS)

  3. .eslintrc.yaml

  4. .eslintrc.yml

  5. .eslintrc.json

  6. package.json 中的 eslintConfig


ESLint 的内部判断逻辑

javascript

复制代码
// 伪代码:ESLint 如何选择配置文件
function loadConfig(filePath) {
  const ext = path.extname(filePath);
  
  if (ext === '.js') {
    // 根据 package.json 的 type 决定如何加载
    if (packageJson.type === 'module') {
      return import(filePath);  // ESM
    } else {
      return require(filePath); // CJS
    }
  }
  
  if (ext === '.cjs') {
    return require(filePath);  // 总是 CJS
  }
  
  // ... 其他格式
}

最佳实践建议

1. 推荐使用 .eslintrc.cjs

bash

复制代码
# 无论项目类型,.eslintrc.cjs 都是安全的选择
# 因为:
# - 明确的 CommonJS 标识
# - 兼容所有 Node.js 版本
# - 避免模块类型混淆
mv .eslintrc.js .eslintrc.cjs

2. 明确项目模块类型

json

复制代码
// 在 package.json 中明确声明
{
  "name": "my-project",
  "type": "module",  // 或省略(默认 CommonJS)
  "devDependencies": {
    "eslint": "^9.0.0"
  }
}

3. 配置示例模板

javascript

复制代码
// .eslintrc.cjs(通用模板)
module.exports = {
  root: true,
  env: {
    browser: true,
    node: true,
    es2021: true
  },
  extends: ['eslint:recommended'],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module'  // 这里指解析的代码类型
  },
  rules: {
    // 你的规则
  }
};

4. 调试配置加载

bash

复制代码
# 查看 ESLint 如何解析配置
npx eslint --debug-config

# 或使用 Node.js 调试
NODE_OPTIONS='--loader eslint-flat-config-utils' npx eslint .

🚨 常见错误与解决

错误1:Cannot use import statement outside a module

bash

复制代码
# 原因:.eslintrc.js 在 CJS 项目中被当作 ESM
# 解决:
rm .eslintrc.js
echo 'module.exports = {}' > .eslintrc.cjs

错误2:ReferenceError: module is not defined

bash

复制代码
# 原因:.eslintrc.cjs 在 ESM 环境中被错误加载
# 解决:确保文件名是 .cjs,不是 .js

错误3:配置不生效

bash

复制代码
# 检查配置加载顺序
npx eslint --print-config src/index.js

# 或使用调试模式
npx eslint --debug src/

📦 工具推荐

检测模块类型

bash

复制代码
# 检查当前文件的模块类型
node -p "require('fs').readFileSync('package.json', 'utf8')" | grep type

# 或使用工具
npm install -g check-esm
check-esm .

自动转换

bash

复制代码
# 将 .eslintrc.js 转换为 .eslintrc.cjs
# (手动重命名并修改导出语法)
mv .eslintrc.js .eslintrc.cjs
# 然后确保使用 module.exports

总结

对于大多数项目,推荐使用 .eslintrc.cjs,因为它明确标识为 CommonJS 模块,避免因 Node.js 版本或项目配置导致的模块系统混淆问题。

只有在纯 ESM 项目且明确了解模块系统时,才使用 .eslintrc.js