ESLint 09 🛡️ Language Options — ecmaVersion, sourceType, and globals
The languageOptions key in the flat config controls how ESLint parses and understands your code. It has three properties that matter for every project: ecmaVersion, which tells the parser which syntax is valid; sourceType, which tells it whether the file is a module or a script; and globals, which tells the rules which identifiers exist at runtime. The defaults are modern: ecmaVersion: "latest" and sourceType: "module" . A project that targets an older runtime or a CommonJS environment must override them, and a project that uses console, window, or process must declare those globals or the no-undef rule will flag them.
This chapter covers each of the three properties, the globals package that provides predefined environment sets, the parser options that enable JSX, and the migration from the legacy env key that no longer exists in flat config.
Key point: ESLint does not assume any runtime globals beyond the ECMAScript built-ins. console, window, process, and describe are not recognized unless they are declared in languageOptions.globals. The globals package provides the predefined sets for browser, Node, Jest, and other environments.
Why language options are explicit
The syntax problem. The parser needs to know which ECMAScript version to accept. If ecmaVersion is set to 5, an arrow function is a syntax error. If it is set to 2022, class fields and top-level await are valid. The default is "latest", which accepts the most recent stage 4 syntax. The ecmaVersion also determines which built-in globals are automatically available: Promise is enabled when the version is 2015 or higher, and Float16Array is enabled when the version is 2025 or higher .
The mode problem. The sourceType determines whether the file is parsed as an ES module or a script. module enables import and export and runs the code in strict mode. commonjs enables require and module.exports and runs in non-strict mode with a top-level function scope. script treats the file as a classic script with a shared global scope. The default is module, which is correct for most modern projects but wrong for a CommonJS file or a browser script loaded with a <script> tag .
The globals problem. Some ESLint rules, particularly no-undef, need to know which identifiers are defined at runtime. ESLint does not make assumptions about the environment. A browser project uses window and document; a Node project uses process and __dirname; a test file uses describe and it. These must be declared in languageOptions.globals, or the rules will report them as undefined .
a. ecmaVersion and sourceType
The ecmaVersion accepts a number or a year string. 3 and 5 are the legacy versions. 2015 through the current year are valid. "latest" is the default and is recommended for most projects .
// eslint.config.js
export default [
{
languageOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
]
The sourceType accepts "module", "commonjs", or "script". module is invalid when ecmaVersion is 3 or 5, because ES modules did not exist in those versions . A project that lints a mix of ESM and CommonJS files can use separate configuration objects with files patterns to apply the correct sourceType to each set.
export default [
{
files: ['**/*.mjs', 'src/**/*.js'],
languageOptions: { sourceType: 'module' },
},
{
files: ['**/*.cjs', 'scripts/**/*.js'],
languageOptions: { sourceType: 'commonjs' },
},
]
The parserOptions key is a separate property inside languageOptions that controls the built-in parser. The most common use is enabling JSX: parserOptions: { ecmaFeatures: { jsx: true } } . For TypeScript or other languages, the parser key is set to an external parser, and the ecmaVersion and sourceType are usually handled by that parser.
b. The globals property and the globals package
The globals property is an object where each key is a global identifier and each value is its access level. "readonly" means the code can read it but not assign to it. "writable" means the code can assign to it. "off" disables the global, which is useful when a standard global should be treated as unavailable .
export default [
{
languageOptions: {
globals: {
myGlobal: 'readonly',
myOtherGlobal: 'writable',
Promise: 'off',
},
},
},
]
Writing every global by hand is impractical. The globals package provides predefined sets for common environments. The sets are spread into the globals object.
import globals from 'globals'
export default [
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
]
The globals package includes browser, node, jest, mocha, es2021, and many others . A test file that uses describe and it can have ...globals.jest spread into its configuration, either globally or scoped with a files pattern.
A subtlety for TypeScript projects: when linting with type information, the TypeScript compiler deduces globals from type definition files, so languageOptions.globals is often unnecessary for .ts files. For plain JavaScript files in the same project, the globals still need to be declared .
c. The env migration
The legacy .eslintrc format used an env key to enable predefined environments. The flat config removes env entirely. The migration is to import the globals package and spread the relevant set into languageOptions.globals.
The eslint-env configuration comment is also no longer recognized in flat config. It will be reported as an error in a future version. The migration is to replace it with a /* global */ comment or to define the globals in the configuration file .
// Legacy (not flat config)
/* eslint-env mocha */
// Flat config replacement
/* global describe, it */
The globals package is the flat-config replacement for the old env shorthand. globals.node is the equivalent of env: { node: true }, and globals.browser is the equivalent of env: { browser: true } .
Complete Example Session
// ============================================
// PART 1: THE DEFAULT LANGUAGE OPTIONS
// ============================================
export default [
{
// No languageOptions key.
// ecmaVersion is "latest".
// sourceType is "module".
// No globals are defined.
},
]
// ============================================
// PART 2: EXPLICIT ECMAVERSION AND SOURCETYPE
// ============================================
export default [
{
languageOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
},
]
// ============================================
// PART 3: COMMONJS SOURCE TYPE
// ============================================
export default [
{
files: ['**/*.cjs'],
languageOptions: {
sourceType: 'commonjs',
},
},
]
// ============================================
// PART 4: BROWSER GLOBALS
// ============================================
import globals from 'globals'
export default [
{
languageOptions: {
globals: {
...globals.browser,
},
},
},
]
// ============================================
// PART 5: NODE GLOBALS
// ============================================
import globals from 'globals'
export default [
{
languageOptions: {
globals: {
...globals.node,
},
},
},
]
// ============================================
// PART 6: SCOPED TEST GLOBALS
// ============================================
import globals from 'globals'
export default [
{
files: ['**/*.test.js'],
languageOptions: {
globals: {
...globals.jest,
},
},
},
]
// ============================================
// PART 7: JSX PARSER OPTIONS
// ============================================
export default [
{
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
},
]
// ============================================
// PART 8: CUSTOM GLOBALS
// ============================================
export default [
{
languageOptions: {
globals: {
myGlobal: 'readonly',
myCounter: 'writable',
},
},
},
]
// ============================================
// PART 9: DISABLING A GLOBAL
// ============================================
export default [
{
languageOptions: {
globals: {
...globals.browser,
Promise: 'off',
},
},
},
]
// ============================================
// PART 10: THE COMPLETE CONFIG
// ============================================
import js from '@eslint/js'
import globals from 'globals'
export default [
js.configs.recommended,
{
languageOptions: {
ecmaVersion: 2022,
sourceType: 'module',
globals: {
...globals.browser,
},
},
},
{
files: ['**/*.test.js'],
languageOptions: {
globals: {
...globals.jest,
},
},
},
]
The ten parts covered the defaults, explicit ecmaVersion and sourceType, CommonJS, browser globals, Node globals, scoped test globals, JSX parser options, custom globals, disabling a global, and the complete config.
Quick Reference
languageOptions properties
| Property | Default | Values |
|---|---|---|
ecmaVersion | "latest" | 3, 5, 2015–current, "latest" |
sourceType | "module" | "module", "commonjs", "script" |
globals | {} | Object mapping names to access levels |
parser | Built-in | External parser module |
parserOptions | {} | ecmaFeatures, jsx, etc. |
sourceType modes
| Value | Scope | Strict mode | import/export |
|---|---|---|---|
module | Module | Yes | Yes |
commonjs | Function | No | No |
script | Global | No | No |
Globals access levels
| Value | Meaning |
|---|---|
"readonly" | Read only, no assignment |
"writable" | Read and write |
"off" | Disabled |
false | Equivalent to "readonly" (deprecated) |
true | Equivalent to "writable" (deprecated) |
The globals package sets
| Set | Environment |
|---|---|
globals.browser | Browser (window, document) |
globals.node | Node.js (process, __dirname) |
globals.jest | Jest (describe, it, expect) |
globals.es2021 | ES2021 built-ins |
Best Practices
✅ Do This:
// Use the globals package for environment sets
import globals from 'globals'
globals: { ...globals.node } // ✅
// Scope test globals to test files
{ files: ['**/*.test.js'], languageOptions: { globals: { ...globals.jest } } } // ✅
// Set sourceType explicitly for CommonJS files
{ files: ['**/*.cjs'], languageOptions: { sourceType: 'commonjs' } } // ✅
// Enable JSX in parserOptions
parserOptions: { ecmaFeatures: { jsx: true } } // ✅
❌ Don’t Do This:
// Use the legacy env key
env: { node: true } // ❌ (not flat config)
// Assume console is available without declaring it
// no-undef will flag it // ❌
// Use sourceType: "module" with ecmaVersion: 5
{ ecmaVersion: 5, sourceType: 'module' } // ❌ (invalid)
// Use eslint-env comments in flat config
/* eslint-env mocha */ // ❌ (not recognized)
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
console is not defined | No globals declared | Spread ...globals.node or ...globals.browser |
require is not defined | sourceType is module | Use sourceType: 'commonjs' for that file |
import is a syntax error | ecmaVersion is too low | Set ecmaVersion to 2015 or higher |
describe is not defined | Test globals not declared | Spread ...globals.jest in a scoped config |
env key has no effect | Flat config does not support env | Use languageOptions.globals instead |
Real-World Examples
1. Modern ESM with browser globals
languageOptions: { ecmaVersion: 2022, sourceType: 'module', globals: { ...globals.browser } }
2. Node CommonJS
languageOptions: { sourceType: 'commonjs', globals: { ...globals.node } }
3. Test file globals
{ files: ['**/*.test.js'], languageOptions: { globals: { ...globals.jest } } }
4. JSX support
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } }
5. Custom global
globals: { __DEV__: 'readonly' }
6. Disable a global
globals: { Promise: 'off' }
7. Explicit ECMAScript 5
languageOptions: { ecmaVersion: 5, sourceType: 'script' }
8. The globals package import
import globals from 'globals'
9. The env migration
// env: { node: true } → globals: { ...globals.node }
10. The complete language options
languageOptions: { ecmaVersion: 2022, sourceType: 'module', globals: { ...globals.browser } }
Visual
The three properties
┌─────────────────────────────────────────────────────────────┐
│ languageOptions │
│ │
│ ecmaVersion ──► which syntax is valid │
│ sourceType ──► module vs commonjs vs script │
│ globals ──► which identifiers exist at runtime │
└─────────────────────────────────────────────────────────────┘
The globals package
┌─────────────────────────────────────────────────────────────┐
│ globals.browser │
│ └── window, document, localStorage, ... │
│ │
│ globals.node │
│ └── process, __dirname, require, ... │
│ │
│ globals.jest │
│ └── describe, it, expect, ... │
│ │
│ Spread into languageOptions.globals. │
└─────────────────────────────────────────────────────────────┘
The env migration
┌─────────────────────────────────────────────────────────────┐
│ Legacy Flat config │
│ │
│ env: { node: true } globals: { ...globals.node } │
│ env: { browser: true } globals: { ...globals.browser }│
│ │
│ /* eslint-env mocha */ /* global describe, it */ │
└─────────────────────────────────────────────────────────────┘
The default behavior
┌─────────────────────────────────────────────────────────────┐
│ No languageOptions: │
│ ecmaVersion = "latest" │
│ sourceType = "module" │
│ globals = {} │
│ │
│ console is NOT defined. │
│ process is NOT defined. │
└─────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
ecmaVersion default | "latest" |
ecmaVersion values | 3, 5, 2015–current, "latest" |
sourceType default | "module" |
sourceType values | "module", "commonjs", "script" |
globals default | {} (no runtime globals assumed) |
| Globals package | globals on npm |
| Common sets | browser, node, jest, mocha |
| JSX | parserOptions.ecmaFeatures.jsx |
env key | Not supported in flat config |
Key takeaways:
ecmaVersionandsourceTypecontrol parsing. The defaults are"latest"and"module". A CommonJS project must setsourceType: 'commonjs', and a project that targets older syntax must lowerecmaVersion. The version also determines which built-in globals are automatically enabled .- ESLint does not assume runtime globals.
console,window,process, anddescribeare not recognized unless they are declared inlanguageOptions.globals. Theno-undefrule depends on this knowledge. - The
globalspackage provides the environment sets. Spreading...globals.browseror...globals.nodeintolanguageOptions.globalsis the flat-config replacement for the legacyenvkey . - Test globals should be scoped to test files. A
filespattern with...globals.jestapplies the test globals only to the test files, keeping the production code linted against the production globals. - The
envkey andeslint-envcomments are not supported. The migration is tolanguageOptions.globalsfor the configuration and/* global */for the inline comment . - JSX is enabled with
parserOptions.ecmaFeatures.jsx. ThelanguageOptions.parserOptionskey controls the built-in parser. For TypeScript or other languages, the externalparserkey replaces the built-in parser entirely.
Remember: languageOptions is how ESLint knows what language it is linting. ecmaVersion sets the syntax, sourceType sets the module mode, and globals sets the runtime environment. The defaults are modern but incomplete: they assume ESM and the latest syntax, and they assume no runtime globals at all. Declare the environment with the globals package, scope the test globals to the test files, and use sourceType: 'commonjs' for the files that use require.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!