Skip to content

Commit

Permalink
feat(plugin-eslint): add exclude option for Nx projects
Browse files Browse the repository at this point in the history
  • Loading branch information
hanna-skryl committed Oct 25, 2024
1 parent 849ce1f commit 0a280e6
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 6 deletions.
4 changes: 2 additions & 2 deletions packages/plugin-eslint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ Detected ESLint rules are mapped to Code PushUp audits. Audit reports are calcul

If you're using an Nx monorepo, additional helper functions are provided to simplify your configuration:

- If you wish to combine all projects in your workspace into one report, use the `eslintConfigFromAllNxProjects` helper:
- If you wish to combine all projects in your workspace into one report, use the `eslintConfigFromAllNxProjects` helper. You can exclude specific projects if needed by passing their names in the exclude option:

```js
import eslintPlugin, { eslintConfigFromAllNxProjects } from '@code-pushup/eslint-plugin';

export default {
plugins: [
// ...
await eslintPlugin(await eslintConfigFromAllNxProjects()),
await eslintPlugin(await eslintConfigFromAllNxProjects({ exclude: ['server'] })),
],
};
```
Expand Down
19 changes: 19 additions & 0 deletions packages/plugin-eslint/src/lib/nx.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,25 @@ describe('Nx helpers', () => {
},
] satisfies ESLintTarget[]);
});

it('should exclude specified projects and return only eslintrc and patterns of a remaining project', async () => {
await expect(
eslintConfigFromAllNxProjects({ exclude: ['cli', 'core', 'utils'] }),
).resolves.toEqual([
{
eslintrc: './packages/nx-plugin/.eslintrc.json',
patterns: [
'packages/nx-plugin/**/*.ts',
'packages/nx-plugin/package.json',
'packages/nx-plugin/generators.json',
'packages/nx-plugin/src/*.spec.ts',
'packages/nx-plugin/src/*.cy.ts',
'packages/nx-plugin/src/*.stories.ts',
'packages/nx-plugin/src/.storybook/main.ts',
],
},
] satisfies ESLintTarget[]);
});
});

