fix(linter): fix flat config file paths (#20386)

This commit is contained in:
Miroslav Jonaš 2023-11-23 21:43:05 +01:00 committed by GitHub
parent 0e0b3b9e0d
commit 905ef65136
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 74 additions and 122 deletions

View File

@ -381,20 +381,15 @@ exports[`convert-to-flat-config generator should convert json successfully 2`] =
module.exports = [ module.exports = [
...baseConfig, ...baseConfig,
{ {
files: [ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'], files: ['**/*.ts', '**/*.tsx'],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'], files: ['**/*.js', '**/*.jsx'],
rules: {}, rules: {},
}, },
]; ];
@ -451,20 +446,15 @@ exports[`convert-to-flat-config generator should convert yaml successfully 2`] =
module.exports = [ module.exports = [
...baseConfig, ...baseConfig,
{ {
files: [ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'], files: ['**/*.ts', '**/*.tsx'],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'], files: ['**/*.js', '**/*.jsx'],
rules: {}, rules: {},
}, },
]; ];
@ -521,20 +511,15 @@ exports[`convert-to-flat-config generator should convert yml successfully 2`] =
module.exports = [ module.exports = [
...baseConfig, ...baseConfig,
{ {
files: [ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'], files: ['**/*.ts', '**/*.tsx'],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'], files: ['**/*.js', '**/*.jsx'],
rules: {}, rules: {},
}, },
]; ];
@ -547,24 +532,19 @@ exports[`convert-to-flat-config generator should handle custom eslintignores 1`]
module.exports = [ module.exports = [
...baseConfig, ...baseConfig,
{ {
files: [ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'], files: ['**/*.ts', '**/*.tsx'],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'], files: ['**/*.js', '**/*.jsx'],
rules: {}, rules: {},
}, },
{ ignores: ['libs/test-lib/ignore/me'] }, { ignores: ['ignore/me'] },
{ ignores: ['libs/test-lib/ignore/me/as/well'] }, { ignores: ['ignore/me/as/well'] },
]; ];
" "
`; `;

View File

@ -194,10 +194,10 @@ describe('convertEslintJsonToFlatConfig', () => {
{ rules: { "@next/next/no-html-link-for-pages": "off" } }, { rules: { "@next/next/no-html-link-for-pages": "off" } },
{ {
files: [ files: [
"mylib/**/*.ts", "**/*.ts",
"mylib/**/*.tsx", "**/*.tsx",
"mylib/**/*.js", "**/*.js",
"mylib/**/*.jsx" "**/*.jsx"
], ],
rules: { "@next/next/no-html-link-for-pages": [ rules: { "@next/next/no-html-link-for-pages": [
"error", "error",
@ -206,25 +206,25 @@ describe('convertEslintJsonToFlatConfig', () => {
}, },
{ {
files: [ files: [
"mylib/**/*.ts", "**/*.ts",
"mylib/**/*.tsx" "**/*.tsx"
], ],
rules: {} rules: {}
}, },
{ {
files: [ files: [
"mylib/**/*.js", "**/*.js",
"mylib/**/*.jsx" "**/*.jsx"
], ],
rules: {} rules: {}
}, },
...compat.config({ parser: "jsonc-eslint-parser" }).map(config => ({ ...compat.config({ parser: "jsonc-eslint-parser" }).map(config => ({
...config, ...config,
files: ["mylib/**/*.json"], files: ["**/*.json"],
rules: { "@nx/dependency-checks": "error" } rules: { "@nx/dependency-checks": "error" }
})), })),
{ ignores: ["mylib/.next/**/*"] }, { ignores: [".next/**/*"] },
{ ignores: ["mylib/something/else"] } { ignores: ["something/else"] }
]; ];
" "
`); `);

View File

@ -7,10 +7,10 @@ import {
generateFlatOverride, generateFlatOverride,
generatePluginExtendsElement, generatePluginExtendsElement,
generateSpreadElement, generateSpreadElement,
mapFilePath,
stringifyNodeList, stringifyNodeList,
} from '../../utils/flat-config/ast-utils'; } from '../../utils/flat-config/ast-utils';
import { getPluginImport } from '../../utils/eslint-file'; import { getPluginImport } from '../../utils/eslint-file';
import { mapFilePath } from '../../utils/flat-config/path-utils';
/** /**
* Converts an ESLint JSON config to a flat config. * Converts an ESLint JSON config to a flat config.
@ -148,7 +148,7 @@ export function convertEslintJsonToFlatConfig(
) { ) {
isFlatCompatNeeded = true; isFlatCompatNeeded = true;
} }
exportElements.push(generateFlatOverride(override, root)); exportElements.push(generateFlatOverride(override));
}); });
} }
@ -161,7 +161,7 @@ export function convertEslintJsonToFlatConfig(
if (patterns.length > 0) { if (patterns.length > 0) {
exportElements.push( exportElements.push(
generateAst({ generateAst({
ignores: patterns.map((path) => mapFilePath(path, root)), ignores: patterns.map((path) => mapFilePath(path)),
}) })
); );
} }
@ -173,7 +173,7 @@ export function convertEslintJsonToFlatConfig(
.read(ignorePath, 'utf-8') .read(ignorePath, 'utf-8')
.split('\n') .split('\n')
.filter((line) => line.length > 0 && line !== 'node_modules') .filter((line) => line.length > 0 && line !== 'node_modules')
.map((path) => mapFilePath(path, root)); .map((path) => mapFilePath(path));
if (patterns.length > 0) { if (patterns.length > 0) {
exportElements.push(generateAst({ ignores: patterns })); exportElements.push(generateAst({ ignores: patterns }));
} }
@ -188,7 +188,7 @@ export function convertEslintJsonToFlatConfig(
); );
return { return {
content: stringifyNodeList(nodeList, root), content: stringifyNodeList(nodeList),
addESLintRC: isFlatCompatNeeded, addESLintRC: isFlatCompatNeeded,
addESLintJS: isESLintJSNeeded, addESLintJS: isESLintJSNeeded,
}; };

View File

@ -193,20 +193,15 @@ describe('convert-to-flat-config generator', () => {
module.exports = [ module.exports = [
...baseConfig, ...baseConfig,
{ {
files: [ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'], files: ['**/*.ts', '**/*.tsx'],
rules: {}, rules: {},
}, },
{ {
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'], files: ['**/*.js', '**/*.jsx'],
rules: {}, rules: {},
}, },
]; ];

View File

@ -96,7 +96,7 @@ export const getGlobalFlatEslintConfiguration = (
rootProject?: boolean rootProject?: boolean
): string => { ): string => {
const nodeList = createNodeList(new Map(), [], true); const nodeList = createNodeList(new Map(), [], true);
let content = stringifyNodeList(nodeList, ''); let content = stringifyNodeList(nodeList);
content = addImportToFlatConfig(content, 'nxPlugin', '@nx/eslint-plugin'); content = addImportToFlatConfig(content, 'nxPlugin', '@nx/eslint-plugin');
content = addPluginsToExportsBlock(content, [ content = addPluginsToExportsBlock(content, [
{ name: '@nx', varName: 'nxPlugin', imp: '@nx/eslint-plugin' }, { name: '@nx', varName: 'nxPlugin', imp: '@nx/eslint-plugin' },
@ -104,21 +104,21 @@ export const getGlobalFlatEslintConfiguration = (
if (!rootProject) { if (!rootProject) {
content = addBlockToFlatConfigExport( content = addBlockToFlatConfigExport(
content, content,
generateFlatOverride(moduleBoundariesOverride, '') generateFlatOverride(moduleBoundariesOverride)
); );
} }
content = addBlockToFlatConfigExport( content = addBlockToFlatConfigExport(
content, content,
generateFlatOverride(typeScriptOverride, '') generateFlatOverride(typeScriptOverride)
); );
content = addBlockToFlatConfigExport( content = addBlockToFlatConfigExport(
content, content,
generateFlatOverride(javaScriptOverride, '') generateFlatOverride(javaScriptOverride)
); );
if (unitTestRunner === 'jest') { if (unitTestRunner === 'jest') {
content = addBlockToFlatConfigExport( content = addBlockToFlatConfigExport(
content, content,
generateFlatOverride(jestOverride, '') generateFlatOverride(jestOverride)
); );
} }

View File

@ -209,10 +209,10 @@ function createEsLintConfiguration(
nodes.push(generateSpreadElement('baseConfig')); nodes.push(generateSpreadElement('baseConfig'));
} }
overrides.forEach((override) => { overrides.forEach((override) => {
nodes.push(generateFlatOverride(override, projectConfig.root)); nodes.push(generateFlatOverride(override));
}); });
const nodeList = createNodeList(importMap, nodes, isCompatNeeded); const nodeList = createNodeList(importMap, nodes, isCompatNeeded);
const content = stringifyNodeList(nodeList, projectConfig.root); const content = stringifyNodeList(nodeList);
tree.write(join(projectConfig.root, 'eslint.config.js'), content); tree.write(join(projectConfig.root, 'eslint.config.js'), content);
} else { } else {
writeJson(tree, join(projectConfig.root, `.eslintrc.json`), { writeJson(tree, join(projectConfig.root, `.eslintrc.json`), {

View File

@ -17,11 +17,11 @@ import {
generateFlatOverride, generateFlatOverride,
generatePluginExtendsElement, generatePluginExtendsElement,
hasOverride, hasOverride,
mapFilePath,
removeOverridesFromLintConfig, removeOverridesFromLintConfig,
replaceOverride, replaceOverride,
} from './flat-config/ast-utils'; } from './flat-config/ast-utils';
import ts = require('typescript'); import ts = require('typescript');
import { mapFilePath } from './flat-config/path-utils';
export const eslintConfigFileWhitelist = [ export const eslintConfigFileWhitelist = [
'.eslintrc', '.eslintrc',
@ -180,7 +180,7 @@ export function addOverrideToLintConfig(
root, root,
isBase ? baseEsLintFlatConfigFile : 'eslint.config.js' isBase ? baseEsLintFlatConfigFile : 'eslint.config.js'
); );
const flatOverride = generateFlatOverride(override, root); const flatOverride = generateFlatOverride(override);
let content = tree.read(fileName, 'utf8'); let content = tree.read(fileName, 'utf8');
// we will be using compat here so we need to make sure it's added // we will be using compat here so we need to make sure it's added
if (overrideNeedsCompat(override)) { if (overrideNeedsCompat(override)) {
@ -282,7 +282,7 @@ export function replaceOverridesInLintConfig(
} }
content = removeOverridesFromLintConfig(content); content = removeOverridesFromLintConfig(content);
overrides.forEach((override) => { overrides.forEach((override) => {
const flatOverride = generateFlatOverride(override, root); const flatOverride = generateFlatOverride(override);
addBlockToFlatConfigExport(content, flatOverride); addBlockToFlatConfigExport(content, flatOverride);
}); });
@ -359,7 +359,7 @@ export function addIgnoresToLintConfig(
if (useFlatConfig(tree)) { if (useFlatConfig(tree)) {
const fileName = joinPathFragments(root, 'eslint.config.js'); const fileName = joinPathFragments(root, 'eslint.config.js');
const block = generateAst<ts.ObjectLiteralExpression>({ const block = generateAst<ts.ObjectLiteralExpression>({
ignores: ignorePatterns.map((path) => mapFilePath(path, root)), ignores: ignorePatterns.map((path) => mapFilePath(path)),
}); });
tree.write( tree.write(
fileName, fileName,

View File

@ -6,6 +6,7 @@ import {
} from '@nx/devkit'; } from '@nx/devkit';
import { Linter } from 'eslint'; import { Linter } from 'eslint';
import * as ts from 'typescript'; import * as ts from 'typescript';
import { mapFilePath } from './path-utils';
/** /**
* Remove all overrides from the config file * Remove all overrides from the config file
@ -166,7 +167,7 @@ export function replaceOverride(
length: end - start, length: end - start,
}); });
const updatedData = update(data); const updatedData = update(data);
mapFilePaths(updatedData, root); mapFilePaths(updatedData);
changes.push({ changes.push({
type: ChangeType.Insert, type: ChangeType.Insert,
index: start, index: start,
@ -694,12 +695,11 @@ export function stringifyNodeList(
| ts.Identifier | ts.Identifier
| ts.ExpressionStatement | ts.ExpressionStatement
| ts.SourceFile | ts.SourceFile
>, >
root: string
): string { ): string {
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
const resultFile = ts.createSourceFile( const resultFile = ts.createSourceFile(
joinPathFragments(root, ''), '',
'', '',
ts.ScriptTarget.Latest, ts.ScriptTarget.Latest,
true, true,
@ -749,10 +749,9 @@ export function generateRequire(
* Generates AST object or spread element based on JSON override object * Generates AST object or spread element based on JSON override object
*/ */
export function generateFlatOverride( export function generateFlatOverride(
override: Linter.ConfigOverride<Linter.RulesRecord>, override: Linter.ConfigOverride<Linter.RulesRecord>
root: string
): ts.ObjectLiteralExpression | ts.SpreadElement { ): ts.ObjectLiteralExpression | ts.SpreadElement {
mapFilePaths(override, root); mapFilePaths(override);
if ( if (
!override.env && !override.env &&
!override.extends && !override.extends &&
@ -810,43 +809,24 @@ export function generateFlatOverride(
} }
export function mapFilePaths( export function mapFilePaths(
override: Linter.ConfigOverride<Linter.RulesRecord>, override: Linter.ConfigOverride<Linter.RulesRecord>
root: string
) { ) {
if (override.files) { if (override.files) {
override.files = Array.isArray(override.files) override.files = Array.isArray(override.files)
? override.files ? override.files
: [override.files]; : [override.files];
override.files = override.files.map((file) => mapFilePath(file, root)); override.files = override.files.map((file) => mapFilePath(file));
} }
if (override.excludedFiles) { if (override.excludedFiles) {
override.excludedFiles = Array.isArray(override.excludedFiles) override.excludedFiles = Array.isArray(override.excludedFiles)
? override.excludedFiles ? override.excludedFiles
: [override.excludedFiles]; : [override.excludedFiles];
override.excludedFiles = override.excludedFiles.map((file) => override.excludedFiles = override.excludedFiles.map((file) =>
mapFilePath(file, root) mapFilePath(file)
); );
} }
} }
export function mapFilePath(filePath: string, root: string) {
if (filePath.startsWith('!')) {
const fileWithoutBang = filePath.slice(1);
if (fileWithoutBang.startsWith('*.')) {
return `!${joinPathFragments(root, '**', fileWithoutBang)}`;
} else if (!fileWithoutBang.startsWith(root)) {
return `!${joinPathFragments(root, fileWithoutBang)}`;
}
return filePath;
}
if (filePath.startsWith('*.')) {
return joinPathFragments(root, '**', filePath);
} else if (!filePath.startsWith(root)) {
return joinPathFragments(root, filePath);
}
return filePath;
}
function addTSObjectProperty( function addTSObjectProperty(
elements: ts.ObjectLiteralElementLike[], elements: ts.ObjectLiteralElementLike[],
key: string, key: string,

View File

@ -2,30 +2,27 @@ import { joinPathFragments } from '@nx/devkit';
import type { Linter } from 'eslint'; import type { Linter } from 'eslint';
export function updateFiles( export function updateFiles(
override: Linter.ConfigOverride<Linter.RulesRecord>, override: Linter.ConfigOverride<Linter.RulesRecord>
root: string
) { ) {
if (override.files) { if (override.files) {
override.files = Array.isArray(override.files) override.files = Array.isArray(override.files)
? override.files ? override.files
: [override.files]; : [override.files];
override.files = override.files.map((file) => mapFilePath(file, root)); override.files = override.files.map((file) => mapFilePath(file));
} }
return override; return override;
} }
function mapFilePath(filePath: string, root: string) { export function mapFilePath(filePath: string) {
if (filePath.startsWith('!')) { if (filePath.startsWith('!')) {
const fileWithoutBang = filePath.slice(1); const fileWithoutBang = filePath.slice(1);
if (fileWithoutBang.startsWith('*.')) { if (fileWithoutBang.startsWith('*.')) {
return `!${joinPathFragments(root, '**', fileWithoutBang)}`; return `!${joinPathFragments('**', fileWithoutBang)}`;
} else {
return `!${joinPathFragments(root, fileWithoutBang)}`;
} }
return filePath;
} }
if (filePath.startsWith('*.')) { if (filePath.startsWith('*.')) {
return joinPathFragments(root, '**', filePath); return joinPathFragments('**', filePath);
} else {
return joinPathFragments(root, filePath);
} }
return filePath;
} }

View File

@ -133,16 +133,16 @@ describe('updateEslint', () => {
module.exports = [ module.exports = [
{ {
files: ["my-app/**/*.*"], files: ["**/*.*"],
rules: { "@next/next/no-html-link-for-pages": "off" } rules: { "@next/next/no-html-link-for-pages": "off" }
}, },
...baseConfig, ...baseConfig,
{ {
"files": [ "files": [
"my-app/**/*.ts", "**/*.ts",
"my-app/**/*.tsx", "**/*.tsx",
"my-app/**/*.js", "**/*.js",
"my-app/**/*.jsx" "**/*.jsx"
], ],
"rules": { "rules": {
"@next/next/no-html-link-for-pages": [ "@next/next/no-html-link-for-pages": [
@ -153,15 +153,15 @@ describe('updateEslint', () => {
}, },
{ {
files: [ files: [
"my-app/**/*.ts", "**/*.ts",
"my-app/**/*.tsx" "**/*.tsx"
], ],
rules: {} rules: {}
}, },
{ {
files: [ files: [
"my-app/**/*.js", "**/*.js",
"my-app/**/*.jsx" "**/*.jsx"
], ],
rules: {} rules: {}
}, },
@ -169,13 +169,13 @@ describe('updateEslint', () => {
...compat.config({ env: { jest: true } }).map(config => ({ ...compat.config({ env: { jest: true } }).map(config => ({
...config, ...config,
files: [ files: [
"my-app/**/*.spec.ts", "**/*.spec.ts",
"my-app/**/*.spec.tsx", "**/*.spec.tsx",
"my-app/**/*.spec.js", "**/*.spec.js",
"my-app/**/*.spec.jsx" "**/*.spec.jsx"
] ]
})), })),
{ ignores: ["my-app/.next/**/*"] } { ignores: [".next/**/*"] }
]; ];
" "
`); `);