fix(linter): ensure that @nx/eslint-plugin is installed when we add an extracted base eslintrc file (#26679)

The current eslint logic doesn't add the necessary `@nx/eslint-plugin`
package when we extract root config. This PR fixes the issue. This
applies to projects not generated using CNW but rather using something
like `npm create vite`, where the eslint setup isn't what we expect.

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## 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 #
This commit is contained in:
Jack Hsu 2024-06-26 06:38:31 -04:00 committed by GitHub
parent 81dced7252
commit c24c20e990
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 110 additions and 12 deletions

View File

@ -1,5 +1,6 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
GeneratorCallback,
joinPathFragments, joinPathFragments,
offsetFromRoot, offsetFromRoot,
ProjectConfiguration, ProjectConfiguration,
@ -15,7 +16,7 @@ import {
getGlobalFlatEslintConfiguration, getGlobalFlatEslintConfiguration,
} from './global-eslint-config'; } from './global-eslint-config';
import { useFlatConfig } from '../../utils/flat-config'; import { useFlatConfig } from '../../utils/flat-config';
import { eslintVersion } from '../../utils/versions'; import { eslintVersion, nxVersion } from '../../utils/versions';
import { import {
addBlockToFlatConfigExport, addBlockToFlatConfigExport,
addImportToFlatConfig, addImportToFlatConfig,
@ -31,7 +32,7 @@ export function migrateConfigToMonorepoStyle(
tree: Tree, tree: Tree,
unitTestRunner: string, unitTestRunner: string,
keepExistingVersions?: boolean keepExistingVersions?: boolean
): void { ): GeneratorCallback {
const rootEslintConfig = findEslintFile(tree); const rootEslintConfig = findEslintFile(tree);
let skipCleanup = false; let skipCleanup = false;
if ( if (
@ -105,6 +106,14 @@ export function migrateConfigToMonorepoStyle(
} }
} }
}); });
return addDependenciesToPackageJson(
tree,
{},
{
'@nx/eslint-plugin': nxVersion,
}
);
} }
export function findLintTarget( export function findLintTarget(

View File

@ -0,0 +1,92 @@
import {
addProjectConfiguration,
ProjectGraph,
readJson,
Tree,
} from '@nx/devkit';
import { Linter } from '../utils/linter';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { lintProjectGenerator } from './lint-project';
let projectGraph: ProjectGraph;
jest.mock('@nx/devkit', () => ({
...jest.requireActual<any>('@nx/devkit'),
createProjectGraphAsync: jest
.fn()
.mockImplementation(async () => projectGraph),
}));
describe('@nx/eslint:lint-project (convert to monorepo style)', () => {
let tree: Tree;
const defaultOptions = {
skipFormat: false,
addPlugin: true,
};
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
const rootpkg = {
root: '.',
projectType: 'library' as const,
targets: {
'eslint:lint': {
executor: 'nx:run-commands',
options: {
command: 'eslint .',
},
},
},
};
projectGraph = {
nodes: {
rootpkg: {
type: 'lib',
name: 'rootpkg',
data: rootpkg,
},
},
dependencies: {},
};
addProjectConfiguration(tree, 'rootpkg', rootpkg);
tree.write(
'.eslintrc.cjs',
`
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
}
`
);
});
it('should generate a eslint config and configure the target in project configuration', async () => {
addProjectConfiguration(tree, 'nestedpkg', {
root: 'nestedpkg',
projectType: 'library',
targets: {},
});
await lintProjectGenerator(tree, {
...defaultOptions,
linter: Linter.EsLint,
project: 'nestedpkg',
setParserOptionsProject: false,
});
expect(readJson(tree, 'package.json')).toMatchObject({
devDependencies: {
'@nx/eslint-plugin': expect.any(String),
},
});
});
});

View File

@ -1,16 +1,16 @@
import { import {
createProjectGraphAsync, createProjectGraphAsync,
formatFiles,
GeneratorCallback, GeneratorCallback,
NxJsonConfiguration, NxJsonConfiguration,
offsetFromRoot,
ProjectConfiguration, ProjectConfiguration,
ProjectGraph, ProjectGraph,
Tree,
readNxJson,
formatFiles,
offsetFromRoot,
readJson, readJson,
readNxJson,
readProjectConfiguration, readProjectConfiguration,
runTasksInSerial, runTasksInSerial,
Tree,
updateJson, updateJson,
updateProjectConfiguration, updateProjectConfiguration,
writeJson, writeJson,
@ -21,10 +21,7 @@ import { findEslintFile } from '../utils/eslint-file';
import { join } from 'path'; import { join } from 'path';
import { lintInitGenerator } from '../init/init'; import { lintInitGenerator } from '../init/init';
import type { Linter } from 'eslint'; import type { Linter } from 'eslint';
import { import { migrateConfigToMonorepoStyle } from '../init/init-migration';
findLintTarget,
migrateConfigToMonorepoStyle,
} from '../init/init-migration';
import { getProjects } from 'nx/src/generators/utils/project-configuration'; import { getProjects } from 'nx/src/generators/utils/project-configuration';
import { useFlatConfig } from '../../utils/flat-config'; import { useFlatConfig } from '../../utils/flat-config';
import { import {
@ -36,7 +33,6 @@ import {
import { import {
baseEsLintConfigFile, baseEsLintConfigFile,
baseEsLintFlatConfigFile, baseEsLintFlatConfigFile,
ESLINT_CONFIG_FILENAMES,
} from '../../utils/config-file'; } from '../../utils/config-file';
import { hasEslintPlugin } from '../utils/plugin'; import { hasEslintPlugin } from '../utils/plugin';
import { setupRootEsLint } from './setup-root-eslint'; import { setupRootEsLint } from './setup-root-eslint';
@ -144,12 +140,13 @@ export async function lintProjectGeneratorInternal(
filteredProjects.push(project); filteredProjects.push(project);
} }
}); });
migrateConfigToMonorepoStyle( const migrateTask = migrateConfigToMonorepoStyle(
filteredProjects, filteredProjects,
tree, tree,
options.unitTestRunner, options.unitTestRunner,
options.keepExistingVersions options.keepExistingVersions
); );
tasks.push(migrateTask);
} }
} }