Skip to content

Commit 241fcea

Browse files
nzakasamareshsmmdjermanovic
authored
docs: Use and define languages (#18795)
* docs: Use and define languages refs #16999 * Fix broken link * Fix link (again) * Update docs/src/extend/languages.md Co-authored-by: Amaresh S M <[email protected]> * Update docs/src/extend/languages.md Co-authored-by: Amaresh S M <[email protected]> * Update docs/src/extend/languages.md Co-authored-by: Milos Djermanovic <[email protected]> * Update docs/src/extend/languages.md Co-authored-by: Milos Djermanovic <[email protected]> * Update docs/src/extend/languages.md Co-authored-by: Milos Djermanovic <[email protected]> * Update docs/src/extend/languages.md Co-authored-by: Milos Djermanovic <[email protected]> --------- Co-authored-by: Amaresh S M <[email protected]> Co-authored-by: Milos Djermanovic <[email protected]>
1 parent 0f68a85 commit 241fcea

File tree

3 files changed

+157
-1
lines changed

3 files changed

+157
-1
lines changed

docs/src/extend/languages.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
title: Languages
3+
eleventyNavigation:
4+
key: languages
5+
parent: create plugins
6+
title: Languages
7+
order: 4
8+
9+
---
10+
11+
Starting with ESLint v9.7.0, you can extend ESLint with additional languages through plugins. While ESLint began as a linter strictly for JavaScript, the ESLint core is generic and can be used to lint any programming language. Each language is defined as an object that contains all of the parsing, evaluating, and traversal functionality required to lint a file. These languages are then distributed in plugins for use in user configurations.
12+
13+
## Language Requirements
14+
15+
In order to create a language, you need:
16+
17+
1. **A parser.** The parser is the piece that converts plain text into a data structure. There is no specific format that ESLint requires the data structure to be in, so you can use any already-existing parser, or write your own.
18+
1. **A `SourceCode` object.** The way ESLint works with an AST is through a `SourceCode` object. There are some required methods on each `SourceCode`, and you can also add more methods or properties that you'd like to expose to rules.
19+
1. **A `Language` object.** The `Language` object contains information about the language itself along with methods for parsing and creating the `SourceCode` object.
20+
21+
### Parser Requirements for Languages
22+
23+
To get started, make sure you have a parser that can be called from JavaScript. The parser must return a data structure representing the code that was parsed. Most parsers return an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST) to represent the code, but they can also return a [concrete syntax tree](https://en.wikipedia.org/wiki/Parse_tree) (CST). Whether an AST or CST is returned doesn't matter to ESLint, it only matters that there is a data structure to traverse.
24+
25+
While there is no specific structure an AST or CST must follow, it's easier to integrate with ESLint when each node in the tree contains the following information:
26+
27+
1. **Type** - A property on each node representing the node type is required. For example, in JavaScript, the `type` property contains this information for each node. ESLint rules use node types to define the visitor methods, so it's important that each node can be identified by a string. The name of the property doesn't matter (discussed further below) so long as one exists. This property is typically named `type` or `kind` by most parsers.
28+
1. **Location** - A property on each node representing the location of the node in the original source code is required. The location must contain:
29+
* The line on which the node starts
30+
* The column on which the node starts
31+
* The line on which the node ends
32+
* The column on which the node ends
33+
34+
As with the node type, the property name doesn't matter. Two common property names are `loc` (as in [ESTree](https://github.com/estree/estree/blob/3851d4a6eae5e5473371893959b88b62007469e8/es5.md#node-objects)) and `position` (as in [Unist](https://github.com/syntax-tree/unist?tab=readme-ov-file#node)). This information is used by ESLint to report errors and rule violations.
35+
1. **Range** - A property on each node representing the location of the node's source inside the source code is required. The range indicates the index at which the first character is found and the index after the last character, such that calling `code.slice(start, end)` returns the text that the node represents. Once again, no specific property name is required, and this information may even be merged with location information. ESTree uses the `range` property while Unist includes this information on `position` along with the location information. This information is used by ESLint to apply autofixes.
36+
37+
### The `SourceCode` Object
38+
39+
ESLint holds information about source code in a `SourceCode` object. This object is the API used both by ESLint internally and by rules written to work on the code (via `context.sourceCode`). The `SourceCode` object must implement the `TextSourceCode` interface as defined in the [`@eslint/core`](https://npmjs.com/package/@eslint/core) package.
40+
41+
A basic `SourceCode` object must implement the following:
42+
43+
* `ast` - a property containing the AST or CST for the source code.
44+
* `text` - the text of the source code.
45+
* `getLoc(nodeOrToken)` - a method that returns the location of a given node or token. This must match the `loc` structure that ESTree uses.
46+
* `getRange(nodeOrToken)` - a method that returns the range of a given node or token. This must return an array where the first item is the start index and the second is the end index.
47+
* `traverse()` - a method that returns an iterable for traversing the AST or CST. The iterator must return objects that implement either `VisitTraversalStep` or `CallTraversalStep` from `@eslint/core`.
48+
49+
The following optional members allow you to customize how ESLint interacts with the object:
50+
51+
* `visitorKeys` - visitor keys that are specific to just this `SourceCode` object. Typically not necessary as `Language#visitorKeys` is used most of the time.
52+
* `applyLanguageOptions(languageOptions)` - if you have specific language options that need to be applied after parsing, you can do so in this method.
53+
* `getDisableDirectives()` - returns any disable directives in the code. ESLint uses this to apply disable directives and track unused directives.
54+
* `getInlineConfigNodes()` - returns any inline config nodes. ESLint uses this to report errors when `noInlineConfig` is enabled.
55+
* `applyInlineConfig()` - returns inline configuration elements to ESLint. ESLint uses this to alter the configuration of the file being linted.
56+
* `finalize()` - this method is called just before linting begins and is your last chance to modify `SourceCode`. If you've defined `applyLanguageOptions()` or `applyInlineConfig()`, then you may have additional changes to apply before the `SourceCode` object is ready.
57+
58+
Additionally, the following members are common on `SourceCode` objects and are recommended to implement:
59+
60+
* `lines` - the individual lines of the source code as an array of strings.
61+
* `getParent(node)` - returns the parent of the given node or `undefined` if the node is the root.
62+
* `getAncestors(node)` - returns an array of the ancestry of the node with the first item as the root of the tree and each subsequent item as the descendants of the root that lead to `node`.
63+
* `getText(node, beforeCount, afterCount)` - returns the string that represents the given node, and optionally, a specified number of characters before and after the node's range.
64+
65+
See [`JSONSourceCode`](https://github.com/eslint/json/blob/main/src/languages/json-source-code.js) as an example of a basic `SourceCode` class.
66+
67+
### The `Language` Object
68+
69+
The `Language` object contains all of the information about the programming language as well as methods for interacting with code written in that language. ESLint uses this object to determine how to deal with a particular file. The `Language` object must implement the `Language` interface as defined in the [`@eslint/core`](https://npmjs.com/package/@eslint/core) package.
70+
71+
A basic `Language` object must implement the following:
72+
73+
* `fileType` - should be `"text"` (in the future, we will also support `"binary"`)
74+
* `lineStart` - either 0 or 1 to indicate how the AST represents the first line in the file. ESLint uses this to correctly display error locations.
75+
* `columnStart` - either 0 or 1 to indicate how the AST represents the first column in each line. ESLint uses this to correctly display error locations.
76+
* `nodeTypeKey` - the name of the property that indicates the node type (usually `"type"` or `"kind"`).
77+
* `validateLanguageOptions(languageOptions)` - validates language options for the language. This method is expected to throw a validation error when an expected language option doesn't have the correct type or value. Unexpected language options should be silently ignored and no error should be thrown. This method is required even if the language doesn't specify any options.
78+
* `parse(file, context)` - parses the given file into an AST or CST, and can also include additional values meant for use in rules. Called internally by ESLint.
79+
* `createSourceCode(file, parseResult, context)` - creates a `SourceCode` object. Call internally by ESLint after `parse()`, and the second argument is the exact return value from `parse()`.
80+
81+
The following optional members allow you to customize how ESLint interacts with the object:
82+
83+
* `visitorKeys` - visitor keys that are specific to the AST or CST. This is used to optimize traversal of the AST or CST inside of ESLint. While not required, it is strongly recommended, especially for AST or CST formats that deviate significantly from ESTree format.
84+
* `matchesSelectorClass(className, node, ancestry)` - allows you to specify selector classes, such as `:expression`, that match more than one node. This method is called whenever an [esquery](https://github.com/estools/esquery) selector contains a `:` followed by an identifier.
85+
86+
See [`JSONLanguage`](https://github.com/eslint/json/blob/main/src/languages/json-language.js) as an example of a basic `Language` class.
87+
88+
## Publish a Language in a Plugin
89+
90+
Languages are published in plugins similar to processors and rules. Define the `languages` key in your plugin as an object whose names are the language names and the values are the language objects. Here's an example:
91+
92+
```js
93+
import { myLanguage } from "../languages/my.js";
94+
95+
const plugin = {
96+
97+
// preferred location of name and version
98+
meta: {
99+
name: "eslint-plugin-example",
100+
version: "1.2.3"
101+
},
102+
languages: {
103+
my: myLanguage
104+
},
105+
rules: {
106+
// add rules here
107+
}
108+
};
109+
110+
// for ESM
111+
export default plugin;
112+
113+
// OR for CommonJS
114+
module.exports = plugin;
115+
```
116+
117+
In order to use a language from a plugin in a configuration file, import the plugin and include it in the `plugins` key, specifying a namespace. Then, use that namespace to reference the language in the `language` configuration, like this:
118+
119+
```js
120+
// eslint.config.js
121+
import example from "eslint-plugin-example";
122+
123+
export default [
124+
{
125+
plugins: {
126+
example
127+
},
128+
language: "example/my"
129+
}
130+
];
131+
```
132+
133+
See [Specify a Language](../use/configure/plugins#specify-a-language) in the Plugin Configuration documentation for more details.

docs/src/extend/plugin-migration-flat-config.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ eleventyNavigation:
44
key: plugin flat config
55
parent: create plugins
66
title: Migration to Flat Config
7-
order: 4
7+
order: 5
88

99
---
1010

docs/src/use/configure/plugins.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,29 @@ export default [
249249
];
250250
```
251251

252+
## Specify a Language
253+
254+
Plugins may provide languages. Languages allow ESLint to lint programming languages besides JavaScript. To specify a language in a [configuration file](./configuration-files#configuration-file), use the `language` key and assign the name of language in the format `namespace/language-name`. For example, the following uses the `json/jsonc` language from `@eslint/json` for `*.json` files.
255+
256+
```js
257+
// eslint.config.js
258+
import json from "@eslint/json";
259+
260+
export default [
261+
{
262+
files: ["**/*.json"],
263+
plugins: {
264+
json
265+
},
266+
language: "json/jsonc"
267+
}
268+
];
269+
```
270+
271+
::: tip
272+
When you specify a `language` in a config object, `languageOptions` becomes specific to that language. Each language defines its own `languageOptions`, so check the documentation of the plugin to determine which options are available.
273+
:::
274+
252275
## Common Problems
253276

254277
* [Plugin rules using the ESLint < v9.0.0 API](../troubleshooting/v9-rule-api-changes)

0 commit comments

Comments
 (0)