Skip to content

Commit

Permalink
Fixes #80: Support ESLint's flat configuration format
Browse files Browse the repository at this point in the history
  • Loading branch information
Standard8 authored and azeemba committed May 19, 2024
1 parent 157d715 commit 8a999c6
Show file tree
Hide file tree
Showing 22 changed files with 2,006 additions and 84 deletions.
10 changes: 9 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,13 @@
"bracketSpacing": false
}],
"no-console": "error"
}
},
"overrides": [
{
"files": "*.mjs",
"parserOptions": {
"sourceType": "module"
}
}
]
}
4 changes: 2 additions & 2 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ jobs:
- run: npm ci
- run: npm run lint
- run: npm run test
- run: npm run integration ci 7 8 # eslint versions
- run: npm run integration test 7 8 # eslint versions
- run: npm run integration ci 7-legacy 8-legacy 8 # eslint versions
- run: npm run integration test 7-legacy 8-legacy 8 # eslint versions
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
Expand Down
57 changes: 52 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,31 @@ $ yarn add --dev eslint eslint-plugin-json

## Usage

### Basic configuration
### Basic configuration (Flat Config ESLint Format)

The `json` plugin ship with two recommended config you can use to easily activate it via the `extends` key.
It comes in two flavor: one strict (`recommended`) and one allowing comments `recommended-with-comments`.


```js
{
export default [
{
files: ["**/*.json"],
...json.configs["recommended"],
];
}
```
### Basic configuration (Legacy ESLint Format)
The `json` plugin ship with two recommended config you can use to easily activate it via the `extends` key.
It comes in two flavor: one strict (`recommended-legacy`) and one allowing comments `recommended-with-comments-legacy`.
```json
{
"extends": ["plugin:json/recommended"]
"extends": ["plugin:json/recommended-legacy"]
}
```
Expand All @@ -43,14 +59,45 @@ eslint . --ext .json,.js
eslint example.json
```
### Custom Configuration
### Custom Configuration (Flat Config ESLint Format)
If you want more granular control over which rules, and which severity you want.
If you want more granular control over which rules, and wich severity you want
If you want them all, add the `json/json` rule (or its alias `json/*`). (this is what the `recommended` config does)
#### Global rules
The global rules (`json/json` or its alias `json/*`) activate all the rules.
Note it can be configured to ignore errors cause by comments.
To do so, add option `'allowComments'` or `{allowComments: true}`
For instance:
```js
{
import json from "eslint-plugin-json";
export default [
{
files: ["**/*.json"],
plugins: { json },
processor: "json/json"
"rules": {
"json/*": ["error", "allowComments"],
// or the equivalent:
"json/*": ["error", {"allowComments": true}]
}
},
];
}
```
### Custom Configuration (Legacy ESLint Format)
If you want more granular control over which rules, and which severity you want.
Add `json` to the list of plugins (You can omit the `eslint-plugin-` prefix)
Then pick your rules.
If you want them all, add the `json/json` rule (or its alias `json/*`). (this is what the `recommended` config does)
If you want them all, add the `json/json` rule (or its alias `json/*`). (this is what the `recommended-legacy` config does)
#### Global rules
The global rules (`json/json` or its alias `json/*`) activate all the rules.
Expand Down
125 changes: 79 additions & 46 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,65 +104,98 @@ const errorSignature = (err) =>

const getErrorCode = _.pipe(_.get('ruleId'), _.split('/'), _.last);

const processors = {
'.json': {
preprocess: function (text, fileName) {
const textDocument = jsonService.TextDocument.create(fileName, 'json', 1, text);
fileDocuments[fileName] = textDocument;
const parsed = jsonServiceHandle.parseJSONDocument(textDocument);
fileLintResults[fileName] = getDiagnostics(parsed);
fileComments[fileName] = parsed.comments;
return ['']; // sorry nothing ;)
},
postprocess: function (messages, fileName) {
const textDocument = fileDocuments[fileName];
delete fileLintResults[fileName];
delete fileComments[fileName];
return _.pipe(
_.first,
_.groupBy(errorSignature),
_.mapValues((errors) => {
if (errors.length === 1) return _.first(errors);
// Otherwise there is two errors: the generic and specific one
// json/* or json/json and json/some-code
const firstErrorCode = getErrorCode(errors[0]);
const isFirstGeneric = ['*', 'json'].includes(firstErrorCode);
const genericError = errors[isFirstGeneric ? 0 : 1];
const specificError = errors[isFirstGeneric ? 1 : 0];
return genericError.severity > specificError.severity
? genericError
: specificError;
}),
_.mapValues((error) => {
const source = textDocument.getText({
start: {line: error.line - 1, character: error.column},
end: {line: error.endLine - 1, character: error.endColumn},
});
return _.assign(error, {
source,
column: error.column + 1,
endColumn: error.endColumn + 1,
});
}),
_.values
)(messages);
},
const meta = {
name: 'eslint-plugin-json',
version: '3.1.0',
};

const jsonProcessor = {
preprocess: function (text, fileName) {
const textDocument = jsonService.TextDocument.create(fileName, 'json', 1, text);
fileDocuments[fileName] = textDocument;
const parsed = jsonServiceHandle.parseJSONDocument(textDocument);
fileLintResults[fileName] = getDiagnostics(parsed);
fileComments[fileName] = parsed.comments;
return ['']; // sorry nothing ;)
},
postprocess: function (messages, fileName) {
const textDocument = fileDocuments[fileName];
delete fileLintResults[fileName];
delete fileComments[fileName];
return _.pipe(
_.first,
_.groupBy(errorSignature),
_.mapValues((errors) => {
if (errors.length === 1) return _.first(errors);
// Otherwise there is two errors: the generic and specific one
// json/* or json/json and json/some-code
const firstErrorCode = getErrorCode(errors[0]);
const isFirstGeneric = ['*', 'json'].includes(firstErrorCode);
const genericError = errors[isFirstGeneric ? 0 : 1];
const specificError = errors[isFirstGeneric ? 1 : 0];
return genericError.severity > specificError.severity
? genericError
: specificError;
}),
_.mapValues((error) => {
const source = textDocument.getText({
start: {line: error.line - 1, character: error.column},
end: {line: error.endLine - 1, character: error.endColumn},
});
return _.assign(error, {
source,
column: error.column + 1,
endColumn: error.endColumn + 1,
});
}),
_.values
)(messages);
},
};

const processors = {
// Supports old config.
'.json': jsonProcessor,
// Supports new config.
json: jsonProcessor,
};

const configs = {
recommended: {
'recommended-legacy': {
plugins: ['json'],
rules: {
'json/*': 'error',
},
},
'recommended-with-comments': {
'recommended-with-comments-legacy': {
plugins: ['json'],
rules: {
'json/*': ['error', {allowComments: true}],
},
},
};

module.exports = {rules, configs, processors};
const json = {meta, rules, configs, processors};

json.configs['recommended'] = {
files: ['**/*.json'],
plugins: {
json,
},
rules: {
'json/*': 'error',
},
processor: 'json/json',
};
json.configs['recommended-with-comments'] = {
files: ['**/*.json'],
plugins: {
json,
},
rules: {
'json/*': ['error', {allowComments: true}],
},
processor: 'json/json',
};

