| | |

ESLint 09 🛡️ Language Options — ecmaVersion, sourceType, and globals

⏱️ Estimated read: 9 min | |

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

PropertyDefaultValues
ecmaVersion"latest"3, 5, 2015–current, "latest"
sourceType"module""module", "commonjs", "script"
globals{}Object mapping names to access levels
parserBuilt-inExternal parser module
parserOptions{}ecmaFeatures, jsx, etc.

sourceType modes

ValueScopeStrict modeimport/export
moduleModuleYesYes
commonjsFunctionNoNo
scriptGlobalNoNo

Globals access levels

ValueMeaning
"readonly"Read only, no assignment
"writable"Read and write
"off"Disabled
falseEquivalent to "readonly" (deprecated)
trueEquivalent to "writable" (deprecated)

The globals package sets

SetEnvironment
globals.browserBrowser (window, document)
globals.nodeNode.js (process, __dirname)
globals.jestJest (describe, it, expect)
globals.es2021ES2021 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

PitfallWhy It HappensFix
console is not definedNo globals declaredSpread ...globals.node or ...globals.browser
require is not definedsourceType is moduleUse sourceType: 'commonjs' for that file
import is a syntax errorecmaVersion is too lowSet ecmaVersion to 2015 or higher
describe is not definedTest globals not declaredSpread ...globals.jest in a scoped config
env key has no effectFlat config does not support envUse 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

ItemValue
ecmaVersion default"latest"
ecmaVersion values3, 5, 2015–current, "latest"
sourceType default"module"
sourceType values"module", "commonjs", "script"
globals default{} (no runtime globals assumed)
Globals packageglobals on npm
Common setsbrowser, node, jest, mocha
JSXparserOptions.ecmaFeatures.jsx
env keyNot supported in flat config

Key takeaways:

  • ecmaVersion and sourceType control parsing. The defaults are "latest" and "module". A CommonJS project must set sourceType: 'commonjs', and a project that targets older syntax must lower ecmaVersion. The version also determines which built-in globals are automatically enabled .
  • ESLint does not assume runtime globals. console, window, process, and describe are not recognized unless they are declared in languageOptions.globals. The no-undef rule depends on this knowledge.
  • The globals package provides the environment sets. Spreading ...globals.browser or ...globals.node into languageOptions.globals is the flat-config replacement for the legacy env key .
  • Test globals should be scoped to test files. A files pattern with ...globals.jest applies the test globals only to the test files, keeping the production code linted against the production globals.
  • The env key and eslint-env comments are not supported. The migration is to languageOptions.globals for the configuration and /* global */ for the inline comment .
  • JSX is enabled with parserOptions.ecmaFeatures.jsx. The languageOptions.parserOptions key controls the built-in parser. For TypeScript or other languages, the external parser key 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.

Was this helpful?


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!