diff --git a/packages/eslint/src/generators/convert-to-flat-config/__snapshots__/generator.spec.ts.snap b/packages/eslint/src/generators/convert-to-flat-config/__snapshots__/generator.spec.ts.snap index 3e7d29711e308..23aa0ff4df9a7 100644 --- a/packages/eslint/src/generators/convert-to-flat-config/__snapshots__/generator.spec.ts.snap +++ b/packages/eslint/src/generators/convert-to-flat-config/__snapshots__/generator.spec.ts.snap @@ -135,7 +135,7 @@ module.exports = [ " `; -exports[`convert-to-flat-config generator should add global gitignores 1`] = ` +exports[`convert-to-flat-config generator should add global eslintignores 1`] = ` "const { FlatCompat } = require('@eslint/eslintrc'); const nxEslintPlugin = require('@nx/eslint-plugin'); const js = require('@eslint/js'); @@ -317,6 +317,33 @@ module.exports = [ " `; +exports[`convert-to-flat-config generator should handle custom eslintignores 1`] = ` +"const baseConfig = require('../../eslint.config.js'); +module.exports = [ + ...baseConfig, + { + files: [ + 'libs/test-lib/**/*.ts', + 'libs/test-lib/**/*.tsx', + 'libs/test-lib/**/*.js', + 'libs/test-lib/**/*.jsx', + ], + rules: {}, + }, + { + files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'], + rules: {}, + }, + { + files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'], + rules: {}, + }, + { ignores: ['libs/test-lib/ignore/me'] }, + { ignores: ['libs/test-lib/ignore/me/as/well'] }, +]; +" +`; + exports[`convert-to-flat-config generator should run successfully 1`] = ` "const { FlatCompat } = require('@eslint/eslintrc'); const nxEslintPlugin = require('@nx/eslint-plugin'); diff --git a/packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.spec.ts b/packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.spec.ts index 93b33ab9fbc05..101294eb71601 100644 --- a/packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.spec.ts +++ b/packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.spec.ts @@ -62,7 +62,8 @@ describe('convertEslintJsonToFlatConfig', () => { tree, '', '.eslintrc.json', - 'eslint.config.js' + 'eslint.config.js', + ['.eslintignore'] ); expect(tree.read('eslint.config.js', 'utf-8')).toMatchInlineSnapshot(` @@ -119,7 +120,6 @@ describe('convertEslintJsonToFlatConfig', () => { `); expect(tree.exists('.eslintrc.json')).toBeFalsy(); - expect(tree.exists('.eslintignore')).toBeFalsy(); }); it('should convert project configs', async () => { @@ -174,7 +174,8 @@ describe('convertEslintJsonToFlatConfig', () => { tree, 'mylib', '.eslintrc.json', - 'eslint.config.js' + 'eslint.config.js', + ['mylib/.eslintignore'] ); expect(tree.read('mylib/eslint.config.js', 'utf-8')).toMatchInlineSnapshot(` @@ -229,6 +230,5 @@ describe('convertEslintJsonToFlatConfig', () => { `); expect(tree.exists('mylib/.eslintrc.json')).toBeFalsy(); - expect(tree.exists('mylib/.eslintignore')).toBeFalsy(); }); }); diff --git a/packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.ts b/packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.ts index f38040beed595..bcae03ea571fc 100644 --- a/packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.ts +++ b/packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.ts @@ -27,7 +27,8 @@ export function convertEslintJsonToFlatConfig( tree: Tree, root: string, sourceFile: string, - destinationFile: string + destinationFile: string, + ignorePaths: string[] ) { const importsMap = new Map(); const exportElements: ts.Expression[] = []; @@ -174,19 +175,20 @@ export function convertEslintJsonToFlatConfig( } } - if (tree.exists(`${root}/.eslintignore`)) { - const patterns = tree - .read(`${root}/.eslintignore`, 'utf-8') - .split('\n') - .filter((line) => line.length > 0 && line !== 'node_modules') - .map((path) => mapFilePath(path, root)); - if (patterns.length > 0) { - exportElements.push(generateAst({ ignores: patterns })); + for (const ignorePath of ignorePaths) { + if (tree.exists(ignorePath)) { + const patterns = tree + .read(ignorePath, 'utf-8') + .split('\n') + .filter((line) => line.length > 0 && line !== 'node_modules') + .map((path) => mapFilePath(path, root)); + if (patterns.length > 0) { + exportElements.push(generateAst({ ignores: patterns })); + } } } tree.delete(join(root, sourceFile)); - tree.delete(join(root, '.eslintignore')); // create the node list and print it to new file const nodeList = createNodeList( diff --git a/packages/eslint/src/generators/convert-to-flat-config/generator.spec.ts b/packages/eslint/src/generators/convert-to-flat-config/generator.spec.ts index 59cd3cb591530..ce098fa3dcaaa 100644 --- a/packages/eslint/src/generators/convert-to-flat-config/generator.spec.ts +++ b/packages/eslint/src/generators/convert-to-flat-config/generator.spec.ts @@ -12,6 +12,7 @@ import { ConvertToFlatConfigGeneratorSchema } from './schema'; import { lintProjectGenerator } from '../lint-project/lint-project'; import { Linter } from '../utils/linter'; import { eslintrcVersion } from '../../utils/versions'; +import { read } from 'fs'; describe('convert-to-flat-config generator', () => { let tree: Tree; @@ -153,7 +154,7 @@ describe('convert-to-flat-config generator', () => { ).toEqual(eslintrcVersion); }); - it('should add global gitignores', async () => { + it('should add global eslintignores', async () => { await lintProjectGenerator(tree, { skipFormat: false, linter: Linter.EsLint, @@ -164,7 +165,39 @@ describe('convert-to-flat-config generator', () => { tree.write('.eslintignore', 'ignore/me'); await convertToFlatConfigGenerator(tree, options); - expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot(); + const config = tree.read('eslint.config.js', 'utf-8'); + expect(config).toContain('ignore/me'); + expect(config).toMatchSnapshot(); + expect(tree.exists('.eslintignore')).toBeFalsy(); + }); + + it('should handle custom eslintignores', async () => { + await lintProjectGenerator(tree, { + skipFormat: false, + linter: Linter.EsLint, + eslintFilePatterns: ['**/*.ts'], + project: 'test-lib', + setParserOptionsProject: false, + }); + tree.write('another-folder/.myeslintignore', 'ignore/me'); + updateJson(tree, 'libs/test-lib/project.json', (json) => { + json.targets.lint.options.ignorePath = 'another-folder/.myeslintignore'; + return json; + }); + tree.write('libs/test-lib/.eslintignore', 'ignore/me/as/well'); + + await convertToFlatConfigGenerator(tree, options); + + expect( + tree.read('libs/test-lib/eslint.config.js', 'utf-8') + ).toMatchSnapshot(); + expect(tree.exists('another-folder/.myeslintignore')).toBeFalsy(); + expect(tree.exists('libs/test-lib/.eslintignore')).toBeFalsy(); + + expect( + readJson(tree, 'libs/test-lib/project.json').targets.lint.options + .ignorePath + ).toBeUndefined(); }); it('should add settings', async () => { diff --git a/packages/eslint/src/generators/convert-to-flat-config/generator.ts b/packages/eslint/src/generators/convert-to-flat-config/generator.ts index 09d24233d2fe9..8bf80fad40781 100644 --- a/packages/eslint/src/generators/convert-to-flat-config/generator.ts +++ b/packages/eslint/src/generators/convert-to-flat-config/generator.ts @@ -26,13 +26,28 @@ export async function convertToFlatConfigGenerator( ); } - // rename root eslint config to eslint.config.js + const eslintIgnoreFiles = new Set(['.eslintignore']); + + // convert root eslint config to eslint.config.js convertRootToFlatConfig(tree, eslintFile); - // rename and map files + + // convert project eslint files to eslint.config.js const projects = getProjects(tree); for (const [project, projectConfig] of projects) { - convertProjectToFlatConfig(tree, project, projectConfig, readNxJson(tree)); + convertProjectToFlatConfig( + tree, + project, + projectConfig, + readNxJson(tree), + eslintIgnoreFiles + ); } + + // delete all .eslintignore files + for (const ignoreFile of eslintIgnoreFiles) { + tree.delete(ignoreFile); + } + // replace references in nx.json updateNxJsonConfig(tree); // install missing packages @@ -60,18 +75,24 @@ function convertProjectToFlatConfig( tree: Tree, project: string, projectConfig: ProjectConfiguration, - nxJson: NxJsonConfiguration + nxJson: NxJsonConfiguration, + eslintIgnoreFiles: Set ) { if (tree.exists(`${projectConfig.root}/.eslintrc.json`)) { if (projectConfig.targets) { const eslintTargets = Object.keys(projectConfig.targets || {}).filter( (t) => projectConfig.targets[t].executor === '@nx/eslint:lint' ); + let ignorePath: string | undefined; for (const target of eslintTargets) { // remove any obsolete `eslintConfig` options pointing to the old config file if (projectConfig.targets[target].options?.eslintConfig) { delete projectConfig.targets[target].options.eslintConfig; } + if (projectConfig.targets[target].options?.ignorePath) { + ignorePath = projectConfig.targets[target].options.ignorePath; + delete projectConfig.targets[target].options.ignorePath; + } updateProjectConfiguration(tree, project, projectConfig); } const nxHasLintTargets = Object.keys(nxJson.targetDefaults || {}).some( @@ -85,8 +106,13 @@ function convertProjectToFlatConfig( tree, projectConfig.root, '.eslintrc.json', - 'eslint.config.js' + 'eslint.config.js', + ignorePath ); + eslintIgnoreFiles.add(`${projectConfig.root}/.eslintignore`); + if (ignorePath) { + eslintIgnoreFiles.add(ignorePath); + } } } } @@ -121,7 +147,11 @@ function convertConfigToFlatConfig( tree: Tree, root: string, source: string, - target: string + target: string, + ignorePath?: string ) { - convertEslintJsonToFlatConfig(tree, root, source, target); + const ignorePaths = ignorePath + ? [ignorePath, `${root}/.eslintignore`] + : [`${root}/.eslintignore`]; + convertEslintJsonToFlatConfig(tree, root, source, target, ignorePaths); }