fix(linter): fix migrating projects with the eslint plugin (#23147)

<!-- 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 -->

Creating a new project with ESLint is mistakenly creating a
`eslint.base.config.js`.

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

Creating a new project with ESLint only creates a
`eslint.base.config.js` file when the repo was originally standalone and
the second project is made.

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

Fixes #
This commit is contained in:
Jason Jean 2024-05-13 14:27:56 -04:00 committed by GitHub
parent 5fea49a980
commit 461b901a38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 40 additions and 32 deletions

View File

@ -34,7 +34,7 @@ describe('convertToCypressTen', () => {
mockedInstalledCypressVersion.mockReturnValue(9); mockedInstalledCypressVersion.mockReturnValue(9);
}); });
afterEach(() => { afterAll(() => {
jest.resetAllMocks(); jest.resetAllMocks();
}); });

View File

@ -1,10 +1,10 @@
import type { import {
createProjectGraphAsync,
GeneratorCallback, GeneratorCallback,
NxJsonConfiguration, NxJsonConfiguration,
ProjectConfiguration, ProjectConfiguration,
ProjectGraph,
Tree, Tree,
} from '@nx/devkit';
import {
readNxJson, readNxJson,
formatFiles, formatFiles,
offsetFromRoot, offsetFromRoot,
@ -134,7 +134,8 @@ export async function lintProjectGeneratorInternal(
if (!options.rootProject) { if (!options.rootProject) {
const projects = {} as any; const projects = {} as any;
getProjects(tree).forEach((v, k) => (projects[k] = v)); getProjects(tree).forEach((v, k) => (projects[k] = v));
if (isMigrationToMonorepoNeeded(projects, tree)) { const graph = await createProjectGraphAsync();
if (isMigrationToMonorepoNeeded(tree, graph)) {
// we only migrate project configurations that have been created // we only migrate project configurations that have been created
const filteredProjects = []; const filteredProjects = [];
Object.entries(projects).forEach(([name, project]) => { Object.entries(projects).forEach(([name, project]) => {
@ -295,10 +296,7 @@ function isBuildableLibraryProject(
* Detect based on the state of lint target configuration of the root project * Detect based on the state of lint target configuration of the root project
* if we should migrate eslint configs to monorepo style * if we should migrate eslint configs to monorepo style
*/ */
function isMigrationToMonorepoNeeded( function isMigrationToMonorepoNeeded(tree: Tree, graph: ProjectGraph): boolean {
projects: Record<string, ProjectConfiguration>,
tree: Tree
): boolean {
// the base config is already created, migration has been done // the base config is already created, migration has been done
if ( if (
tree.exists(baseEsLintConfigFile) || tree.exists(baseEsLintConfigFile) ||
@ -307,25 +305,25 @@ function isMigrationToMonorepoNeeded(
return false; return false;
} }
const configs = Object.values(projects); const nodes = Object.values(graph.nodes);
if (configs.length === 1) {
// get root project
const rootProject = nodes.find((p) => p.data.root === '.');
if (!rootProject || !rootProject.data.targets) {
return false; return false;
} }
// get root project for (const targetConfig of Object.values(rootProject.data.targets ?? {})) {
const rootProject = configs.find((p) => p.root === '.'); if (
if (!rootProject || !rootProject.targets) { ['@nx/eslint:lint', '@nrwl/linter:eslint', '@nx/linter:eslint'].includes(
return false; targetConfig.executor
} ) ||
// check if we're inferring lint target from `@nx/eslint/plugin` (targetConfig.executor === 'nx:run-commands' &&
if (hasEslintPlugin(tree)) { targetConfig.options?.command.startsWith('eslint '))
for (const f of ESLINT_CONFIG_FILENAMES) { ) {
if (tree.exists(f)) {
return true; return true;
} }
} }
}
// find if root project has lint target return false;
const lintTarget = findLintTarget(rootProject);
return !!lintTarget;
} }

View File

@ -2,7 +2,7 @@
exports[`NxPlugin e2e-project Generator should setup the eslint builder 1`] = ` exports[`NxPlugin e2e-project Generator should setup the eslint builder 1`] = `
"{ "{
"extends": ["../../.eslintrc.base.json"], "extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"], "ignorePatterns": ["!**/*"],
"overrides": [ "overrides": [
{ {

View File

@ -48,9 +48,14 @@ describe('React:CypressComponentTestConfiguration', () => {
}); });
beforeEach(() => { beforeEach(() => {
tree = createTreeWithEmptyWorkspace(); tree = createTreeWithEmptyWorkspace();
projectGraph = {
nodes: {},
dependencies: {},
};
}); });
afterEach(() => { afterAll(() => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
@ -422,7 +427,7 @@ describe('React:CypressComponentTestConfiguration', () => {
).toBeFalsy(); ).toBeFalsy();
}); });
it('should not throw error when an invalid --build-target is provided', async () => { it('should throw error when an invalid --build-target is provided', async () => {
mockedAssertCypressVersion.mockReturnValue(); mockedAssertCypressVersion.mockReturnValue();
await applicationGenerator(tree, { await applicationGenerator(tree, {
e2eTestRunner: 'none', e2eTestRunner: 'none',
@ -446,6 +451,7 @@ describe('React:CypressComponentTestConfiguration', () => {
const appConfig = readProjectConfiguration(tree, 'my-app'); const appConfig = readProjectConfiguration(tree, 'my-app');
appConfig.targets['build'].executor = 'something/else'; appConfig.targets['build'].executor = 'something/else';
updateProjectConfiguration(tree, 'my-app', appConfig); updateProjectConfiguration(tree, 'my-app', appConfig);
jest.clearAllMocks();
projectGraph = { projectGraph = {
nodes: { nodes: {
'my-app': { 'my-app': {
@ -465,13 +471,17 @@ describe('React:CypressComponentTestConfiguration', () => {
}, },
dependencies: {}, dependencies: {},
}; };
await expect(async () => {
await cypressComponentConfigGenerator(tree, { await expect(
cypressComponentConfigGenerator(tree, {
project: 'some-lib', project: 'some-lib',
generateTests: true, generateTests: true,
buildTarget: 'my-app:build', buildTarget: 'my-app:build',
}); })
}).resolves; ).rejects.toThrow();
expect(require('@nx/devkit').createProjectGraphAsync).toHaveBeenCalledTimes(
1
);
}); });
it('should setup cypress config files correctly', async () => { it('should setup cypress config files correctly', async () => {