Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(linter): add flat config support to generators #18534

Merged
merged 30 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f5f4105
feat(linter): add flat support to lint-project
meeroslav Aug 8, 2023
8925d82
feat(linter): move ast logic to utils
meeroslav Aug 9, 2023
a69f3f2
feat(linter): move ast logic to utils
meeroslav Aug 9, 2023
146ef89
feat(linter): add addImportToFlatConfig
meeroslav Aug 9, 2023
7fdfa77
feat(linter): add addCompatToFlatConfig
meeroslav Aug 9, 2023
7e0e038
feat(linter): add flat support to react-native
meeroslav Aug 9, 2023
00927de
feat(linter): add flat support to react, detox and expo
meeroslav Aug 9, 2023
f56d2ad
feat(linter): simplify cypress
meeroslav Aug 9, 2023
67501c4
feat(linter): handle node
meeroslav Aug 9, 2023
83fc7ad
feat(linter): handle js
meeroslav Aug 9, 2023
21df9c6
feat(linter): handle angular
meeroslav Aug 9, 2023
233e5f6
fix(linter): escape circular dependency
meeroslav Aug 9, 2023
c8bb741
fix(linter): add support to cypress
meeroslav Aug 9, 2023
42193df
fix(linter): fix unit tests
meeroslav Aug 9, 2023
634e384
fix(linter): add support for move command
meeroslav Aug 9, 2023
703ce01
fix(linter): fix broken code
meeroslav Aug 10, 2023
9dbd2f9
fix(linter): add support for flat config in cypress
meeroslav Aug 10, 2023
25438cb
fix(linter): fix e2e faulty generation
meeroslav Aug 10, 2023
dfb4c1a
fix(linter): minor storybook improvement
meeroslav Aug 10, 2023
74833fc
fix(linter): support next
meeroslav Aug 11, 2023
8ae0994
fix(linter): support compat for override replace
meeroslav Aug 11, 2023
4a3dc32
fix(linter): enable plugin
meeroslav Aug 11, 2023
a510d66
fix(linter): ensure root is set for flat config
meeroslav Aug 11, 2023
b29070f
fix(linter): ensure all config manipulations are supported
meeroslav Aug 11, 2023
948e16f
feat(linter): support flat config migration
meeroslav Aug 11, 2023
5705f26
fix(linter): add support for plugin removal and base import
meeroslav Aug 12, 2023
8e90737
cleanup(linter): cleanup json converter
meeroslav Aug 14, 2023
5c3ab1b
feat(linter): move move logic to linter
meeroslav Aug 18, 2023
3dae274
fix(linter): remove old todo
meeroslav Aug 22, 2023
37699ef
feat(linter): add disclaimer for when nx/linter is missing
meeroslav Aug 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,9 @@
"@swc/core": "^1.3.51",
"@swc/jest": "^0.2.20",
"@testing-library/react": "13.4.0",
"@types/css-minimizer-webpack-plugin": "^3.2.1",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a stub definition, css-minimizer-webpack-plugin already contains types

"@types/cytoscape": "^3.18.2",
"@types/detect-port": "^1.3.2",
"@types/eslint": "~8.4.1",
"@types/eslint": "~8.44.2",
"@types/express": "4.17.14",
"@types/flat": "^5.0.1",
"@types/fs-extra": "^11.0.0",
Expand Down
64 changes: 57 additions & 7 deletions packages/angular/src/generators/add-linting/add-linting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import {
joinPathFragments,
runTasksInSerial,
Tree,
updateJson,
} from '@nx/devkit';
import { Linter, lintProjectGenerator } from '@nx/linter';
import { mapLintPattern } from '@nx/linter/src/generators/lint-project/lint-project';
import { addAngularEsLintDependencies } from './lib/add-angular-eslint-dependencies';
import { extendAngularEslintJson } from './lib/create-eslint-configuration';
import type { AddLintingGeneratorSchema } from './schema';
import {
findEslintFile,
isEslintConfigSupported,
replaceOverridesInLintConfig,
} from '@nx/linter/src/generators/utils/eslint-file';
import { camelize, dasherize } from '@nx/devkit/src/utils/string-utils';

export async function addLintingGenerator(
tree: Tree,
Expand All @@ -35,11 +39,57 @@ export async function addLintingGenerator(
});
tasks.push(lintTask);

