Skip to content

Commit

Permalink
fix(linter): generate flat config for new projects correctly (#26328)
Browse files Browse the repository at this point in the history
- Change generated import for `FlatCompat`:
  ```diff
  - const FlatCompat = require('@eslint/eslintrc');
  + const { FlatCompat } = require('@eslint/eslintrc');
  ```
- Fix replacing overrides to be reflected in the end result (the updated
content with the replacements was not being assigned)
- Add extended plugins/configs to the start (matches behavior of the old
config)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #22350 
Fixes #26151
  • Loading branch information
leosvelperez authored Jun 4, 2024
1 parent 0594deb commit e95204b
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 7 deletions.
38 changes: 38 additions & 0 deletions e2e/eslint/src/linter.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as path from 'path';
import {
checkFilesDoNotExist,
checkFilesExist,
cleanupProject,
createFile,
Expand Down Expand Up @@ -521,6 +522,43 @@ describe('Linter', () => {
);
});
});

describe('flat config', () => {
beforeAll(() => {
runCLI(`generate @nx/eslint:convert-to-flat-config`);
});

it('should generate new projects using flat config', () => {
const reactLib = uniq('react-lib');
const jsLib = uniq('js-lib');

runCLI(
`generate @nx/react:lib ${reactLib} --directory=${reactLib} --projectNameAndRootFormat=as-provided`
);
runCLI(
`generate @nx/js:lib ${jsLib} --directory=${jsLib} --projectNameAndRootFormat=as-provided`
);

checkFilesExist(
`${reactLib}/eslint.config.js`,
`${jsLib}/eslint.config.js`
);
checkFilesDoNotExist(
`${reactLib}/.eslintrc.json`,
`${jsLib}/.eslintrc.json`
);

// validate that the new projects are linted successfully
let output = runCLI(`lint ${reactLib}`);
expect(output).toContain(
`Successfully ran target lint for project ${reactLib}`
);
output = runCLI(`lint ${jsLib}`);
expect(output).toContain(
`Successfully ran target lint for project ${jsLib}`
);
});
});
});

describe('Root projects migration', () => {
Expand Down
121 changes: 121 additions & 0 deletions packages/eslint/src/generators/utils/eslint-file.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
addExtendsToLintConfig,
findEslintFile,
lintConfigHasOverride,
replaceOverridesInLintConfig,
} from './eslint-file';

import { Tree, readJson } from '@nx/devkit';
Expand Down Expand Up @@ -120,4 +121,124 @@ describe('@nx/eslint:lint-file', () => {
]);
});
});

describe('replaceOverridesInLintConfig', () => {
it('should replace overrides when using flat config', () => {
tree.write('eslint.config.js', 'module.exports = {};');
tree.write(
'apps/demo/eslint.config.js',
`const baseConfig = require("../../eslint.config.js");
module.exports = [
...baseConfig,
{
files: [
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.jsx"
],
rules: {}
},
{
files: [
"**/*.ts",
"**/*.tsx"
],
rules: {}
},
{
files: [
"**/*.js",
"**/*.jsx"
],
rules: {}
}
];`
);

replaceOverridesInLintConfig(tree, 'apps/demo', [
{
files: ['*.ts'],
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'myOrg',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'my-org',
style: 'kebab-case',
},
],
},
},
{
files: ['*.html'],
extends: ['plugin:@nx/angular-template'],
rules: {},
},
]);

expect(tree.read('apps/demo/eslint.config.js', 'utf-8'))
.toMatchInlineSnapshot(`
"const { FlatCompat } = require("@eslint/eslintrc");
const js = require("@eslint/js");
const baseConfig = require("../../eslint.config.js");
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
...baseConfig,
...compat.config({ extends: [
"plugin:@nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
] }).map(config => ({
...config,
files: ["**/*.ts"],
rules: {
...config.rules,
"@angular-eslint/directive-selector": [
"error",
{
type: "attribute",
prefix: "myOrg",
style: "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
type: "element",
prefix: "my-org",
style: "kebab-case"
}
]
}
})),
...compat.config({ extends: ["plugin:@nx/angular-template"] }).map(config => ({
...config,
files: ["**/*.html"],
rules: {
...config.rules
}
})),
];"
`);
});
});
});
9 changes: 7 additions & 2 deletions packages/eslint/src/generators/utils/eslint-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ export function replaceOverridesInLintConfig(
content = removeOverridesFromLintConfig(content);
overrides.forEach((override) => {
const flatOverride = generateFlatOverride(override);
addBlockToFlatConfigExport(content, flatOverride);
content = addBlockToFlatConfigExport(content, flatOverride);
});

tree.write(fileName, content);
Expand All @@ -315,7 +315,12 @@ export function addExtendsToLintConfig(
const pluginExtends = generatePluginExtendsElement(plugins);
let content = tree.read(fileName, 'utf8');
content = addCompatToFlatConfig(content);
tree.write(fileName, addBlockToFlatConfigExport(content, pluginExtends));
tree.write(
fileName,
addBlockToFlatConfigExport(content, pluginExtends, {
insertAtTheEnd: false,
})
);
} else {
const fileName = joinPathFragments(root, '.eslintrc.json');
updateJson(tree, fileName, (json) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ describe('ast-utils', () => {
];`;
const result = addCompatToFlatConfig(content);
expect(result).toMatchInlineSnapshot(`
"const FlatCompat = require("@eslint/eslintrc");
"const { FlatCompat } = require("@eslint/eslintrc");
const js = require("@eslint/js");
const baseConfig = require("../../eslint.config.js");
Expand Down Expand Up @@ -262,7 +262,7 @@ describe('ast-utils', () => {
];`;
const result = addCompatToFlatConfig(content);
expect(result).toMatchInlineSnapshot(`
"const FlatCompat = require("@eslint/eslintrc");
"const { FlatCompat } = require("@eslint/eslintrc");
const baseConfig = require("../../eslint.config.js");
const js = require("@eslint/js");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ export function addCompatToFlatConfig(content: string) {
if (result.includes('const compat = new FlatCompat')) {
return result;
}
result = addImportToFlatConfig(result, 'FlatCompat', '@eslint/eslintrc');
result = addImportToFlatConfig(result, ['FlatCompat'], '@eslint/eslintrc');
const index = result.indexOf('module.exports');
return applyChangesToString(result, [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ describe('updateEslint', () => {

expect(tree.read(`${schema.appProjectRoot}/eslint.config.js`, 'utf-8'))
.toMatchInlineSnapshot(`
"const FlatCompat = require("@eslint/eslintrc");
"const { FlatCompat } = require("@eslint/eslintrc");
const js = require("@eslint/js");
const baseConfig = require("../eslint.config.js");
Expand All @@ -127,6 +127,7 @@ describe('updateEslint', () => {
module.exports = [
...compat.extends("plugin:@nx/react-typescript", "next", "next/core-web-vitals"),
...baseConfig,
{
"files": [
Expand Down Expand Up @@ -156,7 +157,6 @@ describe('updateEslint', () => {
],
rules: {}
},
...compat.extends("plugin:@nx/react-typescript", "next", "next/core-web-vitals"),
...compat.config({ env: { jest: true } }).map(config => ({
...config,
files: [
Expand Down

0 comments on commit e95204b

Please sign in to comment.