Skip to content

Commit

Permalink
feat(linter): use overrides in root eslint config (#3949)
Browse files Browse the repository at this point in the history
* feat(linter): use overrides in root eslint config

* feat(linter): update-root-eslint-config-to-use-overrides migration
  • Loading branch information
JamesHenry authored Oct 23, 2020
1 parent 6b61a6c commit 4f5fb0f
Show file tree
Hide file tree
Showing 8 changed files with 478 additions and 26 deletions.
30 changes: 25 additions & 5 deletions e2e/linter/src/linter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ forEachCli('nx', () => {
runCLI(`generate @nrwl/react:app ${myapp}`);

const eslintrc = readJson('.eslintrc.json');
eslintrc.rules['no-console'] = 'error';
eslintrc.overrides.forEach((override) => {
if (override.files.includes('*.ts')) {
override.rules['no-console'] = 'error';
}
});
updateFile('.eslintrc.json', JSON.stringify(eslintrc, null, 2));

updateFile(`apps/${myapp}/src/main.ts`, `console.log("should fail");`);
Expand All @@ -35,7 +39,11 @@ forEachCli('nx', () => {
runCLI(`generate @nrwl/react:app ${myapp}`);

const eslintrc = readJson('.eslintrc.json');
eslintrc.rules['no-console'] = 'error';
eslintrc.overrides.forEach((override) => {
if (override.files.includes('*.ts')) {
override.rules['no-console'] = 'error';
}
});
updateFile('.eslintrc.json', JSON.stringify(eslintrc, null, 2));

updateFile(`apps/${myapp}/src/main.ts`, `console.log("should fail");`);
Expand All @@ -50,7 +58,11 @@ forEachCli('nx', () => {
runCLI(`generate @nrwl/react:app ${myapp}`);

const eslintrc = readJson('.eslintrc.json');
eslintrc.rules['no-console'] = undefined;
eslintrc.overrides.forEach((override) => {
if (override.files.includes('*.ts')) {
override.rules['no-console'] = undefined;
}
});
updateFile('.eslintrc.json', JSON.stringify(eslintrc, null, 2));

updateFile(`apps/${myapp}/src/main.ts`, `console.log("should fail");`);
Expand Down Expand Up @@ -82,7 +94,11 @@ forEachCli('nx', () => {
updateFile('workspace.json', JSON.stringify(workspaceJson, null, 2));

const eslintrc = readJson('.eslintrc.json');
eslintrc.rules['no-console'] = undefined;
eslintrc.overrides.forEach((override) => {
if (override.files.includes('*.ts')) {
override.rules['no-console'] = undefined;
}
});
updateFile('.eslintrc.json', JSON.stringify(eslintrc, null, 2));

updateFile(`apps/${myapp}/src/main.ts`, `console.log("should fail");`);
Expand Down Expand Up @@ -130,7 +146,11 @@ forEachCli('nx', () => {
runCLI(`generate @nrwl/react:app ${myapp}`);

const eslintrc = readJson('.eslintrc.json');
eslintrc.rules['no-console'] = 'error';
eslintrc.overrides.forEach((override) => {
if (override.files.includes('*.ts')) {
override.rules['no-console'] = 'error';
}
});
updateFile('.eslintrc.json', JSON.stringify(eslintrc, null, 2));
updateFile(`apps/${myapp}/src/main.ts`, `console.log("should fail");`);

Expand Down
37 changes: 37 additions & 0 deletions packages/eslint-plugin-nx/src/configs/javascript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* This configuration is intended to be applied to ALL .js and .jsx files
* within an Nx workspace.
*
* It should therefore NOT contain any rules or plugins which are specific
* to one ecosystem, such as React, Angular, Node etc.
*
* We use @typescript-eslint/parser rather than the built in JS parser
* because that is what Nx ESLint configs have always done and we don't
* want to change too much all at once.
*
* TODO: Evaluate switching to the built-in JS parser (espree) in Nx v11,
* it should yield a performance improvement but could introduce subtle
* breaking changes - we should also look to replace all the @typescript-eslint
* related plugins and rules below.
*/
export default {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'prettier/@typescript-eslint',
],
rules: {
'@typescript-eslint/explicit-member-accessibility': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-parameter-properties': 'off',
},
};
2 changes: 2 additions & 0 deletions packages/eslint-plugin-nx/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import typescript from './configs/typescript';
import javascript from './configs/javascript';
import reactTmp from './configs/react-tmp';
import reactBase from './configs/react-base';
import reactJsx from './configs/react-jsx';
Expand All @@ -11,6 +12,7 @@ import enforceModuleBoundaries, {
module.exports = {
configs: {
typescript,
javascript,
react: reactTmp,
'react-base': reactBase,
'react-typescript': reactTypescript,
Expand Down
5 changes: 5 additions & 0 deletions packages/linter/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"version": "10.4.0-beta.0",
"description": "Update ESLint config files to use preset configs which eslint-plugin-nx exports",
"factory": "./src/migrations/update-10-4-0/update-eslint-configs-to-use-nx-presets"
},
"update-root-eslint-config-to-use-overrides": {
"version": "10.4.0-beta.1",
"description": "Update root ESLint config to use overrides",
"factory": "./src/migrations/update-10-4-0/update-root-eslint-config-to-use-overrides"
}
},
"packageJsonUpdates": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { Tree } from '@angular-devkit/schematics';
import { readJsonInTree } from '@nrwl/workspace';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { runMigration } from '../../utils/testing';

describe('Update root ESLint config to use overrides', () => {
let tree: Tree;
beforeEach(async () => {
tree = Tree.empty();
tree = createEmptyWorkspace(tree);
});

const testCases = [
{
// Most recent root ESLint config (before this change) with no modifications
input: {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
extends: ['plugin:@nrwl/nx/typescript'],
rules: {
'@nrwl/nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] },
],
},
],
},
},
expected: {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
rules: {
'@nrwl/nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] },
],
},
],
},
},
{
files: ['*.ts', '*.tsx'],
extends: ['plugin:@nrwl/nx/typescript'],
parserOptions: { project: './tsconfig.*?.json' },
rules: {},
},
{
files: ['*.js', '*.jsx'],
extends: ['plugin:@nrwl/nx/javascript'],
rules: {},
},
],
},
},

{
// Example using custom overrides already (should be a noop)
input: {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
extends: ['plugin:@nrwl/nx/typescript'],
overrides: [
{
files: ['*.ts'],
rules: {
foo: 'error',
},
},
],
},
expected: {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
extends: ['plugin:@nrwl/nx/typescript'],
overrides: [
{
files: ['*.ts'],
rules: {
foo: 'error',
},
},
],
},
},

{
// Example using custom rules and plugins at the top-level
input: {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx', 'plugin-a', 'plugin-b'],
extends: ['plugin:@nrwl/nx/typescript'],
rules: {
bar: 'warn',
'plugin-a/qux': ['error', { someConfig: true }],
'plugin-b/baz': 'off',
},
},
expected: {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
plugins: ['plugin-a', 'plugin-b'],
rules: {
bar: 'warn',
'plugin-a/qux': ['error', { someConfig: true }],
'plugin-b/baz': 'off',
},
},
{
files: ['*.ts', '*.tsx'],
extends: ['plugin:@nrwl/nx/typescript'],
parserOptions: { project: './tsconfig.*?.json' },
rules: {},
},
{
files: ['*.js', '*.jsx'],
extends: ['plugin:@nrwl/nx/javascript'],
rules: {},
},
],
},
},

{
// Example using other custom config at the top-level
input: {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
settings: {
foo: 'bar',
},
env: {
browser: true,
},
parser: 'some-custom-parser-value',
parserOptions: {
custom: 'option',
},
extends: ['plugin:@nrwl/nx/typescript', 'custom-extends-config'],
},
expected: {
root: true,
ignorePatterns: ['**/*'],
plugins: ['@nrwl/nx'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
extends: ['custom-extends-config'],
env: {
browser: true,
},
settings: {
foo: 'bar',
},
parser: 'some-custom-parser-value',
parserOptions: {
custom: 'option',
},
rules: {},
},
{
files: ['*.ts', '*.tsx'],
extends: ['plugin:@nrwl/nx/typescript'],
parserOptions: { project: './tsconfig.*?.json' },
rules: {},
},
{
files: ['*.js', '*.jsx'],
extends: ['plugin:@nrwl/nx/javascript'],
rules: {},
},
],
},
},
];

testCases.forEach((tc, i) => {
it(`should update the existing root .eslintrc.json file to use overrides, CASE ${i}`, async () => {
tree.create('.eslintrc.json', JSON.stringify(tc.input));

const result = await runMigration(
'update-root-eslint-config-to-use-overrides',
{},
tree
);
expect(readJsonInTree(result, '.eslintrc.json')).toEqual(tc.expected);
});
});
});
Loading

0 comments on commit 4f5fb0f

Please sign in to comment.