updateJson(
tree,
joinPathFragments(options.projectRoot, '.eslintrc.json'),
(json) => extendAngularEslintJson(json, options)
);
if (isEslintConfigSupported(tree)) {
const eslintFile = findEslintFile(tree, options.projectRoot);
// keep parser options if they exist
const hasParserOptions = tree
.read(joinPathFragments(options.projectRoot, eslintFile), 'utf8')
.includes(`${options.projectRoot}/tsconfig.*?.json`);

replaceOverridesInLintConfig(tree, options.projectRoot, [
{
files: ['*.ts'],
...(hasParserOptions
? {
parserOptions: {
project: [`${options.projectRoot}/tsconfig.*?.json`],
},
}
: {}),
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: camelize(options.prefix),
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: dasherize(options.prefix),
style: 'kebab-case',
},
],
},
},
{
files: ['*.html'],
extends: ['plugin:@nx/angular-template'],
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {},
},
]);
}

if (!options.skipPackageJson) {
const installTask = addAngularEsLintDependencies(tree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ type EslintExtensionSchema = {
prefix: string;
};

/**
* @deprecated Use tools from `@nx/linter/src/generators/utils/eslint-file` instead
*/
export const extendAngularEslintJson = (
json: Linter.Config,
options: EslintExtensionSchema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('Cypress e2e configuration', () => {

beforeEach(() => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
tree.write('.eslintrc.json', '{}'); // we are explicitly checking for existance of config type
});

afterAll(() => {
Expand Down
108 changes: 61 additions & 47 deletions packages/cypress/src/utils/add-linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import {
readProjectConfiguration,
runTasksInSerial,
Tree,
updateJson,
} from '@nx/devkit';
import { Linter, lintProjectGenerator } from '@nx/linter';
import { globalJavaScriptOverrides } from '@nx/linter/src/generators/init/global-eslint-config';
import { installedCypressVersion } from './cypress-version';
import { eslintPluginCypressVersion } from './versions';
import {
addExtendsToLintConfig,
addOverrideToLintConfig,
addPluginsToLintConfig,
findEslintFile,
isEslintConfigSupported,
replaceOverridesInLintConfig,
} from '@nx/linter/src/generators/utils/eslint-file';
import { javaScriptOverride } from '@nx/linter/src/generators/init/global-eslint-config';

export interface CyLinterOptions {
project: string;
Expand Down Expand Up @@ -42,7 +49,8 @@ export async function addLinterToCyProject(
const tasks: GeneratorCallback[] = [];
const projectConfig = readProjectConfiguration(tree, options.project);

if (!tree.exists(joinPathFragments(projectConfig.root, '.eslintrc.json'))) {
const eslintFile = findEslintFile(tree, projectConfig.root);
if (!eslintFile) {
tasks.push(
await lintProjectGenerator(tree, {
project: options.project,
Expand Down Expand Up @@ -73,60 +81,66 @@ export async function addLinterToCyProject(
: () => {}
);

updateJson(
tree,
joinPathFragments(projectConfig.root, '.eslintrc.json'),
(json) => {
if (options.rootProject) {
json.plugins = ['@nx'];
json.extends = ['plugin:cypress/recommended'];
} else {
json.extends = ['plugin:cypress/recommended', ...json.extends];
}
json.overrides ??= [];
const globals = options.rootProject ? [globalJavaScriptOverrides] : [];
const override = {
if (isEslintConfigSupported(tree)) {
const overrides = [];
if (options.rootProject) {
addPluginsToLintConfig(tree, projectConfig.root, '@nx');
overrides.push(javaScriptOverride);
}
addExtendsToLintConfig(
tree,
projectConfig.root,
'plugin:cypress/recommended'
);
const cyVersion = installedCypressVersion();
/**
* We need this override because we enabled allowJS in the tsconfig to allow for JS based Cypress tests.
* That however leads to issues with the CommonJS Cypress plugin file.
*/
const cy6Override = {
files: [`${options.cypressDir}/plugins/index.js`],
rules: {
'@typescript-eslint/no-var-requires': 'off',
'no-undef': 'off',
},
};
const addCy6Override = cyVersion && cyVersion < 7;

if (options.overwriteExisting) {
overrides.push({
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
parserOptions: !options.setParserOptionsProject
? undefined
: {
project: `${projectConfig.root}/tsconfig.*?.json`,
},
rules: {},
};
const cyFiles = [
{
...override,
files: [
'*.cy.{ts,js,tsx,jsx}',
`${options.cypressDir}/**/*.{ts,js,tsx,jsx}`,
],
},
];
if (options.overwriteExisting) {
json.overrides = [...globals, override];
} else {
json.overrides.push(...globals);
json.overrides.push(...cyFiles);
});
if (addCy6Override) {
overrides.push(cy6Override);
}

const cyVersion = installedCypressVersion();
if (cyVersion && cyVersion < 7) {
/**
* We need this override because we enabled allowJS in the tsconfig to allow for JS based Cypress tests.
* That however leads to issues with the CommonJS Cypress plugin file.
*/
json.overrides.push({
files: [`${options.cypressDir}/plugins/index.js`],
rules: {
'@typescript-eslint/no-var-requires': 'off',
'no-undef': 'off',
},
});
replaceOverridesInLintConfig(tree, projectConfig.root, overrides);
} else {
overrides.unshift({
files: [
'*.cy.{ts,js,tsx,jsx}',
`${options.cypressDir}/**/*.{ts,js,tsx,jsx}`,
],
parserOptions: !options.setParserOptionsProject
? undefined
: {
project: `${projectConfig.root}/tsconfig.*?.json`,
},
rules: {},
});
if (addCy6Override) {
overrides.push(cy6Override);
}
return json;
overrides.forEach((override) =>
addOverrideToLintConfig(tree, projectConfig.root, override)
);
}
);
}

return runTasksInSerial(...tasks);
}
15 changes: 8 additions & 7 deletions packages/detox/src/generators/application/lib/add-linting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import {
joinPathFragments,
runTasksInSerial,
Tree,
updateJson,
} from '@nx/devkit';
import { extendReactEslintJson, extraEslintDependencies } from '@nx/react';
import { extraEslintDependencies } from '@nx/react';
import { NormalizedSchema } from './normalize-options';
import {
addExtendsToLintConfig,
isEslintConfigSupported,
} from '@nx/linter/src/generators/utils/eslint-file';

export async function addLinting(host: Tree, options: NormalizedSchema) {
if (options.linter === Linter.None) {
Expand All @@ -24,11 +27,9 @@ export async function addLinting(host: Tree, options: NormalizedSchema) {
skipFormat: true,
});

updateJson(
host,
joinPathFragments(options.e2eProjectRoot, '.eslintrc.json'),
extendReactEslintJson
);
if (isEslintConfigSupported(host)) {
addExtendsToLintConfig(host, options.e2eProjectRoot, 'plugin:@nx/react');
}

const installTask = addDependenciesToPackageJson(
host,
Expand Down
38 changes: 14 additions & 24 deletions packages/expo/src/utils/add-linting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import { Linter, lintProjectGenerator } from '@nx/linter';
import {
addDependenciesToPackageJson,
GeneratorCallback,
joinPathFragments,
runTasksInSerial,
Tree,
updateJson,
} from '@nx/devkit';
import { extraEslintDependencies } from '@nx/react/src/utils/lint';
import {
extendReactEslintJson,
extraEslintDependencies,
} from '@nx/react/src/utils/lint';
import type { Linter as ESLintLinter } from 'eslint';
addExtendsToLintConfig,
addIgnoresToLintConfig,
isEslintConfigSupported,
} from '@nx/linter/src/generators/utils/eslint-file';

interface NormalizedSchema {
linter?: Linter;
Expand Down Expand Up @@ -39,24 +38,15 @@ export async function addLinting(host: Tree, options: NormalizedSchema) {

tasks.push(lintTask);

updateJson(
host,
joinPathFragments(options.projectRoot, '.eslintrc.json'),
(json: ESLintLinter.Config) => {
json = extendReactEslintJson(json);

json.ignorePatterns = [
...json.ignorePatterns,
'.expo',
'node_modules',
'web-build',
'cache',
'dist',
];

return json;
}
);
if (isEslintConfigSupported(host)) {
addExtendsToLintConfig(host, options.projectRoot, 'plugin:@nx/react');
addIgnoresToLintConfig(host, options.projectRoot, [
'.expo',
'web-build',
'cache',
'dist',
]);
}

if (!options.skipPackageJson) {
const installTask = await addDependenciesToPackageJson(
Expand Down
16 changes: 10 additions & 6 deletions packages/js/src/generators/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,20 +256,24 @@ export async function addLint(
setParserOptionsProject: options.setParserOptionsProject,
rootProject: options.rootProject,
});
// Also update the root .eslintrc.json lintProjectGenerator will not generate it for root projects.
// Also update the root ESLint config. The lintProjectGenerator will not generate it for root projects.
// But we need to set the package.json checks.
if (options.rootProject) {
updateJson(tree, '.eslintrc.json', (json) => {
json.overrides ??= [];
json.overrides.push({
const {
addOverrideToLintConfig,
isEslintConfigSupported,
// nx-ignore-next-line
} = require('@nx/linter/src/generators/utils/eslint-file');

if (isEslintConfigSupported(tree)) {
addOverrideToLintConfig(tree, '', {
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'@nx/dependency-checks': 'error',
},
});
return json;
});
}
}
return task;
}
Expand Down
Loading