module.exports = json;
3 changes: 0 additions & 3 deletions test/.eslintrc.with-recommended-comments-config.json

This file was deleted.

3 changes: 3 additions & 0 deletions test/.eslintrc.with-recommended-comments-config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import json from '../src/index.js';

export default [json.configs['recommended-with-comments']];
3 changes: 3 additions & 0 deletions test/.eslintrc.with-recommended-comments-legacy-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["plugin:self/recommended-with-comments-legacy"]
}
3 changes: 0 additions & 3 deletions test/.eslintrc.with-recommended-config.json

This file was deleted.

3 changes: 3 additions & 0 deletions test/.eslintrc.with-recommended-config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import json from '../src/index.js';

export default [json.configs['recommended']];
3 changes: 3 additions & 0 deletions test/.eslintrc.with-recommended-legacy-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["plugin:self/recommended-legacy"]
}
File renamed without changes.
28 changes: 28 additions & 0 deletions test/custom.eslintrc.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json from '../src/index.js';

export default [
{
files: ['**/*.json'],
plugins: {json},
processor: 'json/json',
rules: {
'json/*': 'warn',
'json/duplicate-key': 'error',
'json/trailing-comma': 'error',
},
},
{
files: ['samples/json-with-comments.json'],
plugins: {json},
rules: {
'json/*': ['warn', {allowComments: true}],
},
},
{
files: ['samples/wrong-syntax.json'],
plugins: {json},
rules: {
'json/*': 'error',
},
},
];
Loading

0 comments on commit 8a999c6

Please sign in to comment.