Skip to content

Commit

Permalink
test(plugin-eslint): integration test for Nx helper with mock monorepo
Browse files Browse the repository at this point in the history
  • Loading branch information
matejchalk committed Dec 4, 2023
1 parent 003e0c1 commit 6c1edb0
Show file tree
Hide file tree
Showing 22 changed files with 311 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .nxignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# exclude mock Nx monorepos used for testing
mocks
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.nx/cache
7 changes: 7 additions & 0 deletions packages/plugin-eslint/mocks/fixtures/nx-monorepo/nx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"pluginsConfig": {
"@nrwl/js": {
"analyzeSourceFiles": true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": ["error"]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "cli",
"sourceRoot": "packages/cli/src",
"projectType": "application",
"targets": {
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"packages/cli/**/*.ts",
"packages/cli/package.json"
]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { exampleCore } from '@example/core';

exampleCore();
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.base.json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": ["error"]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "core",
"sourceRoot": "packages/core/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"packages/core/**/*.ts",
"packages/core/package.json"
]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { exampleSharedUtil } from '@example/utils';

export function exampleCore() {
exampleSharedUtil();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.base.json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": ["error"]
}
},
{
"files": ["./package.json", "./generators.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/nx-plugin-checks": "error"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "nx-plugin",
"sourceRoot": "packages/nx-plugin/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"packages/nx-plugin/**/*.ts",
"packages/nx-plugin/package.json",
"packages/nx-plugin/generators.json"
]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { exampleSharedUtil } from '@example/utils';

export function exampleNxPlugin() {
exampleSharedUtil();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.base.json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "utils",
"sourceRoot": "packages/utils/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"packages/utils/**/*.ts",
"packages/utils/package.json"
]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export function exampleSharedUtil() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.base.json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"compileOnSave": false,
"compilerOptions": {
"rootDir": ".",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es2015",
"module": "esnext",
"lib": ["es2020", "dom"],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"baseUrl": ".",
"paths": {
"@example/cli": ["packages/cli/src/index.ts"],
"@example/core": ["packages/core/src/index.ts"],
"@example/nx-plugin": ["packages/nx-plugin/src/index.ts"],
"@example/utils": ["packages/utils/src/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]
}
92 changes: 92 additions & 0 deletions packages/plugin-eslint/src/lib/nx.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { setWorkspaceRoot } from 'nx/src/utils/workspace-root';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import type { SpyInstance } from 'vitest';
import { ESLintPluginConfig } from './config';
import { eslintConfigFromNxProjects } from './nx';

describe('Nx helpers', () => {
let cwdSpy: SpyInstance;

beforeAll(() => {
const workspaceDir = join(
fileURLToPath(dirname(import.meta.url)),
'..',
'..',
'mocks',
'fixtures',
'nx-monorepo',
);
cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(workspaceDir);

process.env['NX_DAEMON'] = 'false';

setWorkspaceRoot(workspaceDir);
});

afterAll(() => {
cwdSpy.mockRestore();
});

describe('create config from all Nx projects', () => {
it('should include eslintrc and patterns of each project', async () => {
await expect(eslintConfigFromNxProjects()).resolves.toEqual({
eslintrc: {
root: true,
overrides: [
{
files: ['packages/cli/**/*.ts', 'packages/cli/package.json'],
extends: './packages/cli/.eslintrc.json',
},
{
files: ['packages/core/**/*.ts', 'packages/core/package.json'],
extends: './packages/core/.eslintrc.json',
},
{
files: [
'packages/nx-plugin/**/*.ts',
'packages/nx-plugin/package.json',
'packages/nx-plugin/generators.json',
],
extends: './packages/nx-plugin/.eslintrc.json',
},
{
files: ['packages/utils/**/*.ts', 'packages/utils/package.json'],
extends: './packages/utils/.eslintrc.json',
},
],
},
patterns: [
'packages/cli/**/*.ts',
'packages/cli/package.json',
'packages/cli/src/*.spec.ts',
'packages/cli/src/*.cy.ts',
'packages/cli/src/*.stories.ts',
'packages/cli/src/.storybook/main.ts',

'packages/core/**/*.ts',
'packages/core/package.json',
'packages/core/src/*.spec.ts',
'packages/core/src/*.cy.ts',
'packages/core/src/*.stories.ts',
'packages/core/src/.storybook/main.ts',

'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',

'packages/utils/**/*.ts',
'packages/utils/package.json',
'packages/utils/src/*.spec.ts',
'packages/utils/src/*.cy.ts',
'packages/utils/src/*.stories.ts',
'packages/utils/src/.storybook/main.ts',
],
} satisfies ESLintPluginConfig);
});
});
});
8 changes: 4 additions & 4 deletions packages/plugin-eslint/src/lib/nx/find-all-projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export async function eslintConfigFromNxProjects(): Promise<ESLintPluginConfig>
const projectGraph = await createProjectGraphAsync({ exitOnError: false });
const projectsConfiguration =
readProjectsConfigurationFromProjectGraph(projectGraph);
const projects = Object.values(projectsConfiguration.projects).filter(
project => 'lint' in (project.targets ?? {}),
);
const projects = Object.values(projectsConfiguration.projects)
.filter(project => 'lint' in (project.targets ?? {}))
.sort((a, b) => a.root.localeCompare(b.root));

// create single ESLint config with project-specific overrides
const eslintConfig: ESLint.ConfigData = {
Expand All @@ -37,7 +37,7 @@ export async function eslintConfigFromNxProjects(): Promise<ESLintPluginConfig>
// HACK: ESLint.calculateConfigForFile won't find rules included only for subsets of *.ts when globs used
// so we explicitly provide additional patterns used by @code-pushup/eslint-config to ensure those rules are included
// this workaround won't be necessary once flat configs are stable (much easier to find all rules)
`${project.sourceRoot}/*.test.ts`, // jest/* and vitest/* rules
`${project.sourceRoot}/*.spec.ts`, // jest/* and vitest/* rules
`${project.sourceRoot}/*.cy.ts`, // cypress/* rules
`${project.sourceRoot}/*.stories.ts`, // storybook/* rules
`${project.sourceRoot}/.storybook/main.ts`, // storybook/no-uninstalled-addons rule
Expand Down
5 changes: 3 additions & 2 deletions packages/plugin-eslint/src/lib/nx/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ProjectConfiguration } from '@nx/devkit';
import { join } from 'node:path';
import { fileExists, toArray } from '@code-pushup/utils';

export async function findCodePushupEslintrc(
Expand All @@ -11,7 +12,7 @@ export async function findCodePushupEslintrc(
// eslint-disable-next-line functional/no-loop-statements
for (const ext of extensions) {
const filename = `./${project.root}/${name}.${ext}`;
if (await fileExists(filename)) {
if (await fileExists(join(process.cwd(), filename))) {
return filename;
}
}
Expand All @@ -32,5 +33,5 @@ export function getEslintConfig(project: ProjectConfiguration): string {
const options = project.targets?.['lint']?.options as
| { eslintConfig?: string }
| undefined;
return options?.eslintConfig ?? `${project.root}/.eslintrc.json`;
return options?.eslintConfig ?? `./${project.root}/.eslintrc.json`;
}

0 comments on commit 6c1edb0

Please sign in to comment.