feat(linter): support yaml for flat config conversion (#20022)
This commit is contained in:
parent
31df83bb2f
commit
d1a213fa4e
@ -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": {
|
||||
|
||||
@ -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'] },
|
||||
];
|
||||
"
|
||||
`;
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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 } },
|
||||
{
|
||||
|
||||
@ -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,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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' },
|
||||
|
||||
@ -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`), {
|
||||
|
||||
@ -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')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user