feat(linter): support yaml for flat config conversion (#20022)

This commit is contained in:
Miroslav Jonaš 2023-11-22 20:37:51 +01:00 committed by GitHub
parent 31df83bb2f
commit d1a213fa4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 386 additions and 115 deletions

View File

@ -30,7 +30,8 @@
"executors": "./executors.json",
"generators": "./generators.json",
"peerDependencies": {
"eslint": "^8.0.0"
"eslint": "^8.0.0",
"js-yaml": "4.1.0"
},
"dependencies": {
"tslib": "^2.3.0",
@ -41,6 +42,9 @@
"peerDependenciesMeta": {
"eslint": {
"optional": true
},
"js-yaml": {
"optional": true
}
},
"publishConfig": {

View File

@ -5,10 +5,12 @@ exports[`convert-to-flat-config generator should add env configuration 1`] = `
const nxEslintPlugin = require('@nx/eslint-plugin');
const globals = require('globals');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
@ -49,10 +51,12 @@ exports[`convert-to-flat-config generator should add global and env configuratio
const nxEslintPlugin = require('@nx/eslint-plugin');
const globals = require('globals');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{
@ -96,10 +100,12 @@ exports[`convert-to-flat-config generator should add global configuration 1`] =
"const { FlatCompat } = require('@eslint/eslintrc');
const nxEslintPlugin = require('@nx/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{ languageOptions: { globals: { myCustomGlobal: 'readonly' } } },
@ -139,10 +145,12 @@ exports[`convert-to-flat-config generator should add global eslintignores 1`] =
"const { FlatCompat } = require('@eslint/eslintrc');
const nxEslintPlugin = require('@nx/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{
@ -183,10 +191,12 @@ exports[`convert-to-flat-config generator should add parser 1`] = `
const nxEslintPlugin = require('@nx/eslint-plugin');
const typescriptEslintParser = require('@typescript-eslint/parser');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{ languageOptions: { parser: typescriptEslintParser } },
@ -229,10 +239,12 @@ const eslintPluginSingleName = require('eslint-plugin-single-name');
const scopeEslintPluginWithName = require('@scope/eslint-plugin-with-name');
const justScopeEslintPlugin = require('@just-scope/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{
plugins: {
@ -278,10 +290,12 @@ exports[`convert-to-flat-config generator should add settings 1`] = `
"const { FlatCompat } = require('@eslint/eslintrc');
const nxEslintPlugin = require('@nx/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{ settings: { sharedData: 'Hello' } },
@ -317,41 +331,16 @@ module.exports = [
"
`;
exports[`convert-to-flat-config generator should handle custom eslintignores 1`] = `
"const baseConfig = require('../../eslint.config.js');
module.exports = [
...baseConfig,
{
files: [
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {},
},
{
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'],
rules: {},
},
{
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'],
rules: {},
},
{ ignores: ['libs/test-lib/ignore/me'] },
{ ignores: ['libs/test-lib/ignore/me/as/well'] },
];
"
`;
exports[`convert-to-flat-config generator should run successfully 1`] = `
exports[`convert-to-flat-config generator should convert json successfully 1`] = `
"const { FlatCompat } = require('@eslint/eslintrc');
const nxEslintPlugin = require('@nx/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{
@ -386,8 +375,9 @@ module.exports = [
"
`;
exports[`convert-to-flat-config generator should run successfully 2`] = `
exports[`convert-to-flat-config generator should convert json successfully 2`] = `
"const baseConfig = require('../../eslint.config.js');
module.exports = [
...baseConfig,
{
@ -410,3 +400,171 @@ module.exports = [
];
"
`;
exports[`convert-to-flat-config generator should convert yaml successfully 1`] = `
"const { FlatCompat } = require('@eslint/eslintrc');
const nxEslintPlugin = require('@nx/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{
sourceTag: '*',
onlyDependOnLibsWithTags: ['*'],
},
],
},
],
},
},
...compat.config({ extends: ['plugin:@nx/typescript'] }).map((config) => ({
...config,
files: ['**/*.ts', '**/*.tsx'],
rules: {},
})),
...compat.config({ extends: ['plugin:@nx/javascript'] }).map((config) => ({
...config,
files: ['**/*.js', '**/*.jsx'],
rules: {},
})),
];
"
`;
exports[`convert-to-flat-config generator should convert yaml successfully 2`] = `
"const baseConfig = require('../../eslint.config.js');
module.exports = [
...baseConfig,
{
files: [
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {},
},
{
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'],
rules: {},
},
{
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'],
rules: {},
},
];
"
`;
exports[`convert-to-flat-config generator should convert yml successfully 1`] = `
"const { FlatCompat } = require('@eslint/eslintrc');
const nxEslintPlugin = require('@nx/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{
sourceTag: '*',
onlyDependOnLibsWithTags: ['*'],
},
],
},
],
},
},
...compat.config({ extends: ['plugin:@nx/typescript'] }).map((config) => ({
...config,
files: ['**/*.ts', '**/*.tsx'],
rules: {},
})),
...compat.config({ extends: ['plugin:@nx/javascript'] }).map((config) => ({
...config,
files: ['**/*.js', '**/*.jsx'],
rules: {},
})),
];
"
`;
exports[`convert-to-flat-config generator should convert yml successfully 2`] = `
"const baseConfig = require('../../eslint.config.js');
module.exports = [
...baseConfig,
{
files: [
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {},
},
{
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'],
rules: {},
},
{
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'],
rules: {},
},
];
"
`;
exports[`convert-to-flat-config generator should handle custom eslintignores 1`] = `
"const baseConfig = require('../../eslint.config.js');
module.exports = [
...baseConfig,
{
files: [
'libs/test-lib/**/*.ts',
'libs/test-lib/**/*.tsx',
'libs/test-lib/**/*.js',
'libs/test-lib/**/*.jsx',
],
rules: {},
},
{
files: ['libs/test-lib/**/*.ts', 'libs/test-lib/**/*.tsx'],
rules: {},
},
{
files: ['libs/test-lib/**/*.js', 'libs/test-lib/**/*.jsx'],
rules: {},
},
{ ignores: ['libs/test-lib/ignore/me'] },
{ ignores: ['libs/test-lib/ignore/me/as/well'] },
];
"
`;

View File

@ -1,4 +1,4 @@
import { Tree } from '@nx/devkit';
import { Tree, readJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { convertEslintJsonToFlatConfig } from './json-converter';
@ -58,22 +58,23 @@ describe('convertEslintJsonToFlatConfig', () => {
tree.write('.eslintignore', 'node_modules\nsomething/else');
convertEslintJsonToFlatConfig(
const { content } = convertEslintJsonToFlatConfig(
tree,
'',
'.eslintrc.json',
'eslint.config.js',
readJson(tree, '.eslintrc.json'),
['.eslintignore']
);
expect(tree.read('eslint.config.js', 'utf-8')).toMatchInlineSnapshot(`
expect(content).toMatchInlineSnapshot(`
"const { FlatCompat } = require("@eslint/eslintrc");
const nxEslintPlugin = require("@nx/eslint-plugin");
const js = require("@eslint/js");
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { "@nx": nxEslintPlugin } },
{
@ -118,8 +119,6 @@ describe('convertEslintJsonToFlatConfig', () => {
];
"
`);
expect(tree.exists('.eslintrc.json')).toBeFalsy();
});
it('should convert project configs', async () => {
@ -170,23 +169,24 @@ describe('convertEslintJsonToFlatConfig', () => {
tree.write('mylib/.eslintignore', 'node_modules\nsomething/else');
convertEslintJsonToFlatConfig(
const { content } = convertEslintJsonToFlatConfig(
tree,
'mylib',
'.eslintrc.json',
'eslint.config.js',
readJson(tree, 'mylib/.eslintrc.json'),
['mylib/.eslintignore']
);
expect(tree.read('mylib/eslint.config.js', 'utf-8')).toMatchInlineSnapshot(`
expect(content).toMatchInlineSnapshot(`
"const { FlatCompat } = require("@eslint/eslintrc");
const baseConfig = require("../../eslint.config.js");
const globals = require("globals");
const js = require("@eslint/js");
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
...baseConfig,
...compat.extends("plugin:@nx/react-typescript", "next", "next/core-web-vitals"),
@ -228,7 +228,5 @@ describe('convertEslintJsonToFlatConfig', () => {
];
"
`);
expect(tree.exists('mylib/.eslintrc.json')).toBeFalsy();
});
});

View File

@ -1,13 +1,6 @@
import {
Tree,
addDependenciesToPackageJson,
names,
readJson,
} from '@nx/devkit';
import { join } from 'path';
import { Tree, names } from '@nx/devkit';
import { ESLint } from 'eslint';
import * as ts from 'typescript';
import { eslintVersion, eslintrcVersion } from '../../../utils/versions';
import {
createNodeList,
generateAst,
@ -26,21 +19,20 @@ import { getPluginImport } from '../../utils/eslint-file';
export function convertEslintJsonToFlatConfig(
tree: Tree,
root: string,
sourceFile: string,
destinationFile: string,
config: ESLint.ConfigData,
ignorePaths: string[]
) {
): { content: string; addESLintRC: boolean; addESLintJS: boolean } {
const importsMap = new Map<string, string>();
const exportElements: ts.Expression[] = [];
let isFlatCompatNeeded = false;
let isESLintJSNeeded = false;
let combinedConfig: ts.PropertyAssignment[] = [];
let languageOptions: ts.PropertyAssignment[] = [];
// read original config
const config: ESLint.ConfigData = readJson(tree, `${root}/${sourceFile}`);
if (config.extends) {
isFlatCompatNeeded = addExtends(importsMap, exportElements, config, tree);
const extendsResult = addExtends(importsMap, exportElements, config);
isFlatCompatNeeded = extendsResult.isFlatCompatNeeded;
isESLintJSNeeded = extendsResult.isESLintJSNeeded;
}
if (config.plugins) {
@ -188,36 +180,28 @@ export function convertEslintJsonToFlatConfig(
}
}
tree.delete(join(root, sourceFile));
// create the node list and print it to new file
const nodeList = createNodeList(
importsMap,
exportElements,
isFlatCompatNeeded
);
const content = stringifyNodeList(nodeList, root, destinationFile);
tree.write(join(root, destinationFile), content);
if (isFlatCompatNeeded) {
addDependenciesToPackageJson(
tree,
{},
{
'@eslint/eslintrc': eslintrcVersion,
}
);
}
return {
content: stringifyNodeList(nodeList, root),
addESLintRC: isFlatCompatNeeded,
addESLintJS: isESLintJSNeeded,
};
}
// add parsed extends to export blocks and add import statements
function addExtends(
importsMap: Map<string, string | string[]>,
configBlocks: ts.Expression[],
config: ESLint.ConfigData,
tree: Tree
): boolean {
config: ESLint.ConfigData
): { isFlatCompatNeeded: boolean; isESLintJSNeeded: boolean } {
let isFlatCompatNeeded = false;
let isESLintJSNeeded = false;
const extendsConfig = Array.isArray(config.extends)
? config.extends
: [config.extends];
@ -253,13 +237,7 @@ function addExtends(
});
if (eslintPluginExtends.length) {
addDependenciesToPackageJson(
tree,
{},
{
'@eslint/js': eslintVersion,
}
);
isESLintJSNeeded = true;
importsMap.set('@eslint/js', 'js');
eslintPluginExtends.forEach((plugin) => {
@ -277,18 +255,12 @@ function addExtends(
}
if (eslintrcConfigs.length) {
isFlatCompatNeeded = true;
addDependenciesToPackageJson(
tree,
{},
{
'@eslint/js': eslintVersion,
}
);
isESLintJSNeeded = true;
configBlocks.push(generatePluginExtendsElement(eslintrcConfigs));
}
return isFlatCompatNeeded;
return { isFlatCompatNeeded, isESLintJSNeeded };
}
function addPlugins(

View File

@ -12,7 +12,7 @@ import { ConvertToFlatConfigGeneratorSchema } from './schema';
import { lintProjectGenerator } from '../lint-project/lint-project';
import { Linter } from '../utils/linter';
import { eslintrcVersion } from '../../utils/versions';
import { read } from 'fs';
import { dump } from 'js-yaml';
describe('convert-to-flat-config generator', () => {
let tree: Tree;
@ -42,7 +42,7 @@ describe('convert-to-flat-config generator', () => {
});
});
it('should run successfully', async () => {
it('should convert json successfully', async () => {
await lintProjectGenerator(tree, {
skipFormat: false,
linter: Linter.EsLint,
@ -68,6 +68,66 @@ describe('convert-to-flat-config generator', () => {
);
});
it('should convert yaml successfully', async () => {
await lintProjectGenerator(tree, {
skipFormat: false,
linter: Linter.EsLint,
eslintFilePatterns: ['**/*.ts'],
project: 'test-lib',
setParserOptionsProject: false,
});
const yamlContent = dump(readJson(tree, 'libs/test-lib/.eslintrc.json'));
tree.delete('libs/test-lib/.eslintrc.json');
tree.write('libs/test-lib/.eslintrc.yaml', yamlContent);
await convertToFlatConfigGenerator(tree, options);
expect(tree.exists('eslint.config.js')).toBeTruthy();
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeTruthy();
expect(
tree.read('libs/test-lib/eslint.config.js', 'utf-8')
).toMatchSnapshot();
// check nx.json changes
const nxJson = readJson(tree, 'nx.json');
expect(nxJson.targetDefaults.lint.inputs).toContain(
'{workspaceRoot}/eslint.config.js'
);
expect(nxJson.namedInputs.production).toContain(
'!{projectRoot}/eslint.config.js'
);
});
it('should convert yml successfully', async () => {
await lintProjectGenerator(tree, {
skipFormat: false,
linter: Linter.EsLint,
eslintFilePatterns: ['**/*.ts'],
project: 'test-lib',
setParserOptionsProject: false,
});
const yamlContent = dump(readJson(tree, 'libs/test-lib/.eslintrc.json'));
tree.delete('libs/test-lib/.eslintrc.json');
tree.write('libs/test-lib/.eslintrc.yml', yamlContent);
await convertToFlatConfigGenerator(tree, options);
expect(tree.exists('eslint.config.js')).toBeTruthy();
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeTruthy();
expect(
tree.read('libs/test-lib/eslint.config.js', 'utf-8')
).toMatchSnapshot();
// check nx.json changes
const nxJson = readJson(tree, 'nx.json');
expect(nxJson.targetDefaults.lint.inputs).toContain(
'{workspaceRoot}/eslint.config.js'
);
expect(nxJson.namedInputs.production).toContain(
'!{projectRoot}/eslint.config.js'
);
});
it('should add plugin extends', async () => {
await lintProjectGenerator(tree, {
skipFormat: false,
@ -86,10 +146,12 @@ describe('convert-to-flat-config generator', () => {
"const { FlatCompat } = require('@eslint/eslintrc');
const nxEslintPlugin = require('@nx/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
...compat.extends('plugin:storybook/recommended'),
{ plugins: { '@nx': nxEslintPlugin } },
@ -127,6 +189,7 @@ describe('convert-to-flat-config generator', () => {
expect(tree.read('libs/test-lib/eslint.config.js', 'utf-8'))
.toMatchInlineSnapshot(`
"const baseConfig = require('../../eslint.config.js');
module.exports = [
...baseConfig,
{
@ -337,10 +400,12 @@ describe('convert-to-flat-config generator', () => {
"const { FlatCompat } = require('@eslint/eslintrc');
const nxEslintPlugin = require('@nx/eslint-plugin');
const js = require('@eslint/js');
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
module.exports = [
{ plugins: { '@nx': nxEslintPlugin } },
{

View File

@ -1,8 +1,10 @@
import {
addDependenciesToPackageJson,
formatFiles,
getProjects,
NxJsonConfiguration,
ProjectConfiguration,
readJson,
readNxJson,
Tree,
updateJson,
@ -10,7 +12,11 @@ import {
} from '@nx/devkit';
import { ConvertToFlatConfigGeneratorSchema } from './schema';
import { findEslintFile } from '../utils/eslint-file';
import { join } from 'path';
import { eslintrcVersion, eslintVersion } from '../../utils/versions';
import { ESLint } from 'eslint';
import { convertEslintJsonToFlatConfig } from './converters/json-converter';
import { load } from 'js-yaml';
export async function convertToFlatConfigGenerator(
tree: Tree,
@ -20,9 +26,9 @@ export async function convertToFlatConfigGenerator(
if (!eslintFile) {
throw new Error('Could not find root eslint file');
}
if (!eslintFile.endsWith('.json')) {
if (eslintFile.endsWith('.js')) {
throw new Error(
'Only json eslint config files are supported for conversion'
'Only json and yaml eslint config files are supported for conversion'
);
}
@ -60,15 +66,15 @@ export async function convertToFlatConfigGenerator(
export default convertToFlatConfigGenerator;
function convertRootToFlatConfig(tree: Tree, eslintFile: string) {
if (eslintFile.endsWith('.base.json')) {
convertConfigToFlatConfig(
tree,
'',
'.eslintrc.base.json',
'eslint.base.config.js'
);
if (/\.base\.(js|json|yml|yaml)$/.test(eslintFile)) {
convertConfigToFlatConfig(tree, '', eslintFile, 'eslint.base.config.js');
}
convertConfigToFlatConfig(tree, '', '.eslintrc.json', 'eslint.config.js');
convertConfigToFlatConfig(
tree,
'',
eslintFile.replace('.base.', '.'),
'eslint.config.js'
);
}
function convertProjectToFlatConfig(
@ -78,7 +84,8 @@ function convertProjectToFlatConfig(
nxJson: NxJsonConfiguration,
eslintIgnoreFiles: Set<string>
) {
if (tree.exists(`${projectConfig.root}/.eslintrc.json`)) {
const eslintFile = findEslintFile(tree, projectConfig.root);
if (eslintFile && !eslintFile.endsWith('.js')) {
if (projectConfig.targets) {
const eslintTargets = Object.keys(projectConfig.targets || {}).filter(
(t) => projectConfig.targets[t].executor === '@nx/eslint:lint'
@ -105,7 +112,7 @@ function convertProjectToFlatConfig(
convertConfigToFlatConfig(
tree,
projectConfig.root,
'.eslintrc.json',
eslintFile,
'eslint.config.js',
ignorePath
);
@ -153,5 +160,67 @@ function convertConfigToFlatConfig(
const ignorePaths = ignorePath
? [ignorePath, `${root}/.eslintignore`]
: [`${root}/.eslintignore`];
convertEslintJsonToFlatConfig(tree, root, source, target, ignorePaths);
if (source.endsWith('.json')) {
const config: ESLint.ConfigData = readJson(tree, `${root}/${source}`);
const conversionResult = convertEslintJsonToFlatConfig(
tree,
root,
config,
ignorePaths
);
return processConvertedConfig(tree, root, source, target, conversionResult);
}
if (source.endsWith('.yaml') || source.endsWith('.yml')) {
const originalContent = tree.read(`${root}/${source}`, 'utf-8');
const config = load(originalContent, {
json: true,
filename: source,
}) as ESLint.ConfigData;
const conversionResult = convertEslintJsonToFlatConfig(
tree,
root,
config,
ignorePaths
);
return processConvertedConfig(tree, root, source, target, conversionResult);
}
}
function processConvertedConfig(
tree: Tree,
root: string,
source: string,
target: string,
{
content,
addESLintRC,
addESLintJS,
}: { content: string; addESLintRC: boolean; addESLintJS: boolean }
) {
// remove original config file
tree.delete(join(root, source));
// save new
tree.write(join(root, target), content);
// add missing packages
if (addESLintRC) {
addDependenciesToPackageJson(
tree,
{},
{
'@eslint/eslintrc': eslintrcVersion,
}
);
}
if (addESLintJS) {
addDependenciesToPackageJson(
tree,
{},
{
'@eslint/js': eslintVersion,
}
);
}
}

View File

@ -7,7 +7,6 @@ import {
generateFlatOverride,
stringifyNodeList,
} from '../utils/flat-config/ast-utils';
import { addPluginsToLintConfig } from '../utils/eslint-file';
/**
* This configuration is intended to apply to all TypeScript source files.
@ -97,7 +96,7 @@ export const getGlobalFlatEslintConfiguration = (
rootProject?: boolean
): string => {
const nodeList = createNodeList(new Map(), [], true);
let content = stringifyNodeList(nodeList, '', 'eslint.config.js');
let content = stringifyNodeList(nodeList, '');
content = addImportToFlatConfig(content, 'nxPlugin', '@nx/eslint-plugin');
content = addPluginsToExportsBlock(content, [
{ name: '@nx', varName: 'nxPlugin', imp: '@nx/eslint-plugin' },

View File

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

View File

@ -695,18 +695,27 @@ export function stringifyNodeList(
| ts.ExpressionStatement
| ts.SourceFile
>,
root: string,
fileName: string
root: string
): string {
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
const resultFile = ts.createSourceFile(
joinPathFragments(root, fileName),
joinPathFragments(root, ''),
'',
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.JS
);
return printer.printList(ts.ListFormat.MultiLine, nodes, resultFile);
return (
printer
.printList(ts.ListFormat.MultiLine, nodes, resultFile)
// add new line before compat initialization
.replace(
/const compat = new FlatCompat/,
'\nconst compat = new FlatCompat'
)
// add new line before module.exports = ...
.replace(/module\.exports/, '\nmodule.exports')
);
}
/**

View File

@ -124,6 +124,7 @@ describe('updateEslint', () => {
"const FlatCompat = require("@eslint/eslintrc");
const js = require("@eslint/js");
const baseConfig = require("../eslint.config.js");
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,