describe('create config from target Nx project and its dependencies', () => {
Expand Down
31 changes: 31 additions & 0 deletions packages/plugin-eslint/src/lib/nx/filter-project-graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type {
ProjectGraph,
ProjectGraphDependency,
ProjectGraphProjectNode,
} from '@nx/devkit';

export function filterProjectGraph(
projectGraph: ProjectGraph,
exclude: string[] = [],
): ProjectGraph {
const filteredNodes: Record<string, ProjectGraphProjectNode> = Object.entries(
projectGraph.nodes,
).reduce(
(acc, [projectName, projectNode]) =>
exclude.includes(projectName)
? acc
: { ...acc, [projectName]: projectNode },
{},
);
const filteredDependencies: Record<string, ProjectGraphDependency[]> =
Object.entries(projectGraph.dependencies).reduce(
(acc, [key, deps]) =>
exclude.includes(key) ? acc : { ...acc, [key]: deps },
{},
);
return {
nodes: filteredNodes,
dependencies: filteredDependencies,
version: projectGraph.version,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { describe, expect, it } from 'vitest';
import { toProjectGraph } from '@code-pushup/test-utils';
import { filterProjectGraph } from './filter-project-graph';

describe('filterProjectGraph', () => {
it('should exclude specified projects from nodes', () => {
const projectGraph = toProjectGraph([
{ name: 'client', type: 'app', data: { root: 'apps/client' } },
{ name: 'server', type: 'app', data: { root: 'apps/server' } },
{ name: 'models', type: 'lib', data: { root: 'libs/models' } },
]);

const filteredGraph = filterProjectGraph(projectGraph, ['client']);

expect(Object.keys(filteredGraph.nodes)).not.toContain('client');
expect(Object.keys(filteredGraph.nodes)).toContain('server');
expect(Object.keys(filteredGraph.nodes)).toContain('models');
});

it('should exclude dependencies of excluded projects', () => {
const projectGraph = toProjectGraph(
[
{ name: 'client', type: 'app', data: { root: 'apps/client' } },
{ name: 'server', type: 'app', data: { root: 'apps/server' } },
{ name: 'models', type: 'lib', data: { root: 'libs/models' } },
],
{
client: ['server'],
server: ['models'],
},
);

const filteredGraph = filterProjectGraph(projectGraph, ['client']);

expect(Object.keys(filteredGraph.dependencies)).not.toContain('client');
expect(filteredGraph.dependencies['server']).toEqual([
{ source: 'server', target: 'models', type: 'static' },
]);
});

it('should include all projects if exclude list is empty', () => {
const projectGraph = toProjectGraph([
{ name: 'client', type: 'app', data: { root: 'apps/client' } },
{ name: 'server', type: 'app', data: { root: 'apps/server' } },
{ name: 'models', type: 'lib', data: { root: 'libs/models' } },
]);

const filteredGraph = filterProjectGraph(projectGraph, []);

expect(Object.keys(filteredGraph.nodes)).toContain('client');
expect(Object.keys(filteredGraph.nodes)).toContain('server');
expect(Object.keys(filteredGraph.nodes)).toContain('models');
});

it('should ignore non-existent projects in the exclude list', () => {
const projectGraph = toProjectGraph([
{ name: 'client', type: 'app', data: { root: 'apps/client' } },
{ name: 'server', type: 'app', data: { root: 'apps/server' } },
{ name: 'models', type: 'lib', data: { root: 'libs/models' } },
]);

const filteredGraph = filterProjectGraph(projectGraph, ['non-existent']);

expect(Object.keys(filteredGraph.nodes)).toContain('client');
expect(Object.keys(filteredGraph.nodes)).toContain('server');
expect(Object.keys(filteredGraph.nodes)).toContain('models');
});
});
20 changes: 16 additions & 4 deletions packages/plugin-eslint/src/lib/nx/find-all-projects.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { ESLintTarget } from '../config';
import { filterProjectGraph } from './filter-project-graph';
import { nxProjectsToConfig } from './projects-to-config';

/**
* Finds all Nx projects in workspace and converts their lint configurations to Code PushUp ESLint plugin parameters.
*
* Allows excluding certain projects from the configuration using the `options.exclude` parameter.
*
* Use when you wish to automatically include every Nx project in a single Code PushUp project.
* If you prefer to only include a subset of your Nx monorepo, refer to {@link eslintConfigFromNxProjectAndDeps} instead.
* If you prefer to include only a subset of your Nx monorepo, specify projects to exclude using the `exclude` option
* or consider using {@link eslintConfigFromNxProjectAndDeps} for finer control.
*
* @example
* import eslintPlugin, {
Expand All @@ -15,17 +19,25 @@ import { nxProjectsToConfig } from './projects-to-config';
* export default {
* plugins: [
* await eslintPlugin(
* await eslintConfigFromAllNxProjects()
* await eslintConfigFromAllNxProjects({ exclude: ['server'] })
* )
* ]
* }
*
* @param options - Configuration options to filter projects
* @param options.exclude - Array of project names to exclude from the ESLint configuration
* @returns ESLint config and patterns, intended to be passed to {@link eslintPlugin}
*/
export async function eslintConfigFromAllNxProjects(): Promise<ESLintTarget[]> {
export async function eslintConfigFromAllNxProjects(
options: { exclude?: string[] } = {},
): Promise<ESLintTarget[]> {
const { createProjectGraphAsync } = await import('@nx/devkit');
const projectGraph = await createProjectGraphAsync({ exitOnError: false });
return nxProjectsToConfig(projectGraph);
const filteredProjectGraph = filterProjectGraph(
projectGraph,
options.exclude,
);
return nxProjectsToConfig(filteredProjectGraph);
}

/**
Expand Down

0 comments on commit 0a280e6

Please sign in to comment.